[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69942BYQFDB9PR4KNXCK62V":3,"skill-same:01M69942BYQFDB9PR4KNXCK62V":59},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":25,"linkOnly":26,"markdownContent":27,"markdownBody":28,"frontMatter":29,"refName":30,"commitSha":31,"skillDirectory":32,"forksCount":33,"repositoryHtmlUrl":34,"repositoryHomepageUrl":6,"ownerHtmlUrl":35,"ownerBlogUrl":36,"ownerDescription":37,"updatedAt":38,"install":39,"resources":52},"01M69942BYQFDB9PR4KNXCK62V","dev-mcp",null,"google\u002Fcapsem","google","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1342004?v=4","01M698VYEFDM0VJBZDYYJ2T83S","Google","01M69939YZJTP726NQK4NZNB48",186,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fdev-mcp\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[23,24],"MCP","dev mcp","2026-11-10 10:12:30",false,"---\nname: dev-mcp\ndescription: MCP for Capsem: the SDK-backed npm host server, guest relay, and tool routing. Use when working on MCP servers, tool policy, telemetry, or in-VM debugging.\n---\n\n# MCP in Capsem\n\nCapsem has two MCP entry points:\n\n1. **`@capsem\u002Fmcp`** (host): a standalone TypeScript stdio server. It presents typed tools and uses `@capsem\u002Fsdk` for authenticated gateway HTTP. It does not open the service UDS, discover local services, or read VM runtime state.\n2. **Guest MCP relay + confined proxy endpoint**: bridges AI agents inside a VM to built-in and external MCP servers through bounded framed records on vsock port 5002.\n\nThe native installer does not install Node.js or download the npm host package.\nThe guest relay, aggregator, and built-in Rust components remain native product\ncomponents.\n\n## Using the npm MCP tools for fast debugging\n\nInstall and register the package with an explicit gateway URL, bearer token, and\ntransport timeout:\n\n```sh\nnpm install --global @capsem\u002Fmcp\nCAPSEM_GATEWAY_TOKEN=\"$(cat ~\u002F.capsem\u002Frun\u002Fgateway.token)\" \\\n  capsem-mcp --gateway-url http:\u002F\u002F127.0.0.1:19222 --timeout-ms 30000\n```\n\nThe token comes from `CAPSEM_GATEWAY_TOKEN` or `--token-file`; `--token` is\nrefused because argv is world-readable. The bearer token belongs only to\nthe host MCP process; never copy it into a VM, container, guest tool argument,\nor workload. stdout is protocol-only and sanitized diagnostics use stderr.\n\nThe current registry is defined in `mcp\u002Ftypescript\u002Fsrc\u002Fhost-tools.ts`,\n`network-tools.ts`, and `mcp-tools.ts`. Parameters follow the TypeScript\nSDK names, including `vm_id`, `vcpu`, `memory`, `env`, and\n`timeout_secs`. Canonical tools include `capsem_pause` and\n`capsem_status`; the retired `capsem_suspend`, `capsem_version`, and\nduplicate `capsem_service_logs` names are not exposed.\n\nThe host tools cover:\n\n- VM lifecycle, one-shot execution, persistence, files, statistics,\n  logs, timelines, panic extraction, and triage.\n- Private network lifecycle, membership, and cursor-based audit logs.\n- Typed MCP server discovery, refresh, permission inspection, and invocation\n  through the running VM's existing relay and security engine.\n\n### Debug workflow\n\n```text\n1. capsem_create\n2. capsem_exec { vm_id: \"...\", command: \"capsem-doctor -k net\" }\n3. capsem_read_file { vm_id: \"...\", path: \"\u002Ftmp\u002Fcapsem-init.log\" }\n4. capsem_timeline { vm_id: \"...\", layers: \"net,tool,fs\", limit: 50 }\n5. Fix and rebuild the owning native component when required.\n6. capsem_delete { vm_id: \"...\" }\n```\n\nThe `--timeout-ms` option bounds the SDK HTTP request.\n`timeout_secs` on execution tools bounds the guest command. Cancellation\ncloses the local request; it does not delete the VM or undo an accepted\nmutation. The SDK does not retry mutations.\n\n### When to use MCP tools vs just recipes\n\n| Scenario | Use |\n| --- | --- |\n| Targeted guest behavior | `capsem_exec` |\n| Read a guest file | `capsem_read_file` |\n| Inspect typed audit evidence | `capsem_timeline`, statistics, and network logs |\n| Run capsem-doctor | `capsem_exec` |\n| Full regression suite | `just test` |\n| Focused native validation | `just focus-test functional` |\n\n## npm host MCP server\n\n```text\nHost AI agent -> @capsem\u002Fmcp (stdio) -> @capsem\u002Fsdk\n  -> authenticated capsem-gateway HTTP -> capsem-service\n```\n\nThe npm process owns tool presentation and typed input\u002Foutput only. The gateway\nauthenticates control requests. The service, VM owner, security engine, and\nlogger retain lifecycle, trusted identity, policy, credentials, and audit\nstorage. The npm process receives no virtualization entitlement, registry\ncredential, CA private key, direct database access, or service socket access.\n\n### Key source files\n\n| File | Purpose |\n| --- | --- |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fcli.ts` | Protocol-only stdio executable |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fserver.ts` | SDK client and tool registration |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fhost-tools.ts` | VM, file, and diagnostic tools |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fnetwork-tools.ts` | Private network tools |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fmcp-tools.ts` | MCP server discovery and calls |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fresults.ts` | Structured results and sanitized errors |\n| `sdk\u002Ftypescript\u002Fsrc\u002F` | Typed gateway client and validators |\n\n## MCP subprocess architecture\n\nThe guest MCP path is not a single process. The per-session `capsem-proxy`\nowns the guest-facing framed endpoint, MCP policy, and ledger admission.\n`capsem-process` owns a scoped adapter to the aggregator and VM exposure tools;\nit cannot reopen the session ledger and the proxy cannot reach the aggregator\nexcept through that granted connected channel. The service supervises two\nconfined native workers behind the adapter:\n\n| Crate | Role | Privileges |\n|-------|------|-----------|\n| `capsem-mcp-aggregator` | Parses MCP transports and routes tools over a connected MessagePack RPC channel. | Inherited HTTP\u002F2 capability for configured endpoints and a narrow builtin-peer spawn channel; no ambient files, direct network, credentials, execution, signals, VM or ledger authority. |\n| `capsem-mcp-builtin` | Implements HTTP tools (`fetch_http`, `grep_http`, `http_headers`) and `echo` over a connected MCP channel. | Inherited HTTP capability only. The service performs current URL\u002Faddress policy and credential handling; HTTP ledger facts return in `_meta` `dev.capsem\u002Fledger`. No policy-file or database access. |\n\nRationale: isolating external-server connections in a low-privilege subprocess means a compromised third-party MCP server cannot reach the host filesystem or the session DB. The built-in tool server runs in its own process for the same reason.\n\nThe shared `capsem-mcp-transport` crate owns connected HTTP capabilities and\nrole bootstrap; it stays independent of VM core, policy, credentials and RMCP.\nThe service supplies role\u002Fincarnation bootstrap records and connected descriptors.\nEach native entrypoint clears inherited environment\u002Fdescriptors, prepares a\ncurrent-thread runtime, installs Seatbelt on macOS or Landlock plus seccomp on\nLinux, and attests denial before acknowledging readiness. Parent watching starts\nafter confinement. Linux aggregator\u002Fbuiltin profiles allow threads but deny forked\nprocesses; approved external executable profiles must retain their own sandbox.\n\nThe owner receives one full-duplex RPC descriptor, never native child handles.\nMessagePack framing is `[4-byte big-endian length][payload]`; builtin MCP uses\nJSON-RPC lines over its own connected descriptor. Use async stream halves in both\ndirections: stdin adoption sets nonblocking flags, so a blocking stdout adapter\nover a duplicate descriptor fails under backpressure.\n\nThe service owns destinations, current policy, credentials and process reaping.\nRefresh preserves inherited capabilities. HTTP streams cancel on policy changes\nor retirement; generation completion awaits broker cleanup and positively reaped\nnative children. Separate PIDs, parent watchers or `kill_on_drop` alone are not\nthat completion proof.\n\nSettings configure external HTTP servers; there is no public arbitrary host\nstdio command\u002Ffile-grant setting. The native spawn broker accepts only predefined\nbuiltin peer indices. New external stdio support needs explicit executable\u002Ffile\ngrants under `McpExternal`, never the trusted companion `with no-sandbox` rule.\n\n`\u002Frun\u002Fcapsem-mcp-server` relays guest JSON-RPC as bounded frames over vsock 5002.\nThe proxy evaluates policy before invoking the scoped owner adapter. Tool facts\nreach `tool_calls` through the proxy's typed ledger capability; the native MCP\nworkers never open the ledger.\n\nBinaries land in `~\u002F.capsem\u002Fbin\u002F` at install time: `capsem-mcp-aggregator`, `capsem-mcp-builtin`.\n\n## Guest MCP Endpoint\n\nThe guest MCP relay bridges AI agents in the guest VM to the confined proxy\nendpoint. It runs over vsock port 5002 using bounded length-prefixed MCP\nframes that carry JSON-RPC payloads and per-frame process attribution.\n\nFramed guest MCP over `vsock:5002` must be tested as the default transport, not as an opt-in benchmark mode. The minimum hardening matrix for that path is:\n- parser\u002Finterpreter: bounded frames, invalid JSON, malformed flags, stream-id reuse, notification\u002Frequest-id mismatch\n- dispatch: `initialize`, `tools\u002Flist`, builtin `tools\u002Fcall`, configured external HTTP `tools\u002Fcall`, `resources\u002Flist`, `prompts\u002Flist`, and method error mapping\n- policy: live policy mutation, per-tool block, resource URI rule, argument-name rule, argument-value rule, return-value rule, deny-over-allow precedence, and proof that blocked requests\u002Fresponses do not leak original data\n- telemetry: `session.db` rows for success, denial, timeout, process attribution, request\u002Fresponse previews, policy fields, and terminal errors\n- boundary: aggregator and VM owner remain DB-free; `capsem-proxy` writes MCP\n  audit facts through its role-bound ledger capability\n- VM E2E: boot a real VM, run `\u002Frun\u002Fcapsem-mcp-server` with no transport\n  override, then inspect the typed timeline\u002Fledger routes\n\n### Architecture\n\n```\nGuest (Claude\u002FGemini) -> capsem-mcp-server (stdin\u002Fstdout relay)\n  -> framed vsock:5002 -> capsem-process descriptor handoff -> capsem-proxy\n  -> SecurityEvent rule check -> scoped VM-owner MCP adapter\n  -> builtin tools | external MCP servers (via aggregator)\n  -> typed ledger capability -> tool_calls plus security_rule_events evidence\n```\n\n### Workloads: streamable HTTP at `mcp.capsem.internal`\n\nAn OCI workload cannot open vsock (seccomp), so it reaches the same endpoint\nover HTTP: `POST http:\u002F\u002Fmcp.capsem.internal\u002Fmcp`, one JSON-RPC message per\nrequest (200 with the answer, 202 for notifications, 405 for GET\u002FDELETE).\nThe host DNS answers that one name in every VM with `192.0.2.1` before any\nprivate-network member is asked, and never upstream. The VM's port-80 rule\ncarries the connection to the MITM proxy, where `mitm_proxy\u002Fmcp_http.rs`\nclaims it before HTTP policy, body sniffing or any dial and hands it to\n`mcp_frame::dispatch_http_message` -- the framed path's parser, in-flight\nbound, MCP security rail and `tool_calls` ledger, with `transport = 'http'`\nand the connection's process name. Attribution is the proxy instance: one\nper VM, with that VM's endpoint and session ledger. Every other name in\n`capsem.internal`, and the MCP name on a non-default port, is refused with a\n`capsem.internal.reserved` net event. The official images configure their\nagent's `capsem` server at that URL (`images\u002F*\u002FDockerfile`).\n\n### Wire format\n\nLength-prefixed MCP frames over vsock. Each frame contains a bounded JSON-RPC payload plus a stream id, flags, and sanitized process name.\n\n#### Handshake\n\nGuest sends NUL-prefixed metadata line first:\n```\n\\0CAPSEM_META:claude\\n\n```\nThen JSON-RPC messages:\n```json\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}\n{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools\u002Flist\"}\n{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools\u002Fcall\",\"params\":{\"name\":\"github__search\",\"arguments\":{\"q\":\"rust\"}}}\n```\n\n### Tool namespacing\n\nTools are namespaced with `__` (double underscore) to prevent collisions:\n- `github` + `search_repos` -> `github__search_repos`\n- `builtin` + `http_get` -> `builtin__http_get`\n\nThe endpoint parses the namespace to route to the correct server.\n\n### Supported methods\n\n| Method | Behavior |\n|--------|----------|\n| `initialize` | Return protocol version + capabilities |\n| `notifications\u002Finitialized` | Notification (no response) |\n| `tools\u002Flist` | Return builtin + all external server tools |\n| `tools\u002Fcall` | Policy check -> route to server -> call via rmcp |\n| `resources\u002Flist` | Return resource catalog from all servers |\n| `resources\u002Fread` | Lookup URI -> read via rmcp |\n| `prompts\u002Flist` | Return prompt catalog |\n| `prompts\u002Fget` | Lookup name -> get via rmcp |\n\n### Security evaluation\n\n```\n1. Parse MCP frame into typed `SecurityEvent` MCP fields.\n2. Apply the shared security engine plugin\u002Frule rail.\n3. Dispatch only if the effective action allows it.\n4. Log canonical tool row, optional MCP protocol row, and matched security rule rows.\n```\n\nConfig hierarchy: built-in defaults, then the user's `settings.toml`, then\nthe corp config, which wins (and whose `corp_locked` rules hold). `[mcp]` in\nsettings.toml declares servers; tool permissions are settings-managed\n`profiles.rules.*` rules and `default.mcp`, edited through `\u002Fmcp\u002F...` routes.\nThe service merges all three into each session's `vm\u002Factive_policy.toml`.\nThere is no MCP-specific decision provider or `user.toml` override rail.\n\nDecisions use the shared security action enum: `allow`, `ask`, `block`,\n`rewrite`, `preprocess`, and `postprocess`. `ask` waits for an approval or\ndenial before dispatch; `block` returns a policy JSON-RPC error without calling\nthe tool.\n\n### Built-in tools\n\n`capsem-mcp-builtin` exposes the HTTP tools `fetch_http`, `grep_http`, and\n`http_headers`, plus `echo`, a zero-I\u002FO probe for benchmarking MCP transport\noverhead. Guests see them under the `local__` namespace (e.g.,\n`local__fetch_http`).\n\n### Endpoint key source files\n\n| File | Purpose |\n|------|---------|\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fnet\u002Fmitm_proxy\u002Fmcp_frame.rs` | Framed transport parser, stream lifecycle, disconnect metrics |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fnet\u002Fmitm_proxy\u002Fmcp_endpoint.rs` | JSON-RPC handler, policy, dispatch, telemetry logging |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fmcp\u002Ftypes.rs` | JsonRpcRequest\u002FResponse, McpToolDef, annotations |\n| `crates\u002Fcapsem-mcp-aggregator\u002Fsrc\u002Fserver_manager.rs` | rmcp client pool, tool routing, catalog |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fmcp\u002Fpolicy.rs` | Tool\u002Fserver allow\u002Fwarn\u002Fblock decisions |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fmcp\u002Fmod.rs` | Tool cache, server detection, collision detection |\n| `crates\u002Fcapsem-agent\u002Fsrc\u002Fmcp_server.rs` | capsem-mcp-server binary (stdin\u002Fstdout relay) |\n\n### Telemetry (tool_calls and security events)\n\n`tool_calls` is the canonical user\u002Fsecurity ledger for all tool origins:\nmodel-native, built-in\u002Flocal, and MCP-origin. UI counts, CEL rules, and\nforensic tool activity must use this table.\n\nMCP protocol facts such as initialize\u002Flist\u002Fresource frames are security events.\nA visible MCP `tools\u002Fcall` without a matching `tool_calls` row is a bug.\n\nEvery request\u002Fresponse logged with: timestamp, server_name, method, tool_name,\ncompact request\u002Fresponse display fields, decision, duration_ms, error_message,\nprocess_name, bytes sent\u002Freceived. Full MCP request\u002Fresponse payloads share the\nsame `event_body_blobs` ledger table as HTTP and model traffic; do not add a\nsecond MCP-only body rail.\n\nRead `references\u002Fmcp-wire.md` for the full wire format details.\n\n## Testing\n\nThe npm package owns its unit, type, coverage, and package-build checks:\n\n```sh\npnpm --dir mcp\u002Ftypescript test\npnpm --dir mcp\u002Ftypescript pack\n```\n\n`tests\u002Fcapsem-sdk\u002Ftest_mcp_cli_parity.py` guards canonical CLI\u002FMCP behavior.\n`tests\u002Fironbank\u002Ftest_mcp_settings_ledger.py` packs the npm artifact, launches it\nover stdio with fixture-issued gateway credentials, drives a real VM and guest\nMCP path, and verifies correlated tool, network, and security ledger evidence.\n`tests\u002Fcapsem-installed\u002Ftest_winterfell_gateway.py` verifies the installed\nnative HTTP cohort without requiring Node.js.\n\nThe guest path retains its Rust tests and real-VM diagnostics:\n\n```sh\ncargo test -p capsem-core mcp\njust exec \"capsem-doctor -k mcp\"\n```\n\n## Lessons learned\n\n1. **Never prepend headers to JSON output.** MCP tool responses with `format=json` must return raw, parseable JSON. Do not wrap JSON in pagination headers, content-length prefixes, or any other text. A JSON-format tool once broke because `paginated_response()` prepended `\"Content length: ...\\nShowing: ...\\n\"` to the JSON array, making `json.loads()` fail. Rule: if a tool offers both text and JSON formats, branch early and return JSON directly without passing through text-oriented helpers like `paginated_response()`.\n","# MCP in Capsem\n\nCapsem has two MCP entry points:\n\n1. **`@capsem\u002Fmcp`** (host): a standalone TypeScript stdio server. It presents typed tools and uses `@capsem\u002Fsdk` for authenticated gateway HTTP. It does not open the service UDS, discover local services, or read VM runtime state.\n2. **Guest MCP relay + confined proxy endpoint**: bridges AI agents inside a VM to built-in and external MCP servers through bounded framed records on vsock port 5002.\n\nThe native installer does not install Node.js or download the npm host package.\nThe guest relay, aggregator, and built-in Rust components remain native product\ncomponents.\n\n## Using the npm MCP tools for fast debugging\n\nInstall and register the package with an explicit gateway URL, bearer token, and\ntransport timeout:\n\n```sh\nnpm install --global @capsem\u002Fmcp\nCAPSEM_GATEWAY_TOKEN=\"$(cat ~\u002F.capsem\u002Frun\u002Fgateway.token)\" \\\n  capsem-mcp --gateway-url http:\u002F\u002F127.0.0.1:19222 --timeout-ms 30000\n```\n\nThe token comes from `CAPSEM_GATEWAY_TOKEN` or `--token-file`; `--token` is\nrefused because argv is world-readable. The bearer token belongs only to\nthe host MCP process; never copy it into a VM, container, guest tool argument,\nor workload. stdout is protocol-only and sanitized diagnostics use stderr.\n\nThe current registry is defined in `mcp\u002Ftypescript\u002Fsrc\u002Fhost-tools.ts`,\n`network-tools.ts`, and `mcp-tools.ts`. Parameters follow the TypeScript\nSDK names, including `vm_id`, `vcpu`, `memory`, `env`, and\n`timeout_secs`. Canonical tools include `capsem_pause` and\n`capsem_status`; the retired `capsem_suspend`, `capsem_version`, and\nduplicate `capsem_service_logs` names are not exposed.\n\nThe host tools cover:\n\n- VM lifecycle, one-shot execution, persistence, files, statistics,\n  logs, timelines, panic extraction, and triage.\n- Private network lifecycle, membership, and cursor-based audit logs.\n- Typed MCP server discovery, refresh, permission inspection, and invocation\n  through the running VM's existing relay and security engine.\n\n### Debug workflow\n\n```text\n1. capsem_create\n2. capsem_exec { vm_id: \"...\", command: \"capsem-doctor -k net\" }\n3. capsem_read_file { vm_id: \"...\", path: \"\u002Ftmp\u002Fcapsem-init.log\" }\n4. capsem_timeline { vm_id: \"...\", layers: \"net,tool,fs\", limit: 50 }\n5. Fix and rebuild the owning native component when required.\n6. capsem_delete { vm_id: \"...\" }\n```\n\nThe `--timeout-ms` option bounds the SDK HTTP request.\n`timeout_secs` on execution tools bounds the guest command. Cancellation\ncloses the local request; it does not delete the VM or undo an accepted\nmutation. The SDK does not retry mutations.\n\n### When to use MCP tools vs just recipes\n\n| Scenario | Use |\n| --- | --- |\n| Targeted guest behavior | `capsem_exec` |\n| Read a guest file | `capsem_read_file` |\n| Inspect typed audit evidence | `capsem_timeline`, statistics, and network logs |\n| Run capsem-doctor | `capsem_exec` |\n| Full regression suite | `just test` |\n| Focused native validation | `just focus-test functional` |\n\n## npm host MCP server\n\n```text\nHost AI agent -> @capsem\u002Fmcp (stdio) -> @capsem\u002Fsdk\n  -> authenticated capsem-gateway HTTP -> capsem-service\n```\n\nThe npm process owns tool presentation and typed input\u002Foutput only. The gateway\nauthenticates control requests. The service, VM owner, security engine, and\nlogger retain lifecycle, trusted identity, policy, credentials, and audit\nstorage. The npm process receives no virtualization entitlement, registry\ncredential, CA private key, direct database access, or service socket access.\n\n### Key source files\n\n| File | Purpose |\n| --- | --- |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fcli.ts` | Protocol-only stdio executable |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fserver.ts` | SDK client and tool registration |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fhost-tools.ts` | VM, file, and diagnostic tools |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fnetwork-tools.ts` | Private network tools |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fmcp-tools.ts` | MCP server discovery and calls |\n| `mcp\u002Ftypescript\u002Fsrc\u002Fresults.ts` | Structured results and sanitized errors |\n| `sdk\u002Ftypescript\u002Fsrc\u002F` | Typed gateway client and validators |\n\n## MCP subprocess architecture\n\nThe guest MCP path is not a single process. The per-session `capsem-proxy`\nowns the guest-facing framed endpoint, MCP policy, and ledger admission.\n`capsem-process` owns a scoped adapter to the aggregator and VM exposure tools;\nit cannot reopen the session ledger and the proxy cannot reach the aggregator\nexcept through that granted connected channel. The service supervises two\nconfined native workers behind the adapter:\n\n| Crate | Role | Privileges |\n|-------|------|-----------|\n| `capsem-mcp-aggregator` | Parses MCP transports and routes tools over a connected MessagePack RPC channel. | Inherited HTTP\u002F2 capability for configured endpoints and a narrow builtin-peer spawn channel; no ambient files, direct network, credentials, execution, signals, VM or ledger authority. |\n| `capsem-mcp-builtin` | Implements HTTP tools (`fetch_http`, `grep_http`, `http_headers`) and `echo` over a connected MCP channel. | Inherited HTTP capability only. The service performs current URL\u002Faddress policy and credential handling; HTTP ledger facts return in `_meta` `dev.capsem\u002Fledger`. No policy-file or database access. |\n\nRationale: isolating external-server connections in a low-privilege subprocess means a compromised third-party MCP server cannot reach the host filesystem or the session DB. The built-in tool server runs in its own process for the same reason.\n\nThe shared `capsem-mcp-transport` crate owns connected HTTP capabilities and\nrole bootstrap; it stays independent of VM core, policy, credentials and RMCP.\nThe service supplies role\u002Fincarnation bootstrap records and connected descriptors.\nEach native entrypoint clears inherited environment\u002Fdescriptors, prepares a\ncurrent-thread runtime, installs Seatbelt on macOS or Landlock plus seccomp on\nLinux, and attests denial before acknowledging readiness. Parent watching starts\nafter confinement. Linux aggregator\u002Fbuiltin profiles allow threads but deny forked\nprocesses; approved external executable profiles must retain their own sandbox.\n\nThe owner receives one full-duplex RPC descriptor, never native child handles.\nMessagePack framing is `[4-byte big-endian length][payload]`; builtin MCP uses\nJSON-RPC lines over its own connected descriptor. Use async stream halves in both\ndirections: stdin adoption sets nonblocking flags, so a blocking stdout adapter\nover a duplicate descriptor fails under backpressure.\n\nThe service owns destinations, current policy, credentials and process reaping.\nRefresh preserves inherited capabilities. HTTP streams cancel on policy changes\nor retirement; generation completion awaits broker cleanup and positively reaped\nnative children. Separate PIDs, parent watchers or `kill_on_drop` alone are not\nthat completion proof.\n\nSettings configure external HTTP servers; there is no public arbitrary host\nstdio command\u002Ffile-grant setting. The native spawn broker accepts only predefined\nbuiltin peer indices. New external stdio support needs explicit executable\u002Ffile\ngrants under `McpExternal`, never the trusted companion `with no-sandbox` rule.\n\n`\u002Frun\u002Fcapsem-mcp-server` relays guest JSON-RPC as bounded frames over vsock 5002.\nThe proxy evaluates policy before invoking the scoped owner adapter. Tool facts\nreach `tool_calls` through the proxy's typed ledger capability; the native MCP\nworkers never open the ledger.\n\nBinaries land in `~\u002F.capsem\u002Fbin\u002F` at install time: `capsem-mcp-aggregator`, `capsem-mcp-builtin`.\n\n## Guest MCP Endpoint\n\nThe guest MCP relay bridges AI agents in the guest VM to the confined proxy\nendpoint. It runs over vsock port 5002 using bounded length-prefixed MCP\nframes that carry JSON-RPC payloads and per-frame process attribution.\n\nFramed guest MCP over `vsock:5002` must be tested as the default transport, not as an opt-in benchmark mode. The minimum hardening matrix for that path is:\n- parser\u002Finterpreter: bounded frames, invalid JSON, malformed flags, stream-id reuse, notification\u002Frequest-id mismatch\n- dispatch: `initialize`, `tools\u002Flist`, builtin `tools\u002Fcall`, configured external HTTP `tools\u002Fcall`, `resources\u002Flist`, `prompts\u002Flist`, and method error mapping\n- policy: live policy mutation, per-tool block, resource URI rule, argument-name rule, argument-value rule, return-value rule, deny-over-allow precedence, and proof that blocked requests\u002Fresponses do not leak original data\n- telemetry: `session.db` rows for success, denial, timeout, process attribution, request\u002Fresponse previews, policy fields, and terminal errors\n- boundary: aggregator and VM owner remain DB-free; `capsem-proxy` writes MCP\n  audit facts through its role-bound ledger capability\n- VM E2E: boot a real VM, run `\u002Frun\u002Fcapsem-mcp-server` with no transport\n  override, then inspect the typed timeline\u002Fledger routes\n\n### Architecture\n\n```\nGuest (Claude\u002FGemini) -> capsem-mcp-server (stdin\u002Fstdout relay)\n  -> framed vsock:5002 -> capsem-process descriptor handoff -> capsem-proxy\n  -> SecurityEvent rule check -> scoped VM-owner MCP adapter\n  -> builtin tools | external MCP servers (via aggregator)\n  -> typed ledger capability -> tool_calls plus security_rule_events evidence\n```\n\n### Workloads: streamable HTTP at `mcp.capsem.internal`\n\nAn OCI workload cannot open vsock (seccomp), so it reaches the same endpoint\nover HTTP: `POST http:\u002F\u002Fmcp.capsem.internal\u002Fmcp`, one JSON-RPC message per\nrequest (200 with the answer, 202 for notifications, 405 for GET\u002FDELETE).\nThe host DNS answers that one name in every VM with `192.0.2.1` before any\nprivate-network member is asked, and never upstream. The VM's port-80 rule\ncarries the connection to the MITM proxy, where `mitm_proxy\u002Fmcp_http.rs`\nclaims it before HTTP policy, body sniffing or any dial and hands it to\n`mcp_frame::dispatch_http_message` -- the framed path's parser, in-flight\nbound, MCP security rail and `tool_calls` ledger, with `transport = 'http'`\nand the connection's process name. Attribution is the proxy instance: one\nper VM, with that VM's endpoint and session ledger. Every other name in\n`capsem.internal`, and the MCP name on a non-default port, is refused with a\n`capsem.internal.reserved` net event. The official images configure their\nagent's `capsem` server at that URL (`images\u002F*\u002FDockerfile`).\n\n### Wire format\n\nLength-prefixed MCP frames over vsock. Each frame contains a bounded JSON-RPC payload plus a stream id, flags, and sanitized process name.\n\n#### Handshake\n\nGuest sends NUL-prefixed metadata line first:\n```\n\\0CAPSEM_META:claude\\n\n```\nThen JSON-RPC messages:\n```json\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}\n{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools\u002Flist\"}\n{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools\u002Fcall\",\"params\":{\"name\":\"github__search\",\"arguments\":{\"q\":\"rust\"}}}\n```\n\n### Tool namespacing\n\nTools are namespaced with `__` (double underscore) to prevent collisions:\n- `github` + `search_repos` -> `github__search_repos`\n- `builtin` + `http_get` -> `builtin__http_get`\n\nThe endpoint parses the namespace to route to the correct server.\n\n### Supported methods\n\n| Method | Behavior |\n|--------|----------|\n| `initialize` | Return protocol version + capabilities |\n| `notifications\u002Finitialized` | Notification (no response) |\n| `tools\u002Flist` | Return builtin + all external server tools |\n| `tools\u002Fcall` | Policy check -> route to server -> call via rmcp |\n| `resources\u002Flist` | Return resource catalog from all servers |\n| `resources\u002Fread` | Lookup URI -> read via rmcp |\n| `prompts\u002Flist` | Return prompt catalog |\n| `prompts\u002Fget` | Lookup name -> get via rmcp |\n\n### Security evaluation\n\n```\n1. Parse MCP frame into typed `SecurityEvent` MCP fields.\n2. Apply the shared security engine plugin\u002Frule rail.\n3. Dispatch only if the effective action allows it.\n4. Log canonical tool row, optional MCP protocol row, and matched security rule rows.\n```\n\nConfig hierarchy: built-in defaults, then the user's `settings.toml`, then\nthe corp config, which wins (and whose `corp_locked` rules hold). `[mcp]` in\nsettings.toml declares servers; tool permissions are settings-managed\n`profiles.rules.*` rules and `default.mcp`, edited through `\u002Fmcp\u002F...` routes.\nThe service merges all three into each session's `vm\u002Factive_policy.toml`.\nThere is no MCP-specific decision provider or `user.toml` override rail.\n\nDecisions use the shared security action enum: `allow`, `ask`, `block`,\n`rewrite`, `preprocess`, and `postprocess`. `ask` waits for an approval or\ndenial before dispatch; `block` returns a policy JSON-RPC error without calling\nthe tool.\n\n### Built-in tools\n\n`capsem-mcp-builtin` exposes the HTTP tools `fetch_http`, `grep_http`, and\n`http_headers`, plus `echo`, a zero-I\u002FO probe for benchmarking MCP transport\noverhead. Guests see them under the `local__` namespace (e.g.,\n`local__fetch_http`).\n\n### Endpoint key source files\n\n| File | Purpose |\n|------|---------|\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fnet\u002Fmitm_proxy\u002Fmcp_frame.rs` | Framed transport parser, stream lifecycle, disconnect metrics |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fnet\u002Fmitm_proxy\u002Fmcp_endpoint.rs` | JSON-RPC handler, policy, dispatch, telemetry logging |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fmcp\u002Ftypes.rs` | JsonRpcRequest\u002FResponse, McpToolDef, annotations |\n| `crates\u002Fcapsem-mcp-aggregator\u002Fsrc\u002Fserver_manager.rs` | rmcp client pool, tool routing, catalog |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fmcp\u002Fpolicy.rs` | Tool\u002Fserver allow\u002Fwarn\u002Fblock decisions |\n| `crates\u002Fcapsem-core\u002Fsrc\u002Fmcp\u002Fmod.rs` | Tool cache, server detection, collision detection |\n| `crates\u002Fcapsem-agent\u002Fsrc\u002Fmcp_server.rs` | capsem-mcp-server binary (stdin\u002Fstdout relay) |\n\n### Telemetry (tool_calls and security events)\n\n`tool_calls` is the canonical user\u002Fsecurity ledger for all tool origins:\nmodel-native, built-in\u002Flocal, and MCP-origin. UI counts, CEL rules, and\nforensic tool activity must use this table.\n\nMCP protocol facts such as initialize\u002Flist\u002Fresource frames are security events.\nA visible MCP `tools\u002Fcall` without a matching `tool_calls` row is a bug.\n\nEvery request\u002Fresponse logged with: timestamp, server_name, method, tool_name,\ncompact request\u002Fresponse display fields, decision, duration_ms, error_message,\nprocess_name, bytes sent\u002Freceived. Full MCP request\u002Fresponse payloads share the\nsame `event_body_blobs` ledger table as HTTP and model traffic; do not add a\nsecond MCP-only body rail.\n\nRead `references\u002Fmcp-wire.md` for the full wire format details.\n\n## Testing\n\nThe npm package owns its unit, type, coverage, and package-build checks:\n\n```sh\npnpm --dir mcp\u002Ftypescript test\npnpm --dir mcp\u002Ftypescript pack\n```\n\n`tests\u002Fcapsem-sdk\u002Ftest_mcp_cli_parity.py` guards canonical CLI\u002FMCP behavior.\n`tests\u002Fironbank\u002Ftest_mcp_settings_ledger.py` packs the npm artifact, launches it\nover stdio with fixture-issued gateway credentials, drives a real VM and guest\nMCP path, and verifies correlated tool, network, and security ledger evidence.\n`tests\u002Fcapsem-installed\u002Ftest_winterfell_gateway.py` verifies the installed\nnative HTTP cohort without requiring Node.js.\n\nThe guest path retains its Rust tests and real-VM diagnostics:\n\n```sh\ncargo test -p capsem-core mcp\njust exec \"capsem-doctor -k mcp\"\n```\n\n## Lessons learned\n\n1. **Never prepend headers to JSON output.** MCP tool responses with `format=json` must return raw, parseable JSON. Do not wrap JSON in pagination headers, content-length prefixes, or any other text. A JSON-format tool once broke because `paginated_response()` prepended `\"Content length: ...\\nShowing: ...\\n\"` to the JSON array, making `json.loads()` fail. Rule: if a tool offers both text and JSON formats, branch early and return JSON directly without passing through text-oriented helpers like `paginated_response()`.\n",{},"main","d357170bb677125c80cebed720e62bf47bf083ad","skills\u002Fdev-mcp",26,"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem","https:\u002F\u002Fgithub.com\u002Fgoogle","https:\u002F\u002Fopensource.google\u002F","Google ❤️ Open Source","2026-11-04 04:36:10",{"source":7,"skillName":5,"commands":40,"prompt":50,"zipAvailable":51},[41,44,47],{"tool":42,"command":43},"npx","npx skills add google\u002Fcapsem --skill dev-mcp",{"tool":45,"command":46},"bunx","bunx skills add google\u002Fcapsem --skill dev-mcp",{"tool":48,"command":49},"pnpm","pnpm dlx skills add google\u002Fcapsem --skill dev-mcp","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fdev-mcp\u002FSKILL.md",true,[53],{"path":54,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":57,"sourceHtmlUrl":58},"references\u002Fmcp-wire.md","REFERENCE","参考文档",5224,"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fdev-mcp\u002Freferences\u002Fmcp-wire.md",[60,69,85,94,101,108],{"skillNo":61,"name":62,"nameZh":6,"description":63,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":67,"publishedAt":25},"01M699558T2T4DJMBN08ZCQ79K","use-capsem","Run tasks in Capsem VMs through MCP. Use when an agent needs isolated execution, file analysis, or reports returned to the host.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fuse-capsem\u002FSKILL.md",[66],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[68,23],"use capsem",{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":83,"publishedAt":25},"01M69953Z1CSVQ4N07QX529CZY","site-marketing","The marketing website, capsem.org. Use when editing copy, adding sections, or changing the site theme.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fsite-marketing\u002FSKILL.md",[75,78],{"categoryNo":76,"categoryName":77,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000019","营销推广",{"categoryNo":79,"categoryName":80,"taxonomyType":81,"taxonomyTypeDesc":82},"01JBSEEDCAT000000000000035","市场营销","PROFESSION","适用职业",[84],"site marketing",{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":25},"01M69952B33KBBV37N8QTC4191","site-infra","The documentation site, docs.capsem.org. Use for user-directed Diátaxis documentation and Astro Starlight changes.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fsite-infra\u002FSKILL.md",[],[92,93],"site infra","docs",{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":25},"01M69950YKCZKCDB98JDQNBBRJ","site-architecture","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fsite-architecture\u002FSKILL.md",[],[100],"site architecture",{"skillNo":102,"name":103,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":106,"publishedAt":25},"01M6994Z61JWZ0G2YRJTWFH933","release-process","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Frelease-process\u002FSKILL.md",[],[107],"release process",{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":121,"publishedAt":25},"01M6994XKHADM6FBTYTAPG7FYC","meta-skill-creation","Create, improve, and evaluate skills. Use when writing a new skill, editing an existing one, or tuning a description for better triggering.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fcapsem\u002Fblob\u002Fmain\u002Fskills\u002Fmeta-skill-creation\u002FSKILL.md",[114,117,118],{"categoryNo":115,"categoryName":116,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":119,"categoryName":120,"taxonomyType":81,"taxonomyTypeDesc":82},"01JBSEEDCAT000000000000036","内容创作者",[122,123],"meta skill creation","writing"]