StatelessHTTPServerTransport: notifications/cancelled leaves the original POST hanging (HTTP exchange never completed)
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 64/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- swift
- Domain
- api, backend-api-design, networking
Research direction
Start in StatelessHTTPServerTransport.swift at handleJSONRPCRequest around lines 230-233, then trace how responseWaiters is resumed in send(_:) and cleared by terminate(). Follow the notifications/cancelled path and determine how the matching waiter should be completed without violating cancellation semantics. Done means the original cancelled POST no longer hangs indefinitely, with the related waiter behavior covered by transport tests.
Written by the indexing model from the issue text.
Description
Summary
In StatelessHTTPServerTransport (0.12.1), a request that is cancelled via notifications/cancelled leaves its original HTTP POST hanging indefinitely.
The cancellation flow correctly suppresses the JSON-RPC response (per base-protocol cancellation semantics, the server must not reply to a cancelled request). But the transport's HTTP waiter for that id is only ever resumed by two paths:
- a matching JSON-RPC response arriving through
send(_:)(responseWaiters.removeValue(forKey: id)+ resume), or - full transport
terminate()(responseWaiters.removeAll()after failing them).
Since a cancelled request produces no response and nothing else resumes the waiter, the withCheckedThrowingContinuation in handleJSONRPCRequest (…/StatelessHTTPServerTransport.swift:230-233) never returns, and the original POST's HTTP exchange never completes.
Spec conflict
MCP 2025-11-25, Streamable HTTP, Sending Messages to the Server #5:
"If the input is a JSON-RPC request, the server MUST either return
Content-Type: text/event-stream, to initiate an SSE stream, orContent-Type: application/json, to return one JSON object."
And cancellation is the spec's own recommended flow (same section, #6):
"To cancel, the client SHOULD explicitly send an MCP
CancelledNotification."
So a spec-conformant client that cancels an in-flight request drives the server into violating the MUST above — the POST receives neither an SSE stream nor a JSON object, ever.
Reproduction sketch
- Start a server on
StatelessHTTPServerTransportwith a slow tool handler. POSTatools/callrequest with id X; while it is in flight,POSTanotifications/cancelledfor id X (202 as expected).- Observe: the first POST never completes.
Suggested direction
- When processing
notifications/cancelled, resume the matchingresponseWaitersentry so the HTTP exchange completes (e.g. with a JSON-RPC error object such as "request cancelled", keeping the HTTP layer satisfied while the JSON-RPC layer stays silent-by-design is admittedly awkward — an error response for the cancelled id seems the pragmatic choice given the transport MUST above). - Independently, a waiter deadline would bound any orphaned exchange.
Related: filed separately, concurrent requests sharing a JSON-RPC id also orphan waiters via map overwrite.
Found during an MCP 2025-11-25 conformance audit of an app embedding this SDK. Happy to provide more detail.
- Dominant language
- Swift
- Stars
- 1.5k
- Forks
- 243
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/swift-sdk
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
enhancement
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
All issues in modelcontextprotocol/swift-sdk
Similar issues
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
type: docs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
googleapis/google-cloud-swift#971 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
bitcoindevkit/bdk-ffi#1125 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
mozilla-mobile/firefox-ios#35743 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
manaflow-ai/cmux#13417 ·