ADR 0008 — MCP dual role: client and server
Status: Accepted Date: 2026-03
Context
Model Context Protocol is becoming the de facto integration surface for LLM-driven tools. Two questions arose during the Phase 12 design:
- Should the agent be an MCP client (consume external MCP servers as tools)?
- Should the agent be an MCP server (expose its own tools to external MCP clients like Claude Desktop, Cursor, Zed)?
These are independent decisions. Picking one does not force the other.
Decision
Do both. Same process, same ToolRegistry, different transports.
- Client —
McpRuntimeManagerspawns stdio or HTTP MCP servers per session (with a shared "sentinel session" for servers that don't need per-session isolation). Their tools register into the per-sessionToolRegistrywith names like{server_name}_{tool_name}and are callable by the agent like any built-in - Server —
agent mcp-serversubcommand reads JSON-RPC from stdin and writes responses to stdout. Anmcp_server.yamlallowlist controls which tools are exposed. Configurableauth_token_envguards theinitializecall when the server is exposed through a tunnel
Both sides speak MCP 2024-11-05 (streamable HTTP) with SSE fallback for legacy servers.
Consequences
Positive
- Being a client: any MCP-speaking tool ecosystem is reachable without writing a custom extension
- Being a server: the agent's tools + memory become available inside Claude Desktop / Cursor / Zed — cross-session memory, remote actions, etc.
- Interop with the broader MCP catalog is a configuration change, not a code change
Negative
- Two independent code paths to keep current as the MCP spec evolves
expose_proxiesconfiguration gotcha: enabling it on the server side makes every upstream MCP server transitively visible to the consuming client. Default isfalseand the docs call this out explicitly- MCP spec churn (2024-11-05 vs future versions) needs staying power