Skip to content

Guest MCP Endpoint

Capsem has two MCP entry points: a host-side server (capsem-mcp) that exposes sandbox management tools to AI agents via stdio, and a guest-side relay (capsem-mcp-server) that carries tool calls from inside the VM to the host MITM MCP endpoint over framed vsock.

graph TB
    subgraph "AI Agent (Claude Code, Gemini CLI)"
        AGENT["MCP Client<br/>(stdio)"]
    end

    subgraph "Host"
        HOST_MCP["capsem-mcp<br/>(stdio MCP server, rmcp)"]
        SVC["capsem-service<br/>(HTTP/UDS)"]
        GW["MITM MCP Endpoint<br/>(framed vsock:5002)"]
        AGG["capsem-mcp-aggregator<br/>(isolated subprocess)"]
        BUILTIN["capsem-mcp-builtin<br/>(isolated subprocess)"]
        EXT["External MCP servers<br/>(HTTP/SSE)"]
    end

    subgraph "Guest VM"
        GUEST_MCP["capsem-mcp-server<br/>(stdio-to-vsock relay)"]
        GUEST_AGENT["AI agent process"]
    end

    AGENT -->|stdio JSON-RPC| HOST_MCP
    HOST_MCP -->|HTTP/UDS| SVC

    GUEST_AGENT -->|stdio| GUEST_MCP
    GUEST_MCP -->|"framed MCP<br/>vsock:5002"| GW
    GW -->|"SecurityEvent + telemetry"| AGG
    AGG -->|"stdio MCP"| BUILTIN
    AGG -->|"HTTP/SSE"| EXT

The host MCP server manages VMs. The guest relay provides MCP tools to code running inside the VM while the host endpoint owns parsing, Security Engine dispatch, telemetry, and routing.

The host MCP server runs as a stdio process, typically spawned by an AI agent (Claude Code, Gemini CLI). It uses the rmcp crate for JSON-RPC handling.

sequenceDiagram
    participant Agent as AI Agent
    participant MCP as capsem-mcp
    participant Svc as capsem-service

    Agent->>MCP: tools/call (capsem_exec)
    MCP->>Svc: POST /exec/{id} (HTTP/UDS)
    Svc-->>MCP: {stdout, stderr, exit_code}
    MCP-->>Agent: tool result

26 tools for full sandbox lifecycle management, telemetry, host diagnostics, and guest MCP routing:

ToolDescriptionService endpoint
capsem_createCreate a new VM (name, RAM, CPUs, env, image)POST /provision
capsem_listList all VMs with status and configGET /list
capsem_infoVM details (ID, PID, status, persistent)GET /info/{id}
capsem_execRun shell command inside VM (timeout param)POST /exec/{id}
capsem_runOne-shot: provision + exec + destroyPOST /run
capsem_read_fileRead file from VM workspaceGET /files/{id}/content?path=<relpath>
capsem_write_fileWrite file to VM workspacePOST /files/{id}/content?path=<relpath>
capsem_stopStop VM (persistent: preserve, ephemeral: destroy)POST /stop/{id}
capsem_suspendSuspend VM (save RAM/CPU state)POST /suspend/{id}
capsem_resumeResume stopped persistent VMPOST /resume/{name}
capsem_persistConvert ephemeral VM to persistentPOST /persist/{id}
capsem_deletePermanently destroy VM and all stateDELETE /delete/{id}
capsem_purgeKill all temp VMs (all=true includes persistent)POST /purge
capsem_forkFork VM into reusable imagePOST /fork/{id}
capsem_vm_logsGet security, process, and serial logs (grep + tail params)GET /logs/{id}
capsem_service_logsGet service logs (grep + tail params)Service log file
capsem_host_logsGet an allowlisted host log by symbolic nameGET /host-logs/{name}
capsem_panicsExtract structured panics and backtraces from host logsGET /panics
capsem_triageSummarize recent panics, IPC drops, server errors, and slow opsGET /triage
capsem_timelineRender a time-ordered session timeline by event layer and trace IDGET /timeline/{id}
capsem_inspect_schemaGet CREATE TABLE statements for telemetry DBSchema constant
capsem_inspectRun SQL query against VM’s session.dbPOST /inspect/{id}
capsem_versionMCP server version and service connectivityLocal + service
capsem_mcp_connectorsList Profile V2 mcpServers entriesGET /mcp/connectors
capsem_mcp_addAdd a standard MCP server entry to a profilePOST /mcp/connectors
capsem_mcp_deleteDelete a direct user Profile V2 MCP server entryDELETE /mcp/connectors/{id}

