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 toSEED_ORG_ID)DATABASE_URL— Postgres connection stringOPENAI_API_KEY— required only forruntime_run_skill
Optional:
WORKSPACE_PATH— path to the workspace directory (defaultworkspace/metacto)WORKSPACE_AUTO_COMMIT=false— disable auto-git globallyWORKSPACE_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_*
| Tool | Purpose |
|---|---|
workspace_list | Every agent/skill/object_type (slug, name, description only) |
workspace_get | One resource with full prompt text |
workspace_write_skill | Create or update a skill. Writes skills/<slug>/{skill.yaml,prompt.md} → commits → applies |
workspace_write_agent | Create or update an agent. Writes agents/<slug>.{yaml,system-prompt.md} → commits → applies |
workspace_write_object_type | Create or update an object type (schema + classification prompt) |
workspace_delete | Remove files + apply (row deletion) |
workspace_apply | Reconcile files → DB. Use after editing files outside MCP |
workspace_diff | Dry-run apply. Shows created/updated/unchanged counts |
workspace_version_history | Recent workspace_version audit rows — "who changed what, when" |
runtime_*
| Tool | Purpose |
|---|---|
runtime_run_skill | Execute a skill with input. Returns run_id, output, Langfuse trace id |
runtime_list_runs | Recent skill runs, filterable by skill slug or status |
runtime_get_run | One full skill run (not truncated) |
runtime_approve_draft | Approve a pending run |
runtime_reject_draft | Reject a pending run (with optional reason) |
Data access
| Tool | Purpose |
|---|---|
objects_list | List business object instances (filter by type slug) |
objects_get | One object + type + document links |
object_types_list | Discover valid type slugs |
search_query | Run 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.
| Tool | Purpose |
|---|---|
web_search | Live web search — ranked titles, URLs, snippets |
fetch_url | Fetch one page; returns readable text/markdown |
crawl_site | Same-origin BFS crawl; pages digest (capped) |
generate_image | Generate an image from a prompt; returns a saved artifact URL |
run_code | Evaluate a math expression (builtin) or run code (sandbox provider) |
create_artifact | Create a CSV, SVG chart, or doc; returns a URL |
Missions
Open-ended team work (see Missions).
| Tool | Purpose |
|---|---|
mission_list | List authored mission templates (starting teams) |
mission_start | Start a mission from a brief — template (missionSlug) or ad-hoc (team); plans + runs to completion or first approval gate |
mission_list_runs | List mission runs (optionally by status) |
mission_get_run | One run with its plan (task graph), team, artifacts, status |
mission_approve | Approve a paused run (awaiting_review) and continue |
mission_cancel | Cancel a run |
mission_promote | Draft 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:
workspace_write_skillwith the new manifest + promptobjects_listwithtype_slug: 'discovery_call'to find the latestruntime_run_skillwith the transcript as input- Show you the output and the
run_idso 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:
- Validate the manifest against the Zod schema. Failure aborts with a structured error — nothing touches disk.
- Write YAML + markdown to
workspace/<org>/<kind>/<slug>/… - Commit to git (if
autoCommitenabled and there are changes). Commit message:context: <default summary or override>. - Apply to the DB (if
autoApplyenabled). Writes aworkspace_versionrow with the new git SHA, diff counts, andapplied_by: 'mcp'. - 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.