3838import io .modelcontextprotocol .spec .McpSchema ;
3939import io .modelcontextprotocol .spec .McpTransportException ;
4040import io .modelcontextprotocol .spec .McpTransportSession ;
41+ import io .modelcontextprotocol .spec .McpTransportSessionClosedException ;
4142import io .modelcontextprotocol .spec .McpTransportSessionNotFoundException ;
4243import io .modelcontextprotocol .spec .McpTransportStream ;
4344import io .modelcontextprotocol .spec .ProtocolVersions ;
@@ -189,14 +190,6 @@ private McpTransportSession<Disposable> createTransportSession() {
189190 return new DefaultMcpTransportSession (onClose );
190191 }
191192
192- private McpTransportSession <Disposable > createClosedSession (McpTransportSession <Disposable > existingSession ) {
193- var existingSessionId = Optional .ofNullable (existingSession )
194- .filter (session -> !(session instanceof ClosedMcpTransportSession <Disposable >))
195- .flatMap (McpTransportSession ::sessionId )
196- .orElse (null );
197- return new ClosedMcpTransportSession <>(existingSessionId );
198- }
199-
200193 private Publisher <Void > createDelete (String sessionId ) {
201194
202195 var uri = Utils .resolveUri (this .baseUri , this .endpoint );
@@ -240,7 +233,8 @@ private void handleException(Throwable t) {
240233 public Mono <Void > closeGracefully () {
241234 return Mono .defer (() -> {
242235 logger .debug ("Graceful close triggered" );
243- McpTransportSession <Disposable > currentSession = this .activeSession .getAndUpdate (this ::createClosedSession );
236+ McpTransportSession <Disposable > currentSession = this .activeSession
237+ .getAndSet (ClosedMcpTransportSession .INSTANCE );
244238 if (currentSession != null ) {
245239 return Mono .from (currentSession .closeGracefully ());
246240 }
@@ -250,6 +244,19 @@ public Mono<Void> closeGracefully() {
250244
251245 private Mono <Disposable > reconnect (McpTransportStream <Disposable > stream ) {
252246 return Mono .deferContextual (ctx -> {
247+ var rh = this .handler .get ();
248+ if (rh == null ) {
249+ logger .warn ("Transport has no request handler registered. Remember to call connect!" );
250+ }
251+
252+ final Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> requestHandler = rh != null
253+ ? rh : msg -> Mono .error (new IllegalStateException ("No request handler" ));
254+
255+ final McpTransportSession <Disposable > transportSession = this .activeSession .get ();
256+
257+ if (ClosedMcpTransportSession .INSTANCE .equals (transportSession )) {
258+ throw new McpTransportSessionClosedException ();
259+ }
253260
254261 if (stream != null ) {
255262 logger .debug ("Reconnecting stream {} with lastId {}" , stream .streamId (), stream .lastId ());
@@ -259,7 +266,7 @@ private Mono<Disposable> reconnect(McpTransportStream<Disposable> stream) {
259266 }
260267
261268 final AtomicReference <Disposable > disposableRef = new AtomicReference <>();
262- final McpTransportSession < Disposable > transportSession = this . activeSession . get ();
269+
263270 var uri = Utils .resolveUri (this .baseUri , this .endpoint );
264271
265272 Disposable connection = Mono .deferContextual (connectionCtx -> {
@@ -389,18 +396,18 @@ else if (statusCode == BAD_REQUEST) {
389396 "Received unrecognized SSE event type: " + sseResponseEvent .sseEvent ().event ()));
390397 })
391398 .retryWhen (authorizationErrorRetrySpec ())
392- .flatMap (jsonrpcMessage -> this . handler . get () .apply (Mono .just (jsonrpcMessage )))
399+ .flatMap (jsonrpcMessage -> requestHandler .apply (Mono .just (jsonrpcMessage )))
393400 .onErrorMap (CompletionException .class , t -> t .getCause ())
394- .onErrorComplete (t -> {
395- this .handleException (t );
396- return true ;
397- })
398401 .doFinally (s -> {
399402 Disposable ref = disposableRef .getAndSet (null );
400403 if (ref != null ) {
401404 transportSession .removeConnection (ref );
402405 }
403406 }))
407+ .onErrorComplete (t -> {
408+ this .handleException (t );
409+ return true ;
410+ })
404411 .contextWrite (ctx )
405412 .subscribe ();
406413
@@ -467,10 +474,23 @@ public String toString(McpSchema.JSONRPCMessage message) {
467474
468475 public Mono <Void > sendMessage (McpSchema .JSONRPCMessage sentMessage ) {
469476 return Mono .create (deliveredSink -> {
477+ var rh = this .handler .get ();
478+ if (rh == null ) {
479+ logger .warn ("Transport has no request handler registered. Remember to call connect!" );
480+ }
481+
482+ final Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> requestHandler = rh != null
483+ ? rh : msg -> Mono .error (new IllegalStateException ("No request handler" ));
484+
485+ var transportSession = this .activeSession .get ();
486+
487+ if (ClosedMcpTransportSession .INSTANCE .equals (transportSession )) {
488+ throw new McpTransportSessionClosedException ();
489+ }
490+
470491 logger .debug ("Sending message {}" , sentMessage );
471492
472493 final AtomicReference <Disposable > disposableRef = new AtomicReference <>();
473- final McpTransportSession <Disposable > transportSession = this .activeSession .get ();
474494
475495 var uri = Utils .resolveUri (this .baseUri , this .endpoint );
476496 String jsonBody = this .toString (sentMessage );
@@ -643,22 +663,26 @@ else if (statusCode == BAD_REQUEST) {
643663 new RuntimeException ("Failed to send message: " + responseEvent ));
644664 })
645665 .retryWhen (authorizationErrorRetrySpec ())
646- .flatMap (jsonRpcMessage -> this . handler . get () .apply (Mono .just (jsonRpcMessage )))
666+ .flatMap (jsonRpcMessage -> requestHandler .apply (Mono .just (jsonRpcMessage )))
647667 .onErrorMap (CompletionException .class , t -> t .getCause ())
648- .onErrorComplete (t -> {
649- // handle the error first
650- this .handleException (t );
651- // inform the caller of sendMessage
652- deliveredSink .error (t );
653- return true ;
654- })
655668 .doFinally (s -> {
656669 logger .debug ("SendMessage finally: {}" , s );
657670 Disposable ref = disposableRef .getAndSet (null );
658671 if (ref != null ) {
659672 transportSession .removeConnection (ref );
660673 }
661- })).contextWrite (deliveredSink .contextView ()).subscribe ();
674+ })).onErrorComplete (t -> {
675+ // handle the error first
676+ try {
677+ this .handleException (t );
678+ }
679+ catch (Exception e ) {
680+ logger .error ("Error handling exception {}" , t .getMessage (), e );
681+ }
682+ // inform the caller of sendMessage
683+ deliveredSink .error (t );
684+ return true ;
685+ }).contextWrite (deliveredSink .contextView ()).subscribe ();
662686
663687 disposableRef .set (connection );
664688 transportSession .addConnection (connection );
0 commit comments