If the service is not running when the MCP server starts, it attempts to launch capsem-service from the same bin/ directory. It polls the UDS socket for up to 5 seconds before giving up.

The guest MCP relay is a minimal stdio-to-framed-vsock bridge. It does not route or execute tools; the host MITM MCP endpoint owns parsing, Security Engine dispatch, telemetry, and routing.

sequenceDiagram
    participant Agent as Guest AI process
    participant Relay as capsem-mcp-server
    participant EP as Host MITM MCP Endpoint

    Relay->>EP: \0CAPSEM_META:claude\n (metadata)
    Agent->>Relay: {"jsonrpc":"2.0","method":"tools/list"}\n (stdin)
    Relay->>EP: MCP frame stream_id=1 process=claude (vsock:5002)
    EP-->>Relay: MCP frame stream_id=1 payload={"jsonrpc":"2.0","result":{...}}
    Relay-->>Agent: {"jsonrpc":"2.0","result":{...}}\n (stdout)
StepDataDirection
1. Connectvsock:5002 (VSOCK_PORT_SNI_PROXY)Guest -> Host
2. Metadata\0CAPSEM_META:<process_name>\nGuest -> Host
3. RelayLength-prefixed MCP frames containing JSON-RPC payloadsBidirectional
4. EOFstdin closes -> half-close vsock writeGuest -> Host

The \0 prefix distinguishes connection metadata from framed content. Process names are sanitized: control characters and spaces replaced with underscores, truncated to 128 characters. The frame envelope also carries the authoritative per-request process name.

Two threads handle the relay:

  • Main thread: stdin -> vsock (reads from AI agent, writes to host)
  • Reader thread: vsock -> stdout (reads from host, writes back to AI agent)

The MITM MCP endpoint receives framed JSON-RPC over vsock:5002, builds a typed MCP SecurityEvent, records mcp_calls, and routes allowed requests through the aggregator:

graph TD
    REQ["tools/call request"] --> PARSE["Extract tool name"]
    PARSE --> CHECK{"Tool category?"}
    CHECK -->|"local__fetch_http,<br/>local__grep_http,<br/>local__http_headers"| BUILTIN["capsem-mcp-builtin<br/>(HTTP tools)"]
    CHECK -->|"snapshots_*, file_*,<br/>dir_*"| FILE["capsem-mcp-builtin<br/>(VirtioFS file tools)"]
    CHECK -->|"server__tool<br/>(contains '__')"| EXT["capsem-mcp-aggregator<br/>(isolated subprocess)"]
    CHECK -->|"Unknown"| ERR["Error: tool not found"]
CategoryCriteriaHandlerExamples
Builtin HTTPlocal__fetch_http, local__grep_http, local__http_headerscapsem-mcp-builtinlocal__fetch_http, local__grep_http, local__http_headers
File toolsName starts with snapshots_, file_, dir_capsem-mcp-builtin (VirtioFS only)file_read, dir_list, snapshots_create
ExternalContains __ separator (server namespace)AggregatorClient routes to isolated subprocessgithub__list_repos, slack__send_message

External tool calls are routed through the MCP Aggregator — an isolated subprocess that manages all external MCP server connections with privilege separation.

