[BUG] HTTP MCP client drops Mcp-Session-Id header — tools/list fails on session-aware servers (mcp-grafana, mcp-go) despite ✓ Connected
Summary
When registering an HTTP-transport MCP server that uses the MCP Streamable HTTP session model (server assigns Mcp-Session-Id on the initialize response and expects clients to echo it on subsequent requests), Claude Code's MCP HTTP client does not appear to propagate this header. The result:
claude mcp listreports✓ Connected— because the initial handshake succeeds- Tools never surface in any Claude Code session — because
tools/listis rejected server-side withInvalid session ID
I confirmed via raw curl against the same server, same network path, same auth, that the full tool catalog returns the moment Mcp-Session-Id is included on tools/list. So this is not a server / network / auth issue — it's missing session-header propagation in the client.
Expected behavior
Per MCP Streamable HTTP — session management spec:
"If the server returns a Mcp-Session-Id header in the initialize response, the client must include this header in all subsequent HTTP requests."
Claude Code's HTTP MCP client should:
- After receiving the
initializeresponse, scan response headers forMcp-Session-Id - Persist that value in the per-server session state
- Include
Mcp-Session-Id: <uuid>on every subsequent JSON-RPC request to that server (tools/list,tools/call, notifications)
Actual behavior
claude mcp add --transport http <name> <url> -s user→ succeedsclaude mcp list→<name>: <url> (HTTP) - ✓ Connected- Inside any Claude Code session, the server's tools never appear (ToolSearch with
<name>keyword returns nothing) - Restarting Claude Code (full quit + new session) does not help
- Server logs / direct curl confirm
tools/listrequests arrive withoutMcp-Session-Id, so the server rejects them withInvalid session ID
Reproducer (3 minimal curls — works against any mcp-go HTTP server with session management, including mcp-grafana)
Step 1. initialize — note mcp-session-id in response headers:
curl -i -X POST https://<your-mcp-server>/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.0.1"}},"id":1}'
Response:
HTTP/2 200
content-type: application/json
mcp-session-id: mcp-session-b773c0a7-a7ba-4b15-b403-44d603c2b1da ← server assigns this
...
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"mcp-grafana","version":"(devel)"},...}}
Step 2. tools/list WITHOUT session header — fails:
curl -X POST https://<your-mcp-server>/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","method":"tools/list","id":2}'
Response:
Invalid session ID
This is what Claude Code is doing (deduced from observation: server is healthy and tools never reach the session).
Step 3. tools/list WITH the session header from step 1 — succeeds:
curl -X POST https://<your-mcp-server>/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "Mcp-Session-Id: mcp-session-b773c0a7-a7ba-4b15-b403-44d603c2b1da" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":2}'
Response:
{"jsonrpc":"2.0","id":2,"result":{"tools":[
{"name":"add_activity_to_incident",...},
{"name":"alerting_manage_routing",...},
{"name":"alerting_manage_rules",...},
... 58 tools total
]}}
Environment
- Claude Code:
2.1.176 - OS: macOS Darwin 24.2.0 arm64
- Transport:
--transport http - Server tested:
mcp-grafana(Go, devel build), self-reportingprotocolVersion: "2024-11-05", exposing 58 tools - Network path is behind Cloudflare Access + WARP Zero Trust, but I've verified end-to-end via raw curl that
Mcp-Session-Idpropagates cleanly through that stack (same path the working step-3 curl above goes through)
Impact
Any MCP server that implements MCP 2024-11-05 / 2025-03-26 Streamable HTTP session management is currently unusable in Claude Code, despite reporting ✓ Connected. That includes (at minimum):
mcp-grafana— 58 tools blocked (Grafana / Prometheus / Loki / dashboards / alerting / OnCall / Pyroscope / annotations / API)- Likely any other
mcp-go-based HTTP server that opts into session management
Workaround
Direct service API via Bash + curl + service-account token. Lost the MCP tool integration but PromQL / dashboard reads still possible. Not a substitute for stateful operations.
Suggested fix area
The Claude Code HTTP MCP client needs to track and inject Mcp-Session-Id per connected server. This appears adjacent to but distinct from #62008 (recovery from 404 session-not-found): #62008 implies the client does send a session id (otherwise it wouldn't get 404 — server would return Invalid session ID), whereas in this report the client appears to never send the header at all on the first request after initialize.
I'm happy to capture claude --debug logs for a fresh handshake against mcp-grafana on request — that should confirm exactly what's serialized on the wire.
References
- MCP Streamable HTTP transport — session management — clause 4 of session management
- #62008 — related: client doesn't re-initialize after HTTP 404 session-not-found (a different facet — session id is sent but stale; here, session id appears never sent)
- #67194 — related: stateless server's GET 405 mis-correlated to tools/list (different transport variant; same general "tools/list never reaches user" symptom from a different cause)
mcp-grafana: https://github.com/grafana/mcp-grafanamcp-go(HTTP transport with session management — likely upstream for many affected servers): https://github.com/mark3labs/mcp-go
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