Skip to content

MCP servers

The Model Context Protocol (MCP) is the wire format agents use to call host capabilities — list tools, call them, get results. Vonzio runs an MCP server for each capability surface and injects them into the agent container at task-start time.

If you’re new to MCP entirely: Anthropic’s MCP spec. The short version: it’s JSON-RPC 2.0 over HTTP (or stdio), with tools/list and tools/call methods.

ServerEndpointWhat it doesInjected when
memory/mcp/memoryRead/write persistent memory rows scoped to (user, profile).memory_enabled !== false on the profile.
notify/mcp/notifySend a notification through any of the user’s notification channels.Always (when INTERNAL_SERVER_URL is set).
gmail/mcp/gmailSearch / read / send Gmail on behalf of the user.User has a Gmail integration granted to this profile.
teller/mcp/tellerRead bank enrollments, balances, transactions, account details.User has a Teller integration granted to this profile.
vonzio/mcp/platformInternal platform operations: spawn child tasks, run playbooks, query memory across profiles.Always.

Custom HTTP MCPs from third parties: add to a profile’s mcp_servers array and they’re injected the same way.

Each MCP server uses bearer token authentication. The orchestrator mints a short-lived token per task and stores it in an in-memory Map<token, session_context>. When the agent calls the MCP endpoint, the server resolves the token to a {userId, sessionId, profileId, …} context and applies it server-side.

Crucially: the token never grants more than the session’s own context. An agent can’t forge a profileId to access another profile’s data. Server-side resolution is the only source of truth for userId / profileId.

Tokens are cleaned up in a finally block when the task ends — they’re not durable.

Integration-backed MCP servers (gmail, teller) are gated by scope. If the integration is scope='agents' and the running profile isn’t in profile_ids, the MCP server isn’t injected at all — the agent doesn’t see gmail_* / teller_* tools in its tools/list response.

When dispatchSession runs, after profile resolution it builds a nonSdkServers array:

nonSdkServers.push({
name: "memory",
type: "http",
url: `${INTERNAL_SERVER_URL}/mcp/memory`,
headers: { Authorization: `Bearer ${memToken}` },
});

The agent container receives this array as JSON via the agent-runner config. The Claude Agent SDK initializes MCP clients for each entry, calls tools/list to discover, then exposes them to the model as mcp__<server>__<tool> tools.

To inject a third-party MCP server into agents on a specific profile:

  1. Set up the server (it must speak MCP over HTTP, including auth if needed).

  2. PATCH the profile to add to mcp_servers:

    {
    "mcp_servers": [
    { "name": "memory", "type": "sdk" },
    { "name": "notify", "type": "sdk" },
    {
    "name": "mycorp-crm",
    "type": "http",
    "url": "https://crm.mycorp.com/mcp",
    "headers": { "Authorization": "Bearer <static-or-env>" }
    }
    ]
    }
  3. The agent will see mcp__mycorp_crm__<tool_name> on its next session.