fix(server): settle in-flight JSON-mode requests and release their stream mappings (v1.x) - #2747
Conversation
…ream mappings Two lifecycle bugs in JSON response mode on v1.x, tracked in modelcontextprotocol#2559: - send() resolved the pending HTTP response but never ran the mapping's cleanup, so every completed POST left one _streamMapping entry behind until close() (main got the equivalent of this in modelcontextprotocol#2286). - conversely, cleanup() only deleted the map entry without settling the Promise<Response> returned by handleRequest(), so close() during an in-flight JSON-mode POST left the HTTP request hanging until the client timed out. cleanup() now resolves it with a 503 JSON-RPC error; on the success path send() has already resolved, and re-resolving is a no-op. Fix modelcontextprotocol#2559 (v1.x half; the main-branch close-settle half is drafted in modelcontextprotocol#2584)
🦋 Changeset detectedLatest commit: 24eae34 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
|
Verified this locally against The red Generated by Claude Code |
Fixes #2559 (v1.x half — the main-branch close-settle half is drafted in #2584)
What
On v1.x, JSON response mode has two lifecycle gaps:
send()resolves the pending HTTP response but never runs the stream mapping'scleanup, so every completed POST leaves one_streamMappingentry behind untilclose(). Long-lived sessions accumulate one entry per request. (main got the equivalent fix in Implement MCP 2026-07-28 #2286; it was never carried to v1.x — as noted on the issue, this is what currently ships as 1.30.0.)cleanup()only deleted the map entry without settling thePromise<Response>returned byhandleRequest().close()runs every mapping's cleanup, but for a JSON-mode POST still waiting on its handler that cleanup settled nothing — so the HTTP request hung until the client timed out.Fix
send()now callsstream.cleanup()right after resolving, mirroring main's Implement MCP 2026-07-28 #2286.cleanup()now settles the pending POST with a503JSON-RPC error (-32000), aligned with the approach drafted for main in fix(server): settle in-flight JSON-mode requests on transport close #2584. On the success pathsend()has already resolved beforecleanup()runs, and re-resolving a settled promise is a no-op, so the new resolve only fires when nothing was ever sent.Tests
settles an in-flight JSON-mode request when the transport closes mid-handler— the tool handler signals it has started,close()lands while it is genuinely parked, and the POST must resolve503with the JSON-RPC error payload instead of hanging.releases the JSON-mode stream mapping once the response has been sent—_streamMapping.sizemust be0after a completed JSON-mode POST (on unpatched v1.x this fails with2: the request's mapping plus initialize's).Both tests fail on unpatched v1.x and pass with the fix. Full suite: 1647 passed (the two unhandled stdio
SyntaxErrors reproduce on pristine v1.x and are unrelated).