Skip to content

fix: resolve MCP HTTP hanging on Claude Code Cloud#51

Merged
klappy merged 10 commits intomainfrom
claude/debug-mcp-http-hanging-cVkBK
Feb 19, 2026
Merged

fix: resolve MCP HTTP hanging on Claude Code Cloud#51
klappy merged 10 commits intomainfrom
claude/debug-mcp-http-hanging-cVkBK

Conversation

@klappy
Copy link
Copy Markdown
Owner

@klappy klappy commented Feb 19, 2026

Three issues caused the MCP server to hang with Claude Code Cloud:

  1. GET /mcp SSE stream never closed: The ReadableStream sent : connected
    but never called controller.close(), creating a zombie connection that
    blocked the client's connection pool indefinitely. Now returns 405
    (correct for stateless servers per MCP spec and official SDK pattern).

  2. POST /mcp always returned SSE: Because Accept includes text/event-stream,
    even initialize and tools/list were wrapped in SSE format. Now prefers
    JSON when client accepts it, only falls back to SSE when client
    exclusively requests text/event-stream.

  3. Removed invalid Connection: keep-alive header (prohibited in HTTP/2).

https://claude.ai/code/session_013qxyMP1Eci9SqmErxff6E9


Note

Medium Risk
Changes MCP HTTP response negotiation and streaming behavior; incorrect Accept handling or SSE framing could break compatibility with MCP clients, though the change is localized to the /mcp endpoint.

Overview
Fixes the Cloudflare Worker MCP endpoint to follow the SSE/JSON contract and avoid client hangs by ensuring the GET /mcp SSE ReadableStream immediately controller.close()s after sending the initial comment.

Updates content negotiation so text/event-stream in Accept takes priority (even when application/json is also present), adjusts GET /mcp without SSE Accept to return a JSON-RPC 405 error with proper headers, removes the Connection: keep-alive header, and standardizes parse-error responses to include id: null.

Bumps versions to 0.14.2 in package.json, workers/package.json, and workers/package-lock.json.

Written by Cursor Bugbot for commit b7e4dfe. This will update automatically on new commits. Configure here.

Three issues caused the MCP server to hang with Claude Code Cloud:

1. GET /mcp SSE stream never closed: The ReadableStream sent `: connected`
   but never called controller.close(), creating a zombie connection that
   blocked the client's connection pool indefinitely. Now returns 405
   (correct for stateless servers per MCP spec and official SDK pattern).

2. POST /mcp always returned SSE: Because Accept includes text/event-stream,
   even initialize and tools/list were wrapped in SSE format. Now prefers
   JSON when client accepts it, only falls back to SSE when client
   exclusively requests text/event-stream.

3. Removed invalid Connection: keep-alive header (prohibited in HTTP/2).

https://claude.ai/code/session_013qxyMP1Eci9SqmErxff6E9
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Feb 19, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
oddkit b7e4dfe Commit Preview URL

Branch Preview URL
Feb 19 2026, 01:38 PM

Comment thread workers/package-lock.json Outdated
claude and others added 2 commits February 19, 2026 05:19
The actual root cause of the hanging: the GET /mcp ReadableStream never
called controller.close(), creating a zombie connection. Fixed by adding
controller.close() after the : connected comment.

Restores SSE response format when Accept includes text/event-stream
(required by test suite and MCP spec). Removes invalid Connection:
keep-alive header (prohibited in HTTP/2).

https://claude.ai/code/session_013qxyMP1Eci9SqmErxff6E9
@cursor

This comment has been minimized.

Comment thread workers/src/index.ts
…on/json

MCP spec-compliant clients send Accept: application/json, text/event-stream.
The previous condition (wantsSSE = acceptHeader includes text/event-stream)
was always true for these clients, making the JSON response path unreachable.

Now the POST handler only uses SSE when the client exclusively requests
text/event-stream. When the client also accepts application/json, the
server prefers JSON, matching the PR description intent and preventing
hangs with Claude Code Cloud.
@cursor

This comment has been minimized.

Comment thread workers/src/index.ts Outdated
…er requirement

Previously, GET requests with Accept: text/event-stream received a 200
SSE stream that immediately closed. Per the MCP 2025-03-26 spec, servers
that do not support GET MUST return 405. The immediately-closing stream
could trigger auto-reconnection loops in SSE clients. Also fixes the
error message which previously suggested using GET with SSE, contradicting
the stateless server design.
@cursor

This comment has been minimized.

Reverts the regressions from 58a2d5f and eedb071 which broke 4 tests:

- Test 4c: GET /mcp with Accept: text/event-stream must return SSE,
  not 405. The 405 should only apply when SSE is NOT requested.
- Tests 4f/4g/4h: POST /mcp with Accept: application/json, text/event-stream
  must return SSE. The MCP spec says SSE takes priority when both are
  accepted. The `!includes("application/json")` guard was wrong.

The root cause of the original hanging bug remains fixed:
controller.close() is called in the GET SSE ReadableStream.

https://claude.ai/code/session_013qxyMP1Eci9SqmErxff6E9
Comments explain the SSE contract, reference specific test numbers
(4c, 4d, 4f, 4g, 4h), and warn against two specific anti-patterns
that have already caused regressions twice:

1. DO NOT return 405 for ALL GETs — only when Accept lacks
   text/event-stream (test 4c vs 4d)
2. DO NOT add `&& !includes("application/json")` to the SSE
   condition — MCP clients send both in Accept (tests 4f, 4g, 4h)
3. DO NOT remove controller.close() — that's the root cause of
   the original hanging bug

https://claude.ai/code/session_013qxyMP1Eci9SqmErxff6E9
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON. A Cloud Agent has been kicked off to fix the reported issue.

Comment thread workers/src/index.ts
The JSON-RPC 2.0 spec requires id: null when the request id cannot
be determined. Added the missing field to the new 405 GET handler
and the pre-existing parse error catch handler.
@cursor
Copy link
Copy Markdown

cursor Bot commented Feb 19, 2026

Bugbot Autofix prepared fixes for 1 of the 1 bugs found in the latest run.

  • ✅ Fixed: JSON-RPC error response missing required id field
    • Added id: null to both the new 405 GET handler response and the pre-existing parse error catch handler to comply with the JSON-RPC 2.0 spec requirement that id must be null when the request id cannot be determined.

@klappy klappy merged commit c54ab54 into main Feb 19, 2026
5 checks passed
@klappy klappy deleted the claude/debug-mcp-http-hanging-cVkBK branch February 19, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants