anthropics / anthropics/claude-code

[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)

Aperta
#93,290 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area:mcp bug has repro platform:macos
Lingua principale
Python
Stelle
145k
Fork
23.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

### 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 via `server/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

```json
{"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:

```sh
# 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

1. Run a stateless Streamable HTTP MCP server built with modelcontextprotocol/go-sdk v1.7.0 (`StreamableHTTPOptions{Stateless: true}`), reachable over HTTPS.
2. Register it as a claude.ai custom connector.
3. In Claude Desktop (macOS 1.49585.0) or the Claude Code CLI 2.1.267 (macOS), call any tool of the connector.
4. Observe the `-32603 header mismatch` error above on every call; server logs show `Mcp-Protocol-Version: 2025-11-25` with `_meta.protocolVersion: 2026-07-28`.
5. 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.

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

Start by reproducing the request through a claude.ai custom connector and compare the Mcp-Protocol-Version header with params._meta.protocolVersion. Use mcp/streamable.go in the referenced Go SDK as the server-side rejection reference, and compare the failing Desktop/CLI path with the working VS Code extension path. Done means requests use matching negotiated versions or consistently use the legacy shape.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
go, kubernetes
Ambito
api, networking
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
45/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.