Expand description
P5-2 (COMPOSABLE-HARNESS-DESIGN.md §2 module 15 mcp.client, D7 rows
1-8): full Model Context Protocol
client support — stdio (P5-1 baseline, GROWN not rewritten), remote
HTTP/SSE transports, resources + templates, prompts-as-commands,
server instructions, and elicitation — plus the handle_request /
serve_stdio harness-as-MCP-server direction (module 16).
Transport model. McpClient hides three wire shapes behind one
request()/list_tools()/call_tool()/… API:
McpClient::connect— stdio (newline-delimited JSON-RPC over a spawned child process’s stdin/stdout). Pre-existing (P5-1 baseline).McpClient::connect_http— a single POST per request (“Streamable HTTP”, non-streaming case): the response body is either a bareapplication/jsonobject or atext/event-streambody carrying the one response event. AMcp-Session-Idresponse header, if the server sends one, is captured and replayed on every subsequent request.McpClient::connect_sse— the legacy (2024-11-05) HTTP+SSE transport: a persistentGETstream whose first event names the POST endpoint for client→server messages; a background reader task forwards every subsequent server→client frame into an in-process channel.
Server-initiated requests and notifications. A real MCP session is
bidirectional: while a client request is in flight, the server may push
a notification (resources/updated, …) or even issue its OWN request
back to the client (elicitation/create). McpClient’s read loop
(McpClient::handle_incoming_message) recognizes all three shapes on
both the stdio and SSE transports (persistent, bidirectional
connections) and dispatches server-initiated requests to the installed
McpElicitationHandler — see that trait’s doc comment for the
HEADLESS-DENY default and the tui-deferred interactive part. The HTTP
transport is a single non-streaming request/response cycle with no
return channel for a reply; a server-initiated request arriving on it
is a documented, tested, fail-CLOSED error (Error::tool("mcp", ...)),
never a silent drop or a hang — see McpClient::http_roundtrip’s doc
comment.
Security posture (this module’s own scope; see also
crate::configfile’s project-sanitization for the config-file side).
MCP OAuth tokens are credentials, the same trust class as
Config::api_key (§3.2 S13) — crate::mcp_oauth handles only the
wire PROTOCOL (device-code grant, refresh); persistence to disk with
trust-grade (owner-only, user/global-directory-only) permissions is a
CLI-layer concern (crates/cli/src/userconfig.rs’s
save_mcp_oauth_tokens/load_mcp_oauth_tokens, mirroring
save_api_key’s existing 0600-perms precedent) — this crate never
writes a token to disk itself. Remote connects honor an active
crate::tools::NetworkPolicy (module 12’s SSRF/domain-allowlist
floor) via crate::tools::check_network_policy, the exact function
crate::tools::ToolContext::check_network itself calls — one
enforcement point, not a second parallel one.
Structs§
- Elicitation
Request - P5-2 (§2.1 dep “elicitation →
tools.questionsurface”; §2 module 6’s own row: “⚡ headless print mode (deny-default like OC, oc§1)”): a server→clientelicitation/createrequest, mid-tools/call, asking the user for structured input. - Elicitation
Response - What an
McpElicitationHandlerreturns for oneElicitationRequest. - Headless
Elicitation Handler - The default: every elicitation request is declined. Correct for
non-interactive/print-mode runs (the only mode this crate’s CLI
embedder —
crates/cli— runs in today); a TUI-backed handler is a tui-deferred follow-up, not built here. - McpClient
- A client connected to an MCP server over stdio, HTTP, or SSE — see the module doc comment for the transport model.
- McpPrompt
ArgDef - One argument a
McpPromptDefaccepts. - McpPrompt
Def - A prompt exposed by a remote MCP server (
prompts/list). - McpPrompt
Source - A prompt this crate can render via
prompts/get— whatMcpServerHandle::promptshands back for a caller to register as a slash-command source (crate::agent::Agent::register_mcp_prompt). - McpResource
Def - A resource exposed by a remote MCP server (
resources/list). - McpResource
Template Def - A resource TEMPLATE exposed by a remote MCP server (
resources/templates/list). - McpServer
Handle - P5-2: one connected server, wrapping the
Arc<Mutex<McpClient>>every derivedTool/prompt-source shares — the single point that produces tools (McpTool), resource tools, prompt names, and the server’s folded-in instructions, so a caller (crates/cli’sattach_mcp) only has to connect once and ask this handle for everything else. - McpTool
- A supercode
Toolbacked by a remote MCP tool. The name is namespacedmcp__<server>__<tool>to match the convention seen in the corpus. - McpTool
Def - A tool exposed by a remote MCP server.
- SdkMcp
Tool - MCP tool projection of the versioned SDK facade. The MCP envelope and tool-call id never enter the SDK request or its canonical session data.
Enums§
- Elicitation
Action - The outcome an
McpElicitationHandlerreturns — the three actions the MCP elicitation spec defines. - McpConnect
Params - How an
McpClientwas connected — kept on the client soMcpClient::reconnectcan rebuild an equivalent connection without the caller having to remember its own parameters.
Constants§
- DEFAULT_
MCP_ TIMEOUT - Default per-request timeout (connect handshake + every subsequent
request()) for the network transports — stdio has no analogous “hung server” risk distinct from a hung read, so it is NOT subject to this timeout (a misbehaving stdio child can still be killed by the caller;kill_on_dropalready covers process cleanup). - MCP_
MAX_ RESOURCE_ BYTES - Hardening cap (same review finding): the maximum joined size of
resources/read’s concatenated text contentsMcpClient::read_resourcewill return before erroring out instead of buffering an unbounded string. - MCP_
MAX_ RESPONSE_ BYTES - Hardening cap (Fable-5 review, memory-DoS-from-a-hostile-configured-
server finding): the maximum size of a single non-streaming HTTP
response body (
McpClient::http_roundtrip) this client will buffer before erroring out. 16 MiB is generous for real tool-call/initialize responses (the actual payloads this transport carries) while bounding how much memory a misbehaving or malicious configured MCP server can force this process to allocate for one response. - MCP_
MAX_ SSE_ FRAME_ BYTES - Hardening cap (same review finding as
MCP_MAX_RESPONSE_BYTES): the maximum size a single un-terminated SSE frame may grow to insideSseLineAccumulatorbefore it’s treated as malformed/hostile and the connection is torn down, rather than the accumulator buffer growing without bound while waiting forever for a blank-line terminator that never arrives.
Traits§
- McpElicitation
Handler - Handles a server-initiated
elicitation/createrequest — thetools.questionsurface’s PROTOCOL side (§2.1 dep). The real interactive prompt UI istui’s job (P5 item #4, not yet built); pending that,HeadlessElicitationHandleris the honest default — DENY (decline), matching module 6’s own “headless print mode: deny-default like OC” row rather than hanging the tool call or silently fabricating an answer. An embedder (or a futuretuiintegration) can install a real interactive handler viaMcpClient::set_elicitation_handler.
Functions§
- cache_
churn_ notice - P5-2 (§2.2 C2 “connect invalidates cache prefix”; §2 module 25
cacheis the referee): the churn notice a caller (crates/cli’sattach_mcp) emits when connecting a server under an activeCachePlan::ImportedPrefix— a pure, independently-testable function so the wording/threshold logic isn’t buried in CLI plumbing. “At minimum emit the churn signal” (P5-2 build brief) — this is that signal; it does not itself reset any cache bookkeeping (seeAgent::register_tool’s own C2 note for the runtime half: any tool registered after the agent’s first turn resetscache_established, MCP-sourced or not). - handle_
request - Handle one JSON-RPC request against a
ToolRegistry, returning the JSON-RPC response (orNonefor notifications that need no reply). - register_
sdk_ tool - Register the SDK adapter alongside ordinary MCP coding tools.
- serve_
stdio - Run a blocking stdio MCP server exposing
registry, reading requests from stdin and writing responses to stdout until EOF.