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 -->|"policy + 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, policy, telemetry, and dispatch.

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 /vms/{id}/exec (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:

Tool Description Service endpoint
capsem_create Create a new VM (name, RAM, CPUs, env, image) POST /vms/create
capsem_list List all VMs with status and config GET /vms/list
capsem_info VM details (ID, PID, profile, status) GET /vms/{id}/info
capsem_exec Run shell command inside VM (timeout param) POST /vms/{id}/exec
capsem_run One-shot: provision + exec + destroy POST /run
capsem_read_file Read file from guest filesystem POST /vms/{id}/files/read
capsem_write_file Write file to guest filesystem POST /vms/{id}/files/write
capsem_stop Stop VM POST /vms/{id}/stop
capsem_suspend Suspend VM (save RAM/CPU state) POST /vms/{id}/pause
capsem_resume Resume stopped or paused VM POST /vms/{id}/resume
capsem_save Save current VM state POST /vms/{id}/save
capsem_delete Permanently destroy VM and all state DELETE /vms/{id}/delete
capsem_purge Clean up disposable sessions; all=true includes retained sessions POST /purge
capsem_fork Fork VM into reusable image POST /vms/{id}/fork
capsem_vm_logs Get serial/process logs (grep + tail params) GET /vms/{id}/logs
capsem_service_logs Get service logs (grep + tail params) Service log file
capsem_host_logs Get an allowlisted host log by symbolic name GET /host-logs/{name}
capsem_panics Extract structured panics and backtraces from host logs GET /panics
capsem_triage Summarize recent panics, IPC drops, server errors, and slow ops GET /triage
capsem_timeline Render a time-ordered session timeline by event layer and trace ID GET /vms/{id}/timeline
capsem_version MCP server version and service connectivity Local + service
capsem_mcp_servers List configured guest MCP servers Service MCP IPC
capsem_mcp_tools List discovered guest MCP tools Service MCP IPC
capsem_mcp_call Call a namespaced guest MCP tool Service MCP IPC

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, policy, telemetry, and dispatch.

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)
Step Data Direction
1. Connect vsock:5002 (VSOCK_PORT_SNI_PROXY) Guest -> Host
2. Metadata \0CAPSEM_META:<process_name>\n Guest -> Host
3. Relay Length-prefixed MCP frames containing JSON-RPC payloads Bidirectional
4. EOF stdin closes -> half-close vsock write Guest -> 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, normalizes the frame into the shared SecurityEvent rule rail, records protocol evidence, 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"]
Category Criteria Handler Examples
Builtin HTTP local__fetch_http, local__grep_http, local__http_headers capsem-mcp-builtin local__fetch_http, local__grep_http, local__http_headers
File tools Name starts with snapshots_, file_, dir_ capsem-mcp-builtin (VirtioFS only) file_read, dir_list, snapshots_create
External Contains __ separator (server namespace) AggregatorClient routes to isolated subprocess github__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 normalized into a first-party SecurityEvent at the framed MITM boundary before the aggregator sees it. Rules use the shared security rule rail described in Policy, so MCP matches use fields such as mcp.method, mcp.server.name, mcp.tool_call.name, and mcp.tool_list.

rule action Boundary behavior
allow Tool call proceeds.
ask Request waits for an approval or denial row before dispatch.
block Returns a policy JSON-RPC error. The request is not dispatched.
preprocess / postprocess Runs the configured plugin against the same SecurityEvent object.

The MCP gateway does not own a separate decision provider. Its job is to parse MCP, attach typed MCP fields to SecurityEvent, call the shared security engine, and log transport evidence plus any security_rule_events matches.

The product/security tool ledger is tool_calls. Every model-native, built-in/local, or MCP-origin tool invocation must appear there with an origin such as native, builtin, local, mcp, or mcp_proxy. Visible MCP protocol facts such as initialize/list/resource frames are represented as typed security events and matching security_rule_events, not as a second tool-call ledger. An MCP tools/call without a matching tool_calls row is a serious telemetry bug.

See Session Telemetry for the full tool_calls, tool_responses, and security-rule ledger joins.

Field Type Purpose
aggregator AggregatorClient Client handle for the isolated MCP aggregator subprocess
db Arc<DbWriter> Async telemetry writer
security_rules RwLock<Arc<SecurityRuleSet>> Hot-reloadable security-event rules
plugin_policy RwLock<Arc<SecurityPluginPolicy>> Hot-reloadable plugin modes for security-event preprocessing/postprocessing

The AggregatorClient is cloneable (Arc-wrapped mpsc channel) and shared across endpoint sessions for a given VM. The rule set uses double-Arc style atomic swap through the endpoint state. New frames read the current rules, so reloads affect already-open guest MCP connections.

MCP server definitions are profile-owned. The profile points at mcp.json, and semantic routes mutate MCP server/tool posture through backend-owned profile rules instead of exposing raw rule text to the UI.

{
"servers": [
{
"id": "capsem",
"name": "Capsem",
"description": "Built-in Capsem MCP server for file and snapshot tools",
"transport": "stdio",
"command": "/run/capsem-mcp-server",
"builtin": true,
"enabled": true
}
]
}

Profile MCP config and corp constraints are validated by the service and passed to the MCP Aggregator subprocess at spawn time. Credentials are broker-owned references, not raw tokens in MCP config.

File Purpose
capsem-mcp/src/main.rs Host MCP server: 26 tools, rmcp handler, service bridge
capsem-agent/src/mcp_server.rs Guest relay: stdin/stdout <-> framed MCP over vsock:5002
capsem-core/src/net/mitm_proxy/mcp_frame.rs Framed transport parser, stream lifecycle, and disconnect metrics
capsem-core/src/net/mitm_proxy/mcp_endpoint.rs Host endpoint: JSON-RPC dispatch, policy, telemetry
capsem-core/src/mcp/aggregator.rs Aggregator protocol types and AggregatorClient
capsem-core/src/mcp/builtin_tools.rs Builtin HTTP tools (fetch_http, grep_http, http_headers)
capsem-core/src/mcp/file_tools.rs File and snapshot tools (VirtioFS workspace)
capsem-core/src/mcp/server_manager.rs External MCP server lifecycle and tool catalog
capsem-core/src/net/policy_config/security_rule_profile.rs Security-event rule schema, validation, Sigma import, and compiled rule set
capsem-core/src/security_engine/ SecurityEvent construction, rule evaluation, plugin actions, and rule-ledger emission
capsem-mcp-aggregator/src/main.rs Isolated subprocess: NDJSON loop, server connections
capsem-process/src/main.rs spawn_mcp_aggregator(): launch and driver tasks
config/profiles/<id>/mcp.json Profile MCP server definitions

See MCP Aggregator for the full subprocess architecture.