Skip to content

MCP Client Capability Matrix

What MCP clients actually support, measured — not what they declare in the handshake, and not what their docs imply. Maintained as findings accumulate; each row states when and against which version it was probed. Corrections and probe results from other clients are welcome as issues or PRs.

Instrument: examples/mcp_client_probe.py (~60-line FastMCP server, one probe per feature, self-test mode as ground truth). Method described in the original spike record; a run takes minutes. When client versions jump, re-run before trusting this table.

The matrix

Headless probes 2026-07-02 (claude-code 2.1.198, codex-cli 0.142.4); interactive elicitation probes 2026-07-05 (claude-code 2.1.201). Stdio transport throughout.

Feature Claude Code Codex CLI
Resources: list + read yes yes
Resource templates in listing no — readable if the concrete URI is known, invisible in the listing yes — template URIs enumerated
Binary resources yes — bytes materialize as a local file, path returned to the agent yes — inline base64
Sampling no (capabilities.sampling = null) no
Elicitation, headless declared (form + url); auto-cancels ({"action":"cancel"} synthesized) declared; auto-cancels — and the parser rejects FastMCP's schema before any UI (details)
Elicitation, interactive yes — full battery passed (2.1.201): inline bordered form, Accept/Decline buttons, Esc = cancel; accept/decline/cancel all map to the spec actions; repeat requests surface fresh forms form UI exists in the TUI (since openai/codex PR #17043, 2026-04) but unreachable from FastMCP servers due to the schema bug
Elicitation: string-enum rendering yes — ["allow", "deny"] renders as a selectable option list, not free text (2.1.201) untested (blocked by the schema bug)
Skills over MCP (fastmcp SkillsDirectoryProvider) skill's static resources (skill://<name>/SKILL.md) appear in resource listing; download_skill works; the list_skills/sync_skills tool layer is not surfaced (2026-07-03) untested
Progress / log notifications accepted, never surfaced to the headless agent not observed
Protocol version 2025-11-25 2025-06-18

Behavioral findings the table can't hold

These matter more than the feature rows.

Capability ≠ discovery. A cold agent with the resource-listing tool available from turn one never called it unaided — and said so explicitly when asked afterward. Resources are only found when a channel the agent already reads names them in plain text. Full argument: Agents Don't Find Resources.

Elicitation outlives the tool call that triggered it. In Claude Code, the client-side tool call can time out (or the server-side run can die) while the form stays open on screen; the human's late answer still resolves the protocol request. Verified live 2026-07-05: a form answered ~2 minutes after the requesting call had already returned TimeoutError. If your server mutates state on elicitation answers, tie the request's lifetime to the work that asked — a late answer with no witness is a silent policy change. (This forced run-scoped escalation cancellation in toolplane.)

Synthetic cancel is indistinguishable from a real user cancel. Headless surfaces answer {"action":"cancel"} immediately; a human pressing Esc sends the same thing. Servers cannot detect "this surface can't prompt" — design elicitation as an enhancement over a path that already works, never as a new failure mode.

Headless Claude Code can still answer elicitations via the Elicitation hook (matcher = MCP server name), so degraded mode is scriptable, not dead.

What this means if you're building an MCP server

  • Resources are safe to build on; deliver concrete URIs in tool results (Claude Code can't discover templated URIs from listings).
  • Binary resources are a genuinely good artifact lane on Claude Code — the agent gets a real file path, no base64 in context.
  • Don't design on sampling.
  • Elicitation works interactively on Claude Code (≥2.1.76; battery-verified on 2.1.201). Keep response schemas to flat strings/enums, fail closed on cancel/decline/error, and scope requests to the work that issued them.
  • For Codex, elicitation is blocked upstream: openai/codex#31163.