Open
Conversation
Author
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
Implements the
notifications/cancellednotification from the MCP specification, enabling bidirectional request cancellation between clients and servers. Both sides can cancel in-flight requests they previously issued, with the receiver disposing the in-progress reactive pipeline.Motivation and Context
The MCP spec defines a cancellation mechanism where either party can send a
notifications/cancellednotification to signal that a previously-issued request is no longer needed. Without this, long-running operations like tool calls have no way to be interrupted — the caller must wait for the full timeout or the operation to complete naturally.This was a missing feature in the Java SDK that is required for full spec compliance.
How Has This Been Tested?
McpClientSessioncancellation (McpClientSessionCancellationTests) — verifies outbound cancellation errors the local pending response with-32800, sends the notification over the wire, and that inbound cancellation disposes the in-progress request pipelineMcpServerSessioncancellation (McpServerSessionCancellationTests) — verifies the same behavior on the server sideMcpRequestHandle(McpRequestHandleTests) — verifies the lazy ID resolution pattern, the cancel closure, and the guard against cancelling before subscriptionLifecycleInitializerTestsandLifecycleInitializerPostInitializationHookTests— updated to work withsendRequestWithId(used for tracking the initialize request ID)McpAsyncServerExchangeTestsandMcpSyncServerExchangeTests— cover the newcancelRequestmethod on the exchangeBreaking Changes
No breaking changes for existing users. All new APIs are additive:
McpAsyncClient.cancelRequest()andcallToolWithHandle()are new public methodsMcpSyncClient.cancelRequest()andcallTool(request, timeout)are new public methodsMcpAsyncServerExchange.cancelRequest()/McpSyncServerExchange.cancelRequest()are newMcpServerbuilder's.cancellationConsumer()is optionalMcpServerFeatures.Asyncrecord has a backwards-compatible constructor withoutcancellationConsumerMcpSession.sendCancellation()is a default method on the interfaceThe only internal change is that
LifecycleInitializernow usessendRequestWithIdinstead ofsendRequestfor the initialize handshake, but this is an internal class not exposed to users.Types of changes
Checklist
Additional context
Architecture
Cancellation touches three layers, each with a distinct responsibility:
Schema layer (
McpSchema) — pure data types:CancelledNotificationrecord withrequestId,reason,_metaErrorCodes.REQUEST_CANCELLED = -32800METHOD_NOTIFICATION_CANCELLED = "notifications/cancelled"Session layer (
McpClientSession/McpServerSession/McpStreamableServerSession) — protocol mechanics:sendCancellation(id, reason): errors the localpendingResponsessink with-32800, then sendsnotifications/cancelledover the wireSubscriptionin aninProgressInboundmap usingdoOnSubscribe/doFinallylifecycle hooks. When a cancellation notification arrives, theSubscriptionis disposed, cancelling the reactive pipeline mid-flightPublic API layer (
McpAsyncClient/McpSyncClient/ Exchange classes) — user-facing:cancelRequest(requestId, reason)— cancel by ID with initialize-request protectioncallToolWithHandle()→McpRequestHandle— returns a handle withrequestId(),response(), andcancel(reason)methodsexchange.cancelRequest()for the server→client directioncancellationConsumeron the server builder for app-level side-effects (logging, metrics)Cancellation flow (client → server)
Spec compliance notes
initializerequest MUST NOT be cancelled — enforced inMcpAsyncClient.cancelRequest()by checking against the tracked initialize request IDDefaultMcpStatelessServerHandlerregisters a no-op handler to avoid "unknown notification" errors[Demo]
Screen.Recording.2026-02-19.at.9.01.59.PM.mov