# mcp (taicpp) Port of the MCP section of `../tai.py`, normative text in `../mcp.md`. Namespace `detz::mcp`, sources in `src/mcp/`. Reuses the `detz::wire` layer from `src/cap/` (see `docs/cap.md`). **Wire parsing is untrusted input**: every byte here comes off a socket, so the parsers follow [cpp-safety.md](cpp-safety.md) (overflow-safe size checks, unsigned range-checked lengths, guarded shifts, no exception escaping the read loop, hostile-input tests under sanitizers). We do not accept regressions in that. ## Spec delta (this milestone) The placeholder `serve_list`/`serve_call` is replaced by the real client, OAuth, bridge, vault tables, tool backends and server. ## Parity table | Python | C++ | |---|---| | `MCP_PROTOCOL_VERSION` `MCP_CLIENT_NAME` `MCP_TOOL_PREFIX` `MCP_RESOLVER_TIMEOUT` `MCP_RESOLVER_STEPS` `MCP_OAUTH_TIMEOUT` `MCP_RESOLVER_BRIEF` `MCP_CONNECT_TIMEOUT` `MCP_CALL_TIMEOUT` `MCP_RESULT_MAX` `MCP_HTTP_MAX_BODY` `MCP_MAX_SERVERS` `MCP_MAX_TOOLS` `MCP_SESSION_IDLE` `MCP_CHAT_TIMEOUT` `MCP_DEFAULT_PORT` `MCP_SERVER_RE` `MCP_VAR_RE` `MCP_GITHUB_RE` | `mcp_constants.h` | | `McpError` `McpJsonRpcError` `McpHttpError` | `mcp_error.h` | | `mcp_sanitize_name` `mcp_github_target` `mcp_expand_vars` `mcp_rewrite_placeholders` `mcp_entry_from_mapping` `mcp_normalize` `mcp_tool_hash` `mcp_bridge_name` | `McpNormalizer` | | `McpStdioTransport` `McpHttpTransport` `McpClient` | same names, `McpTransport` interface | | `McpOAuthCallback` `McpOAuth` | same names | | `McpBridge` | `McpBridge` over `McpHost` | | `Store.mcp_*` | `McpStore` interface, `McpSqliteStore` (four tables) | | `Tools.run_mcp_*`, `mcp_resolve_github`, `mcp_resolve_agent`, `mcp_schemas` | `McpTools` | | `McpServeSession` `McpServeContext` `mcp_serve_tools` `mcp_serve_handler` `mcp_serve_main` | `McpServeSession` `McpServeContext` `McpServeHandler` `McpServeMain` | ## Wire subset Methods: `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`, `notifications/cancelled`. Errors `-32601`, `-32602`, `-32603`. Protocol `2025-11-25`. ## Caps `MCP_CONNECT_TIMEOUT` 20, `MCP_CALL_TIMEOUT` 60, `MCP_RESULT_MAX` 65536, `MCP_HTTP_MAX_BODY` 10485760, `MCP_MAX_SERVERS` 32, `MCP_MAX_TOOLS` 100, `MCP_SESSION_IDLE` 3600, `MCP_CHAT_TIMEOUT` 600, `MCP_RESOLVER_TIMEOUT` 300, `MCP_RESOLVER_STEPS` 12, `MCP_OAUTH_TIMEOUT` 300. ## Vault tables `mcp_servers`, `mcp_server_profiles`, `mcp_tool_pins`, `mcp_server_tools` (`McpSqliteStore::ensureSchema`). ## Agent tools `mcp_add`, `mcp_list`, `mcp_enable`, `mcp_disable`, `mcp_remove`, `mcp_tools`, `mcp_call`, `mcp_login` (`McpTools::dispatch`). Served extras: `chat`, `completions`. ## Differences from Python (declared) - `mcp_login` browser: opens `xdg-open` only when `DISPLAY`/`WAYLAND_DISPLAY` is set; Python's `webbrowser` has a longer fallback list. - Transport error texts come from libcurl mapped to urllib wording (`[Errno 111] Connection refused`, `timed out`); exotic failures differ. - HTTP `Server:` header omits the Python version suffix. - `mcp_store_tools` runs in one transaction (Python leaves partial writes pending on a duplicate tool name). - stdio stderr is a pipe nobody drains, exactly as in tai.py (the doc says drained, the code does not). ## Verification `tests/capmcp_unit.cpp` (vault tables, normalizer, served endpoint in process). No differential run against tai.py yet for the client, OAuth, bridge and server (see docs/wiring/capmcp.md, Known gaps).