Every tools/call request is checked at the framed MITM boundary before the aggregator sees it. Profile-owned enforcement rules use canonical MCP policy roots such as mcp.request.server_name, mcp.request.tool_name, mcp.request.arguments, mcp.response.result_status, and mcp.response.content. Authored rules do not target internal event.* fields.

decisionBoundary behavior
allowTool call proceeds.
askFails closed until an approval UI exists. The request is not dispatched.
blockReturns an enforcement JSON-RPC error. The request is not dispatched.
rewriteApplies only validated declarative mutations before returning to the guest.

The Security Engine writes the resolved event, final decision, rule id, reason, and allowed mutations before telemetry/audit/logging projections run. warn is historical terminology and is not an enforcement decision.

Every tools/call request is logged to the session database mcp_calls table:

ColumnSource
server_namebuiltin, file, or external server name
methodJSON-RPC method (tools/call, tools/list, etc.)
tool_nameTool name from request params
decisionTerminal transport result: allowed, denied, or error
duration_msEnd-to-end call duration
request_previewTruncated request body
response_previewTruncated response body
process_nameGuest process from metadata line
policy_actionFinal enforcement decision: allow, ask, block, or rewrite
policy_ruleMatching rule key, for example security.rules.mcp.block_prod_token
policy_reasonOptional human-readable audit reason
trace_idCross-table correlation ID

See Session Telemetry for the full mcp_calls schema.

FieldTypePurpose
aggregatorAggregatorClientClient handle for the isolated MCP aggregator subprocess
dbArc<DbWriter>Async telemetry writer
The AggregatorClient is cloneable (Arc-wrapped mpsc channel) and shared
across endpoint sessions for a given VM. New frames are lifted into the
Security Engine so reloads affect already-open guest MCP connections through the
same resolved-event path used by HTTP, model, file, and process activity.

MCP server definitions live in Profile V2 payloads under mcpServers using the standard MCP server shape. The service resolves built-in, corp, and user profile layers, then passes the VM-effective connector list to the aggregator.

[mcpServers.github]
command = "github-mcp-server"
args = ["stdio"]
[mcpServers.github.capsem]
enabled = true
editable = true
allowed_tools = ["search_repositories", "get_file_contents"]

External MCP servers may be auto-detected from AI CLI settings (~/.claude/settings.json, ~/.gemini/settings.json) and normalized into profile entries when the relevant profile section is editable. Corp profiles can lock the section so users may use approved tools without changing provider or rule configuration. The resolved connector list is passed to the MCP Aggregator subprocess at spawn time and on reload.

FilePurpose
capsem-mcp/src/main.rsHost MCP server: 26 tools, rmcp handler, service bridge
capsem-agent/src/mcp_server.rsGuest relay: stdin/stdout <-> framed MCP over vsock:5002
capsem-core/src/net/mitm_proxy/mcp_frame.rsFramed transport parser, stream lifecycle, and disconnect metrics
capsem-core/src/net/mitm_proxy/mcp_endpoint.rsHost endpoint: JSON-RPC dispatch, policy, telemetry
capsem-core/src/mcp/aggregator.rsAggregator protocol types and AggregatorClient
capsem-core/src/mcp/builtin_tools.rsBuiltin HTTP tools (fetch_http, grep_http, http_headers)
capsem-core/src/mcp/file_tools.rsFile and snapshot tools (VirtioFS workspace)
capsem-core/src/mcp/server_manager.rsExternal MCP server lifecycle and tool catalog
crates/capsem-security-engine/MCP SecurityEvent projection and resolved-event evidence
capsem-mcp-aggregator/src/main.rsIsolated subprocess: NDJSON loop, server connections
capsem-process/src/main.rsspawn_mcp_aggregator(): launch and driver tasks
config/profiles/Built-in Profile V2 MCP server definitions

See MCP Aggregator for the full subprocess architecture.