Mirror x-mcp-header tool arguments into Mcp-Param-* request headers (SEP-2243)

Merged
KL
4mo ago
+393-14

What

Implements client-side Mcp-Param-* header emission (SEP-2243 "Custom Headers from Tool Parameters"). The client already validated and dropped tools with malformed x-mcp-header annotations but never emitted the headers - the gap the conformance expected-failures file named as the unimplemented S8 design.

On a modern (2026-07-28) Streamable HTTP connection, a tools/call now mirrors each argument annotated with x-mcp-header into an Mcp-Param-<name> header: string verbatim, integer as decimal, boolean as true/false, base64-sentinel-wrapped when not header-safe. Null or absent arguments are omitted, unannotated parameters are never mirrored, and the argument stays in the request body (additive, per spec).

The tool schema comes from the tool's last list_tools entry (annotations are cached); a tool the client never listed emits no Mcp-Param-* headers (spec-permitted).

Changes

  • mcp.shared.inbound: x_mcp_header_map() (schema -> path->token) and mcp_param_headers() (args -> headers), reusing the existing schema walk and encode_header_value codec.
  • ClientSession: modern stamp injects param headers for tools/call; list_tools caches each kept tool's annotations and evicts the entry when a re-list drops the tool.
  • Conformance client.py: adds the http-custom-headers handler (replays the harness toolCalls).
  • docs/migration.md updated.

Conformance

http-custom-headers now passes 18/18 on both the default and 2026-07-28 client legs and is removed from both expected-failures baselines.

The harness fixture annotated number-typed properties with x-mcp-header, which the spec forbids ("Parameters with type number are not permitted"); a conformant client drops those tools, so the scenario was previously unpassable. That is fixed upstream in modelcontextprotocol/conformance#371. CI is temporarily pinned to the pkg.pr.new preview of #371, with a CONFORMANCE_PKG_SHA256 digest + fetch-and-verify step (the repo's established pattern for URL pins) so the mutable URL is verified, not trusted. Repin to the published release once #371 ships.

All tests pass, 100% coverage, strict-no-cover/pyright/ruff clean.

AI Disclaimer

This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.