fix(StatelessHTTPServerTransport): complete a cancelled request's HTTP exchange (#255) - #268
Open
jpurnell wants to merge 1 commit into
Open
Conversation
…exchange A request cancelled via notifications/cancelled left its original HTTP POST hanging indefinitely. The transport's response waiter is only ever resumed by a matching JSON-RPC response or by terminate(); a cancelled request correctly produces no response (the server must stay silent), so nothing resumed the waiter and the withCheckedThrowingContinuation in handleJSONRPCRequest never returned. This also violates the Streamable HTTP requirement that a POST carrying a request MUST receive either an SSE stream or a JSON object. When a CancelledNotification for an in-flight request arrives, complete that request's HTTP exchange with a JSON-RPC error (implementation-defined server-error code -32002, echoing the request id) so the POST returns a JSON object. The notification is still forwarded to the server so it can cancel the underlying work. Adds a regression test: the cancelled request's POST completes with a JSON-RPC error for its id (not a hang), the cancellation returns 202, and both messages still reach the server. Fixes modelcontextprotocol#255
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A request cancelled via
notifications/cancelledleaves its original HTTP POST hangingindefinitely on
StatelessHTTPServerTransport.The transport's response waiter (registered in
handleJSONRPCRequest) is only ever resumedby a matching JSON-RPC response through
send(_:)or by fullterminate(). Perbase-protocol cancellation semantics the server correctly sends no response for a
cancelled request, so nothing resumes the waiter and the
withCheckedThrowingContinuationnever returns.
This also violates the Streamable HTTP requirement (2025-11-25, Sending Messages to the
Server #5): a POST carrying a JSON-RPC request MUST receive either an SSE stream or a
JSON object. A spec-conformant client that cancels (same section, #6: "To cancel, the client
SHOULD explicitly send an MCP
CancelledNotification") drives the server into violating thatMUST.
Fix
When a
CancelledNotificationfor an in-flight request arrives, complete that request's HTTPexchange with a JSON-RPC error — implementation-defined server-error code
-32002,echoing the request id so the client can correlate — so the POST returns a JSON object
instead of hanging. The notification is still forwarded to the server so it can cancel the
underlying work. No-op when the cancellation references an unknown/absent request id.
Tests
Adds
testCancelledRequestCompletesHTTPExchange:its id,
Full MCP suite green (552 tests).
Fixes #255