[BUG] Claude Code violates MCP spec §Session Management item 4 — stale Mcp-Session-Id never reinitialized after server returns 404
Summary
Claude Code's Streamable HTTP MCP client violates a MUST-level requirement in the MCP 2025-06-18 spec: when the server returns HTTP 404 to signal a terminated session, the client must discard the session ID and reinitialize. Claude Code instead caches the stale Mcp-Session-Id forever and sends it on every subsequent request, which the server keeps 404-ing. Every MCP tool call then fails silently with session not found until the user fully restarts Claude Code.
This silently breaks every MCP server hosted on serverless infrastructure (Cloud Run, Lambda, autoscaled Kubernetes, any server with session TTL) after the first container recycle. Including, very likely, Anthropic's own hosted MCP integrations.
The spec is explicit — and it's a MUST
From the MCP 2025-06-18 spec, §Session Management:
3. The server MAY terminate the session at any time, after which it MUST respond to requests containing that session ID with HTTP 404 Not Found. 4. When a client receives HTTP 404 in response to a request containing anMcp-Session-Id, it MUST start a new session by sending a newInitializeRequestwithout a session ID attached.
Claude Code currently implements neither half of item 4. It doesn't detect the 404 as a session-termination signal, it doesn't clear the cached session ID, and it doesn't reinit. The user sees their MCP tools go dead with no recovery path except /exit and relaunch.
One-command reproduction
Against any streamable-HTTP MCP server (here using mcper, but this is architectural — any spec-compliant server will do the same):
$ curl -sS -X POST "https://mcper-9161453686.us-central1.run.app/mcp" \
-H "Authorization: Bearer $MCPER_KEY" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: DEADBEEF000000000000000000" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' -D -
HTTP/2 404
content-type: text/plain
session not found
The server behaves exactly as §Session Management item 3 requires. What Claude Code then does with this 404 is the bug.
Inside a Claude Code session, after the backing Cloud Run container cycles:
Tool: mcp__mcper__wasm_gmail_gmail_list_labels
Error: Streamable HTTP error: Error POSTing to endpoint: session not found
Every subsequent call returns the same error. claude mcp list reports "✓ Connected" — because it only probes reachability, not session validity. The connection is "up," the session is dead, and the client can't tell the difference.
Impact: not an edge case, a default behavior
Cloud Run scales to zero after ~15 min idle by default. Lambda has concurrency-based instance recycling. Cloudflare Workers have execution-time and isolate-level recycling. Any developer workflow with a lunch break, a long compile, or a focus block longer than 15 minutes hits this.
Affected today (non-exhaustive):
- mcper (Google Cloud Run)
- anthropics/claude-code#34498 — Figma Desktop MCP, same symptom
- anthropics/claude-code#40106 — every plugin MCP installed under
~/.claude/plugins/that uses HTTP transport - anthropics/claude-code#48749 — remote-control bridge
- Anthropic's own
gmail.mcp.claude.com,gcal.mcp.claude.com,drivemcp.googleapis.com— all streamable-HTTP, almost certainly session-stateful, almost certainly behind a serverless or autoscaled tier
Prior art — this bug has been reported and ignored
| Issue | Filed | State |
|---|---|---|
| #27142 | 2026-02-20 | Closed by stale bot on 2026-03-28, locked on 2026-04-05. Zero team response in 36 days. Contains the same MCP spec citation. |
| #34498 | 2026-03 | Open, unassigned |
| #40106 | 2026-10 | Open, unassigned. Auto-duplicate-flagged to #34498, #27142, #28726 |
| #48749 | 2026-04 | Open, unassigned |
Four separate reports from four different users hitting four different MCP servers, diagnosing the exact same root cause. One of them auto-closed as "stale" with no human triage.
Proposed fix
In the Streamable HTTP client's request path (pseudocode):
async function request(payload: JSONRPCMessage): Promise<JSONRPCResponse> {
let resp = await http.post(endpoint, {
headers: { ...headers, 'Mcp-Session-Id': this.sessionId },
body: JSON.stringify(payload),
});
// Spec §Session Management item 4: MUST reinit on 404
if (resp.status === 404 && this.sessionId !== undefined) {
this.sessionId = undefined;
await this.initialize(); // existing handshake code
resp = await http.post(endpoint, {
headers: { ...headers, 'Mcp-Session-Id': this.sessionId },
body: JSON.stringify(payload),
});
}
return resp;
}
Bounded to exactly one automatic reinit per failed request to avoid loops. Gate behind a feature flag for a release if needed, but the spec mandates this behavior by default.
Nice-to-haves:
info-level log:"MCP session for '{serverName}' was invalidated; reinitialized successfully"- Re-deliver any pending notifications/subscriptions (stream tools) on the new session
claude mcp listcould probe session validity (send a trivialtools/list) and surface "⚠ session stale" instead of "✓ Connected" when the cached session is dead
Why this matters beyond one stuck user
Claude Code is the de facto reference client for MCP. When it silently violates a MUST, it:
- Teaches server authors that "spec compliance isn't enough — you have to work around Claude Code specifically"
- Pushes them to build stateless MCP servers (or hacks like long session TTLs, keep-alive shims, min-instances=1) instead of using the session abstraction the spec defined
- Erodes MCP as a protocol — when the most deployed client ignores MUSTs, the spec becomes advisory, and every future MCP will have ad-hoc recovery semantics
- Breaks Anthropic's own hosted integrations in the same way, so even users who stick to Anthropic's ecosystem hit it
Repro environment
- Claude Code
2.1.110 - macOS 25.2.0, Apple Silicon
- Confirmed against mcper (Google Cloud Run) — will reproduce on any spec-compliant streamable-HTTP MCP server after session termination
Ask
Please un-stale #27142 (or close this as its successor) and scope a fix for the next minor release. It's ~10 lines in the streamable-HTTP client, mandated by the spec, and it silently fixes an entire class of broken integrations, including Anthropic's own.
Happy to submit a PR if that helps.
---
Issue drafted by Claude Carp (Claude Code agent) in collaboration with the filer. Technical claims, spec citations, and repro verified live.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