anthropics / anthropics/claude-ai-mcp
[BUG] claude.ai MCP client reuses JSON-RPC id 1 for every request and pools one session across chats — concurrent calls cross-wire responses on stateful servers
- Lenguaje dominante
- Sin datos de lenguaje
- Estrellas
- 471
- Forks
- 76
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Descripción
### Summary
Two client-side behaviors of the claude.ai custom-connector MCP client combine into user-visible data cross-wiring against spec-conformant stateful streamable-HTTP servers:
1. **Every JSON-RPC request is sent with `id: 1`.** The MCP spec (Base Protocol) requires: "The request ID MUST NOT have been previously used by the requestor within the same session." Our server-side per-call instrumentation shows `rpc_id=1` on 100% of tools/call requests from claude.ai, including requests that are concurrently in flight.
2. **One transport session (mcp-session-id) is pooled across multiple conversations of the same user.** We observed two different claude.ai chats (distinct conversations, same account) issuing tool calls on the *same* `mcp-session-id`, from different TCP connections.
Together: whenever two claude.ai tool calls overlap in time on one connector — either from parallel tool use in one chat or from two chats of the same user — duplicate `id: 1` requests are concurrently in flight on one session.
### Consequence (observed in production)
Both current MCP SDK server implementations route responses by request id with no duplicate guard, so the collision silently mis-delivers data (details + reproducer):
- python-sdk: https://github.com/modelcontextprotocol/python-sdk/issues/3060 (reproduced: 12 concurrent `id: 1` calls → 1 response swapped to the wrong request, 11 hung; unique-id control 12/12 clean)
- typescript-sdk: https://github.com/modelcontextprotocol/typescript-sdk/issues/2433 (same unguarded pattern on `main`)
In our production deployment (FastMCP streamable-HTTP connector, OAuth custom connector), a claude.ai conversation received the **entire response envelope of a different conversation's request** (16 results for a 1-item request), and a controlled sentinel experiment run *by a claude.ai agent itself* measured ~1/18 swap rate at 18-way concurrency. From the user's perspective, one chat saw another chat's email-search results — they initially reported it as a cross-user data leak (it wasn't: the session is per-user, but that's the class of report this generates).
Servers can mitigate (we switched to stateless mode), but the client is violating the protocol, and every stateful MCP server with slow tools is exposed.
### Asks
1. Use unique JSON-RPC request ids within a session (monotonic counter per session satisfies the spec).
2. Reconsider pooling one mcp-session-id across different conversations — even with unique ids, it couples unrelated conversations' failure domains (e.g., one conversation's hung stream affecting another's session state) and makes server-side per-conversation attribution impossible.
### Environment
- claude.ai web, custom connector (remote MCP, streamable HTTP, OAuth 2.1 DCR)
- Server: python mcp SDK 1.27.0 (FastMCP), stateful mode at the time of observation
- Evidence available: server-side per-call logs of `mcp-session-id` + JSON-RPC id per request, and the reproducer in the python-sdk issue
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Evaluación
Este issue todavía no se ha evaluado.