docs/reference/mcp.md

MCP Server

Vocion ships a Model Context Protocol server so you can author context, run skills, and inspect the review queue from any MCP client — Claude Code, Claude Desktop, Cursor, Zed, Continue. This is the first adapter of the Phase 2 universal interface layer.

Auto-apply is on by default. Every write_* tool writes files to workspace/<org>/, commits to git with a generated message, applies to the DB, and records a workspace_version audit row — all in one atomic call. Override per-call with autoApply: false or autoCommit: false.

Install for Claude Code

claude mcp add vocion -- npm --prefix /absolute/path/to/context-stack run mcp:serve

Or add to ~/.claude/mcp.json (or project-level .mcp.json):

{
  "mcpServers": {
    "vocion": {
      "command": "npm",
      "args": ["--prefix", "/absolute/path/to/context-stack", "run", "mcp:serve"],
      "env": {
        "VOCION_ORG_ID": "org_3B7f6cPKTKnJOExO55asDaUVAay",
        "WORKSPACE_PATH": "/absolute/path/to/context-stack/workspace/metacto"
      }
    }
  }
}

Required env:

  • VOCION_ORG_ID — org id the server acts on (or fall back to SEED_ORG_ID)
  • DATABASE_URL — Postgres connection string
  • OPENAI_API_KEY — required only for runtime_run_skill

Optional:

  • WORKSPACE_PATH — path to the workspace directory (default workspace/metacto)
  • WORKSPACE_AUTO_COMMIT=false — disable auto-git globally
  • WORKSPACE_AUTO_APPLY=false — disable auto-apply globally

Install for Claude Desktop / other clients

Point the client at tsx src/interfaces/mcp/bin.ts with the same env. Any stdio-capable MCP client works — the transport is standard.

Connect over HTTP (multi-tenant) ✓ live

The stdio server is single-tenant — one org per process, fixed by VOCION_ORG_ID. That's right for a developer's local IDE, wrong for a hosted runtime that many tenants or a fleet of agents connect to. The HTTP transport solves that: a single endpoint, multi-tenant, where the org is derived from a tenant Bearer token (vcn_live_… — the same credential the write API uses).

POST https://your-install/api/mcp
Authorization: Bearer vcn_live_...

Point any remote-capable MCP client (Claude, Cursor, Zed, …) at that URL with the Bearer header. The server instance is scoped to the token's org for the life of the request, and every tool call runs under the token's authz principal — the same permission model and review queue as a human. No per-org process, no shared secret: one endpoint, every tenant, isolated by token.

Transport is the standard Streamable HTTP (stateless, JSON responses) over Web Request/Response. Over HTTP the workspace-authoring tools (context_write/delete/apply) are disabled by default — HTTP is the runtime / data / search plane; authoring stays on stdio or CI where a per-org git workspace lives.

Tool reference

context_*

ToolPurpose
workspace_listEvery agent/skill/object_type (slug, name, description only)
workspace_getOne resource with full prompt text
workspace_write_skillCreate or update a skill. Writes skills/<slug>/{skill.yaml,prompt.md} → commits → applies
workspace_write_agentCreate or update an agent. Writes agents/<slug>.{yaml,system-prompt.md} → commits → applies
workspace_write_object_typeCreate or update an object type (schema + classification prompt)
workspace_deleteRemove files + apply (row deletion)
workspace_applyReconcile files → DB. Use after editing files outside MCP
workspace_diffDry-run apply. Shows created/updated/unchanged counts
workspace_version_historyRecent workspace_version audit rows — "who changed what, when"

runtime_*

ToolPurpose
runtime_run_skillExecute a skill with input. Returns run_id, output, Langfuse trace id
runtime_list_runsRecent skill runs, filterable by skill slug or status
runtime_get_runOne full skill run (not truncated)
runtime_approve_draftApprove a pending run
runtime_reject_draftReject a pending run (with optional reason)

Data access

ToolPurpose
objects_listList business object instances (filter by type slug)
objects_getOne object + type + document links
object_types_listDiscover valid type slugs
search_queryRun a retrieval query against indexed documents (pgvector + Postgres FTS hybrid). Returns ranked docs with snippets + links

Built-in tools

The general toolbelt agents get out of the box (see Tools), callable over MCP. Providers are pluggable via env.

