claude-code - 💡(How to fix) Fix [BUG] v2.1.147 regression: HTTP MCP servers without optional GET listening stream - 404 misinterpreted as session expiry, transport torn down

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

Side-by-side --debug log diff. Both versions issue the GET listening-stream open. Both receive 404 from the same gateway. The diff is the reaction.

v2.1.146 (working):

[DEBUG] MCP server "my-http-mcp": Successfully connected (transport: http) in 883ms [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found [DEBUG] MCP server "my-http-mcp": Channel notifications skipped: server did not declare claude/channel capability ... 15 seconds pass (user types prompt) ... [DEBUG] MCP server "my-http-mcp": Calling MCP tool: jira_search_issues [DEBUG] MCP server "my-http-mcp": Tool 'jira_search_issues' completed successfully in 538ms v2.1.147 (broken) — lines added in v2.1.147 marked with +:

[DEBUG] MCP server "my-http-mcp": Successfully connected (transport: http) in 327ms [DEBUG] MCP server "my-http-mcp": Channel notifications skipped: server did not declare claude/channel capability [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found

  • [DEBUG] MCP server "my-http-mcp": MCP session expired (server returned 404), triggering reconnection
  • [DEBUG] MCP server "my-http-mcp": Closing transport (session expired)
  • [DEBUG] MCP server "my-http-mcp": HTTP transport closed/disconnected, attempting automatic reconnection
  • [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
  • ... reconnect loop, more 404s ...
  • [ERROR] MCP server "my-http-mcp" Failed to fetch tools: Not connected
  • [DEBUG] MCP server "my-http-mcp": Terminal connection error 1/3 The new MCP session expired (server returned 404), triggering reconnection branch is the regression. The 404 here is the response to the GET listening-stream open, which carries no Mcp-Session-Id and is not a session-expiry signal.

Root Cause

In Claude Code v2.1.147+, the server appears in claude mcp list as ✓ Connected (because mcp list only does the initialize probe), but tools are absent from interactive sessions and --debug shows the reconnect loop above.

Code Example

Side-by-side --debug log diff. Both versions issue the GET listening-stream open. Both receive 404 from the same gateway. The diff is the reaction.

v2.1.146 (working):

[DEBUG] MCP server "my-http-mcp": Successfully connected (transport: http) in 883ms
[DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
[DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
[DEBUG] MCP server "my-http-mcp": Channel notifications skipped: server did not declare claude/channel capability
... 15 seconds pass (user types prompt) ...
[DEBUG] MCP server "my-http-mcp": Calling MCP tool: jira_search_issues
[DEBUG] MCP server "my-http-mcp": Tool 'jira_search_issues' completed successfully in 538ms
v2.1.147 (broken) — lines added in v2.1.147 marked with +:

  [DEBUG] MCP server "my-http-mcp": Successfully connected (transport: http) in 327ms
  [DEBUG] MCP server "my-http-mcp": Channel notifications skipped: server did not declare claude/channel capability
  [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
+ [DEBUG] MCP server "my-http-mcp": MCP session expired (server returned 404), triggering reconnection
+ [DEBUG] MCP server "my-http-mcp": Closing transport (session expired)
+ [DEBUG] MCP server "my-http-mcp": HTTP transport closed/disconnected, attempting automatic reconnection
+ [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
+ ... reconnect loop, more 404s ...
+ [ERROR] MCP server "my-http-mcp" Failed to fetch tools: Not connected
+ [DEBUG] MCP server "my-http-mcp": Terminal connection error 1/3
The new MCP session expired (server returned 404), triggering reconnection branch is the regression. The 404 here is the response to the GET listening-stream open, which carries no Mcp-Session-Id and is not a session-expiry signal.

---

┌────────────────────────────────────────┬─────────────────────────────────────────────────────────────────┐
RequestResponse├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
POST /mcp (initialize)200, returns protocolVersion: 2025-03-26, issues Mcp-Session-Id├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
POST /mcp (tools/list with session id)200, returns tools                                              │
├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
GET /mcp (open listening stream)404├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
OPTIONS /mcp                           │ 200, Allow: POST└────────────────────────────────────────┴─────────────────────────────────────────────────────────────────┘

---

┌─────────┬────────────────┬───────────────────────┐
VersionReleased (UTC)Result├─────────┼────────────────┼───────────────────────┤
2.1.1422026-05-14     │ ✅ works              │
├─────────┼────────────────┼───────────────────────┤
2.1.1452026-05-19     │ ✅ works              │
├─────────┼────────────────┼───────────────────────┤
2.1.1462026-05-20     │ ✅ last known good    │
├─────────┼────────────────┼───────────────────────┤
2.1.1472026-05-21     │ ❌ first broken       │
├─────────┼────────────────┼───────────────────────┤
2.1.1482026-05-22     │ ❌ broken (inferred)├─────────┼────────────────┼───────────────────────┤
2.1.1492026-05-22     │ ❌ broken (confirmed)├─────────┼────────────────┼───────────────────────┤
2.1.1502026-05-23     │ ❌ broken (confirmed)└─────────┴────────────────┴───────────────────────┘
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

Starting in v2.1.147, Claude Code attempts to open the optional GET listening stream against an HTTP MCP server's URL. When the server returns 404 (a legitimate response per the MCP Streamable HTTP spec — the GET stream is optional), Claude Code now misinterprets it as MCP session expired, tears down the transport, retries in a reconnect loop, and ultimately fails tools/list with Not connected. The user sees the server silently absent from their session.

The same gateway works on v2.1.146 and earlier.

This breaks every HTTP MCP server that implements only the POST half of Streamable HTTP — which is allowed by spec.

This appears to be a side-effect of the auto-reconnect logic added in response to enhancement #59442 (closed) — the same logic that #62008 reports is also not working on its happy path. We landed on the over-broad trigger end of the same bug.

What Should Happen?

Per MCP Streamable HTTP transport spec §2 (server-to-client SSE) (https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):

▎ Clients MAY issue an HTTP GET to the MCP endpoint. This can be used to open an SSE stream, allowing the server to communicate to the client, without the client first sending data via HTTP POST. […] ▎ ▎ If the server does not offer an SSE stream at this endpoint, the server MUST return HTTP 405 Method Not Allowed.

So:

  • Clients MAY open a GET listening stream — it is optional, not required for correct operation.
  • The session-expiry mechanism in the spec (§session management clause 4 (https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management)) applies to POST requests carrying an Mcp-Session-Id that the server has invalidated. A 404 to the initial GET stream-open attempt is not a session-expiry signal — the session id has nothing to do with it.

Expected client behavior:

  • Mcp-Session-Id-carrying POST receiving 404 → legitimate session expiry → reconnect. (This is the fix #59442 asked for, and what #62008 reports still doesn't work.)
  • Initial GET stream-open receiving 404 or 405 → "server doesn't offer the optional listening stream" → log and continue. The POST request channel remains valid and tool calls should proceed normally.

The forgiving behavior in v2.1.146 and earlier was already correct on the GET-stream-open side. The v2.1.147 change appears to have widened the session-expiry trigger to catch any 404 anywhere on the transport, which is too broad.

Error Messages/Logs

Side-by-side --debug log diff. Both versions issue the GET listening-stream open. Both receive 404 from the same gateway. The diff is the reaction.

v2.1.146 (working):

[DEBUG] MCP server "my-http-mcp": Successfully connected (transport: http) in 883ms
[DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
[DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
[DEBUG] MCP server "my-http-mcp": Channel notifications skipped: server did not declare claude/channel capability
... 15 seconds pass (user types prompt) ...
[DEBUG] MCP server "my-http-mcp": Calling MCP tool: jira_search_issues
[DEBUG] MCP server "my-http-mcp": Tool 'jira_search_issues' completed successfully in 538ms
v2.1.147 (broken) — lines added in v2.1.147 marked with +:

  [DEBUG] MCP server "my-http-mcp": Successfully connected (transport: http) in 327ms
  [DEBUG] MCP server "my-http-mcp": Channel notifications skipped: server did not declare claude/channel capability
  [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
+ [DEBUG] MCP server "my-http-mcp": MCP session expired (server returned 404), triggering reconnection
+ [DEBUG] MCP server "my-http-mcp": Closing transport (session expired)
+ [DEBUG] MCP server "my-http-mcp": HTTP transport closed/disconnected, attempting automatic reconnection
+ [DEBUG] MCP server "my-http-mcp": Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
+ ... reconnect loop, more 404s ...
+ [ERROR] MCP server "my-http-mcp" Failed to fetch tools: Not connected
+ [DEBUG] MCP server "my-http-mcp": Terminal connection error 1/3
The new MCP session expired (server returned 404), triggering reconnection branch is the regression. The 404 here is the response to the GET listening-stream open, which carries no Mcp-Session-Id and is not a session-expiry signal.

Steps to Reproduce

Minimal ~/.claude.json:

{ "mcpServers": { "my-http-mcp": { "type": "http", "url": "https://<your-host>/mcp", "headers": { "Authorization": "Bearer <token>" } } } } Server behavior (spec-compliant, POST-only — does not offer the optional GET listening stream):

┌────────────────────────────────────────┬─────────────────────────────────────────────────────────────────┐
│                Request                 │                            Response                             │
├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ POST /mcp (initialize)                 │ 200, returns protocolVersion: 2025-03-26, issues Mcp-Session-Id │
├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ POST /mcp (tools/list with session id) │ 200, returns tools                                              │
├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ GET /mcp (open listening stream)       │ 404                                                             │
├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ OPTIONS /mcp                           │ 200, Allow: POST                                                │
└────────────────────────────────────────┴─────────────────────────────────────────────────────────────────┘

Direct curl against the gateway works perfectly (initialize + notifications/initialized + tools/list all succeed).

In Claude Code v2.1.147+, the server appears in claude mcp list as ✓ Connected (because mcp list only does the initialize probe), but tools are absent from interactive sessions and --debug shows the reconnect loop above.

  1. Set up the config above pointing at any POST-only HTTP MCP server.
  2. Run claude mcp list — observe ✓ Connected (misleading).
  3. Open an interactive session: claude --debug.
  4. Ask Claude to call any tool from the server.
  5. Observe: tools are not surfaced; debug log shows MCP session expired → reconnect loop → Failed to fetch tools: Not connected.

Performed by extracting platform binaries from the published npm tarballs (@anthropic-ai/claude-[email protected]) and running each side-by-side against the same MCP gateway with the same ~/.claude.json config:

┌─────────┬────────────────┬───────────────────────┐
│ Version │ Released (UTC) │        Result         │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.142 │ 2026-05-14     │ ✅ works              │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.145 │ 2026-05-19     │ ✅ works              │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.146 │ 2026-05-20     │ ✅ last known good    │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.147 │ 2026-05-21     │ ❌ first broken       │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.148 │ 2026-05-22     │ ❌ broken (inferred)  │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.149 │ 2026-05-22     │ ❌ broken (confirmed) │
├─────────┼────────────────┼───────────────────────┤
│ 2.1.150 │ 2026-05-23     │ ❌ broken (confirmed) │
└─────────┴────────────────┴───────────────────────┘

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.146

Claude Code Version

2.1.150

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING