You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Terminate cancelled requests in-band on the legacy HTTP wire
Review of the first revision showed that dropping the cancellation
frame outright fought the 2025-era streamable HTTP wire, whose per-request
SSE stream is the ordered, resumable channel for everything about that
request and ends only when a response for its id passes through it. The
out-of-band substitutes each broke something: the transport hook closed
the stream from the side and could overtake messages already queued
through the router (notably the courtesy cancel for a handler's nested
elicitation), had no durable form so a resuming client's replay tailed
forever, and needed two dispatcher call sites; the client aborting its
own POST severed the delivery path for that owed related traffic and
turned routine cancellation into a write against a closed socket.
The 2026-07-28 MUST NOT that motivates this change never coexists with
resumable per-request streams (SEP-2575 removed them), so the fix is
scoped by era instead. The dispatcher stays strictly silent for a
cancelled request everywhere. The one 2025-era transport terminates the
settled request through the same ordered channel with a valid -32800
REQUEST_CANCELLED error (LSP's RequestCancelled) in place of the old
code 0 - so ordering, resumption, and the client's channel are all
preserved by construction, and old and new clients complete the same way
they did before. The client transport is unchanged from main; the
dispatcher settles a cancelled request at a single site, containing a
raising transport hook like its other callback boundaries.
Copy file name to clipboardExpand all lines: docs/migration.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1390,11 +1390,11 @@ Previously it also re-raised exceptions yielded by the transport onto the read s
1390
1390
1391
1391
### Cancelled requests are no longer answered
1392
1392
1393
-
In v1, when the peer sent `notifications/cancelled` for an in-flight request, the receiving side interrupted the handler and answered the request anyway with a JSON-RPC error, `{"code": 0, "message": "Request cancelled"}`. The 2026-07-28 spec says a server MUST NOT send any further messages for a cancelled request, and the sender is expected to stop waiting once it cancels, so that error response has been removed: a cancelled request now produces no response at all - no result, and no error, even if the handler runs to completion or fails afterwards. This applies to both seats (the server for cancelled client requests, and the client for cancelled server-initiated requests such as sampling or elicitation).
1393
+
In v1, when the peer sent `notifications/cancelled` for an in-flight request, the receiving side interrupted the handler and answered the request anyway with a JSON-RPC error, `{"code": 0, "message": "Request cancelled"}` - and `0` is not a defined JSON-RPC error code. The 2026-07-28 transport specifications (stdio, streamable HTTP) say a server **MUST NOT** send any further messages for a cancelled request; the older cancellation pattern already said it **SHOULD NOT**. The sender is expected to stop waiting once it cancels, so that error response has been removed: a cancelled request now produces no response at all - no result, and no error - even if the handler runs to completion or fails afterwards. This applies to both seats (the server for cancelled client requests, and the client for cancelled server-initiated requests such as sampling or elicitation).
1394
1394
1395
-
On the 2025-era streamable HTTP wire, the cancelled request's HTTP exchange still ends rather than staying open: JSON-response mode answers the POST with an empty `202 Accepted`, SSE mode ends the event stream without a response event, and the client tears down the abandoned request's own POST.
1395
+
The one deliberate exception is the 2025-era streamable HTTP transport (`StreamableHTTPServerTransport`), whose wire can end a request's stream only with a response for that id (and stores that response so a resuming client's replay terminates too). Under the 2025 rule, a SHOULD NOT, that transport now terminates a cancelled request with a valid `-32800` error (`mcp.server.streamable_http.REQUEST_CANCELLED`, mirroring LSP's `RequestCancelled`) in place of the old `0`. Nothing else answers.
1396
1396
1397
-
Nothing changes for callers of the built-in client: abandoning a call (cancelling the awaiting task, or a per-request timeout) never waited for that response. If you send `notifications/cancelled` by hand while still awaiting the call, the call no longer resolves with the code-0 error; stop awaiting it yourself, or use a per-request timeout.
1397
+
Nothing changes for callers of the built-in client: abandoning a call (cancelling the awaiting task, or a per-request timeout) never waited for that response. If you send `notifications/cancelled` by hand while still awaiting the call, the call now receives nothing on most transports (over 2025-era streamable HTTP it fails with `REQUEST_CANCELLED`); stop awaiting it yourself, or use a per-request timeout.
1398
1398
1399
1399
### `Server.run()` no longer takes a `stateless` flag
0 commit comments