[BUG] Claude Desktop / Claude Code CLI send Mcp-Protocol-Version: 2025-11-25 with _meta protocolVersion 2026-07-28 via claude.ai connectors — spec-compliant stateless servers reject every call (-32020 header mismatch)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (closest: #92835, which covers the legacy header-vs-negotiated-version case and later drifted to a VS Code tools-loading problem)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.267)
What's Wrong?
When Claude Desktop or the Claude Code CLI talks to a remote MCP server through a claude.ai custom connector, every request after the handshake carries a 2026-07-28 body but a 2025-11-25 header:
- JSON-RPC
params._meta["io.modelcontextprotocol/protocolVersion"] = "2026-07-28"(SEP-2575, the client negotiated the 2026-07-28 protocol viaserver/discover) - HTTP header
Mcp-Protocol-Version: 2025-11-25
SEP-2575 requires the header to match the per-request _meta value, so a spec-compliant stateless server rejects the request with JSON-RPC -32020 (HEADER_MISMATCH), HTTP 400. The connector relays it to the client as -32603. Result: the connector shows as connected, but every tool call fails.
Server side: Go, github.com/modelcontextprotocol/go-sdk v1.7.0, StreamableHTTPOptions{Stateless: true}, behind a plain Kubernetes ingress that only adds CORS headers — nothing in the chain rewrites Mcp-* headers. The rejection is the SDK's own SEP-2575 check (mcp/streamable.go, "header mismatch" branch).
The same server, reached through the same claude.ai connector from the Claude Code VS Code extension (2.1.267, Linux devcontainer), works: that path sends a legacy initialize (2025-11-25) with no _meta, so the check never arms. So the failure depends on which client implementation drives the connector, not on the server.
This is the client-side mirror of what the Go SDK fixed on its own client in modelcontextprotocol/go-sdk#1162 / #1164 (header taken from a default/context while the body carries the negotiated version).
What Should Happen?
After negotiating 2026-07-28, the client must send Mcp-Protocol-Version: 2026-07-28 on every request that carries _meta.protocolVersion = "2026-07-28" (and Mcp-Method/Mcp-Name mirroring per SEP-2575). Alternatively, stay entirely on the legacy 2025-11-25 handshake and send no _meta.
Error Messages/Logs
{"code": -32603, "message": "header mismatch: Mcp-Protocol-Version header \"2025-11-25\" does not match request io.modelcontextprotocol/protocolVersion \"2026-07-28\"", "data": null, "request_id": "req_011CeuMUHDFhBEndkga4oR8N"}
Reproduction of the server side against a bare go-sdk v1.7.0 stateless server (no proxy involved), showing the exact rejection and that the legacy shape passes:
# B — what Claude Desktop / CLI send: rejected
curl -s -X POST http://127.0.0.1:18081 -H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' -H 'Mcp-Protocol-Version: 2025-11-25' \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28"}}}'
# -> 400 {"jsonrpc":"2.0","id":2,"error":{"code":-32020,"message":"Mcp-Protocol-Version header \"2025-11-25\" does not match request io.modelcontextprotocol/protocolVersion \"2026-07-28\""}}
# E — legacy shape (what the VS Code extension path sends): accepted
curl -s -X POST http://127.0.0.1:18081 -H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' -H 'Mcp-Protocol-Version: 2025-11-25' \
--data '{"jsonrpc":"2.0","id":5,"method":"tools/list","params":{}}'
# -> 200 {"jsonrpc":"2.0","id":5,"result":{"tools":[]}}
Steps to Reproduce
- Run a stateless Streamable HTTP MCP server built with modelcontextprotocol/go-sdk v1.7.0 (
StreamableHTTPOptions{Stateless: true}), reachable over HTTPS. - Register it as a claude.ai custom connector.
- In Claude Desktop (macOS 1.49585.0) or the Claude Code CLI 2.1.267 (macOS), call any tool of the connector.
- Observe the
-32603 header mismatcherror above on every call; server logs showMcp-Protocol-Version: 2025-11-25with_meta.protocolVersion: 2026-07-28. - Same connector from the Claude Code VS Code extension 2.1.267 (Linux devcontainer): tool calls succeed.
Claude Model
Sonnet / Opus (model-independent)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.267 (CLI, macOS — failing); 2.1.267 (VS Code extension, Linux — working). Claude for Mac 1.49585.0 (41ad1d) — failing.
Platform
claude.ai custom connector (remote MCP)
Operating System
macOS (failing clients), Linux devcontainer (working client)
Terminal/Shell
zsh / Claude Desktop
Additional Information
- Header mismatch rejection in the Go SDK: https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/mcp/streamable.go (SEP-2575 block in
serveStateless) - Go SDK's own client-side fix of the same asymmetry: https://github.com/modelcontextprotocol/go-sdk/issues/1164, https://github.com/modelcontextprotocol/go-sdk/issues/1162
- Related but different: #92835 (legacy header vs negotiated version, no
_meta) - Spec ambiguity discussion for the legacy path (not applicable to the SEP-2575 path): https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2721
Happy to provide server-side request logs (headers + body) on request.