@@ -53,6 +53,11 @@ public class McpServerSession implements McpSession {
5353
5454 private final AtomicInteger state = new AtomicInteger (STATE_UNINITIALIZED );
5555
56+ /**
57+ * keyed by request ID, value is true if the request is being cancelled.
58+ */
59+ private final Map <Object , Boolean > requestCancellation = new ConcurrentHashMap <>();
60+
5661 /**
5762 * Creates a new server session with the given parameters and the transport to use.
5863 * @param id session id
@@ -165,13 +170,18 @@ public Mono<Void> handle(McpSchema.JSONRPCMessage message) {
165170 }
166171 else if (message instanceof McpSchema .JSONRPCRequest request ) {
167172 logger .debug ("Received request: {}" , request );
173+ requestCancellation .put (request .id (), false );
168174 return handleIncomingRequest (request ).onErrorResume (error -> {
169175 var errorResponse = new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), null ,
170176 new McpSchema .JSONRPCResponse .JSONRPCError (McpSchema .ErrorCodes .INTERNAL_ERROR ,
171177 error .getMessage (), null ));
172178 // TODO: Should the error go to SSE or back as POST return?
173- return this .transport .sendMessage (errorResponse ).then (Mono .empty ());
174- }).flatMap (this .transport ::sendMessage );
179+ return this .transport .sendMessage (errorResponse )
180+ .doFinally (signal -> requestCancellation .remove (request .id ()))
181+ .then (Mono .empty ());
182+ })
183+ .flatMap (response -> this .transport .sendMessage (response )
184+ .doFinally (signal -> requestCancellation .remove (request .id ())));
175185 }
176186 else if (message instanceof McpSchema .JSONRPCNotification notification ) {
177187 // TODO handle errors for communication to without initialization
@@ -207,6 +217,11 @@ private Mono<McpSchema.JSONRPCResponse> handleIncomingRequest(McpSchema.JSONRPCR
207217 resultMono = this .initRequestHandler .handle (initializeRequest );
208218 }
209219 else {
220+ // cancellation request
221+ if (requestCancellation .get (request .id ())) {
222+ requestCancellation .remove (request .id ());
223+ return Mono .empty ();
224+ }
210225 // TODO handle errors for communication to this session without
211226 // initialization happening first
212227 var handler = this .requestHandlers .get (request .method ());
@@ -217,14 +232,32 @@ private Mono<McpSchema.JSONRPCResponse> handleIncomingRequest(McpSchema.JSONRPCR
217232 error .message (), error .data ())));
218233 }
219234
220- resultMono = this .exchangeSink .asMono ().flatMap (exchange -> handler .handle (exchange , request .params ()));
235+ resultMono = this .exchangeSink .asMono ()
236+ .flatMap (exchange -> handler .handle (exchange , request .params ()).flatMap (result -> {
237+ if (requestCancellation .get (request .id ())) {
238+ requestCancellation .remove (request .id ());
239+ return Mono .empty ();
240+ }
241+ else {
242+ return Mono .just (result );
243+ }
244+ }).doOnCancel (() -> requestCancellation .remove (request .id ())));
245+
221246 }
222247 return resultMono
223248 .map (result -> new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), result , null ))
224- .onErrorResume (error -> Mono .just (new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (),
225- null , new McpSchema .JSONRPCResponse .JSONRPCError (McpSchema .ErrorCodes .INTERNAL_ERROR ,
226- error .getMessage (), null )))); // TODO: add error message
227- // through the data field
249+ .onErrorResume (error -> {
250+ if (requestCancellation .get (request .id ())) {
251+ requestCancellation .remove (request .id ());
252+ return Mono .empty ();
253+ }
254+ else {
255+ return Mono .just (new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), null ,
256+ new McpSchema .JSONRPCResponse .JSONRPCError (McpSchema .ErrorCodes .INTERNAL_ERROR ,
257+ error .getMessage (), null )));
258+ }
259+ }); // TODO: add error message
260+ // through the data field
228261 });
229262 }
230263
@@ -240,6 +273,17 @@ private Mono<Void> handleIncomingNotification(McpSchema.JSONRPCNotification noti
240273 exchangeSink .tryEmitValue (new McpAsyncServerExchange (this , clientCapabilities .get (), clientInfo .get ()));
241274 return this .initNotificationHandler .handle ();
242275 }
276+ else if (McpSchema .METHOD_NOTIFICATION_CANCELLED .equals (notification .method ())) {
277+ McpSchema .CancellationMessageNotification cancellationMessageNotification = transport
278+ .unmarshalFrom (notification .params (), new TypeReference <>() {
279+ });
280+ if (requestCancellation .containsKey (cancellationMessageNotification .requestId ())) {
281+ logger .warn ("Received cancellation notification for request {}, cancellation reason is {}" ,
282+ cancellationMessageNotification .requestId (), cancellationMessageNotification .reason ());
283+ requestCancellation .put (cancellationMessageNotification .requestId (), true );
284+ }
285+ return Mono .empty ();
286+ }
243287
244288 var handler = notificationHandlers .get (notification .method ());
245289 if (handler == null ) {
0 commit comments