ToolPurpose
web_searchLive web search — ranked titles, URLs, snippets
fetch_urlFetch one page; returns readable text/markdown
crawl_siteSame-origin BFS crawl; pages digest (capped)
generate_imageGenerate an image from a prompt; returns a saved artifact URL
run_codeEvaluate a math expression (builtin) or run code (sandbox provider)
create_artifactCreate a CSV, SVG chart, or doc; returns a URL

Missions

Open-ended team work (see Missions).

ToolPurpose
mission_listList authored mission templates (starting teams)
mission_startStart a mission from a brief — template (missionSlug) or ad-hoc (team); plans + runs to completion or first approval gate
mission_list_runsList mission runs (optionally by status)
mission_get_runOne run with its plan (task graph), team, artifacts, status
mission_approveApprove a paused run (awaiting_review) and continue
mission_cancelCancel a run
mission_promoteDraft a reusable Workflow from a completed run

Example flows

Edit the Sales Assistant's system prompt

> Use workspace_get to show me agent sales-assistant, then update it to add a rule
> about always including pricing in discovery summaries. Commit it.

Claude Code will call workspace_get with {kind: 'agent', slug: 'sales-assistant'}, edit the systemPrompt field, then call workspace_write_agent — one commit, one apply, done.

Test a new skill on real data

> Create a skill called objection_summary that reads a discovery transcript
> and lists the top 3 objections the prospect raised. Then run it against
> the last discovery call in my objects.

Claude Code will:

  1. workspace_write_skill with the new manifest + prompt
  2. objects_list with type_slug: 'discovery_call' to find the latest
  3. runtime_run_skill with the transcript as input
  4. Show you the output and the run_id so you can approve/reject

Review pending drafts

> Show me pending skill runs, then approve the email drafts that look good.

runtime_list_runs → read → runtime_approve_draft per selection.

Auto-apply semantics

Every write goes through this flow atomically:

  1. Validate the manifest against the Zod schema. Failure aborts with a structured error — nothing touches disk.
  2. Write YAML + markdown to workspace/<org>/<kind>/<slug>/…
  3. Commit to git (if autoCommit enabled and there are changes). Commit message: context: <default summary or override>.
  4. Apply to the DB (if autoApply enabled). Writes a workspace_version row with the new git SHA, diff counts, and applied_by: 'mcp'.
  5. Return {written, commit, apply} in one response — the MCP caller sees the new SHA and diff counts without an extra round-trip.

Turn off either leg with autoCommit: false / autoApply: false per call, or globally with env vars. If git commit fails (hooks, no repo, etc.), the apply is skipped and the caller sees an error — we don't want files on disk that don't match the DB.

Audit trail

Every skill_run stamps workspace_sha with the SHA active at execution time. Every workspace_version records the SHA, per-resource counts, errors, and who applied. To answer "why did the Sales Assistant write that email on April 14?":

SELECT sr.output, sr.workspace_sha, sr.created_at, cv.applied_by
FROM skill_run sr
JOIN workspace_version cv ON cv.sha = sr.workspace_sha AND cv.org_id = sr.org_id
WHERE sr.id = <run_id>;

-- then: git show <sha> on the workspace repo for the exact prompts

Limitations (today)

  • stdio + HTTP transports. Both ship: stdio (single-tenant, local IDE) and Streamable HTTP (multi-tenant, Bearer-scoped — see above). The remaining piece is the OAuth authorization-code flow for clients that prefer interactive sign-in over a pre-issued token (claude.ai, Cursor Cloud); Bearer tokens work today.
  • No workflow resource yet. Workflows (triggers + steps + actions) come in Phase 3 with the conversational bootstrap.
  • Search is first-party — pgvector + Postgres FTS via RetrievalService.search. No third-party retrieval engine.

Extending

Add a new tool in src/interfaces/mcp/tools/<group>.ts:

export function myTool(config: McpConfig): ToolModule {
  return {
    name: 'my_tool',
    title: 'Short title',
    description: 'What it does, shown to the LLM',
    inputSchema: { arg: z.string() },
    handler: async (input) => { /* ... */ },
  };
}

Register it in src/interfaces/mcp/server.ts. Types flow end-to-end.