3030import io .modelcontextprotocol .json .McpJsonDefaults ;
3131import io .modelcontextprotocol .json .McpJsonMapper ;
3232import io .modelcontextprotocol .json .TypeRef ;
33- import io .modelcontextprotocol .spec .ClosedMcpTransportSession ;
34- import io .modelcontextprotocol .spec .DefaultMcpTransportSession ;
35- import io .modelcontextprotocol .spec .DefaultMcpTransportStream ;
36- import io .modelcontextprotocol .spec .HttpHeaders ;
37- import io .modelcontextprotocol .spec .McpClientTransport ;
38- import io .modelcontextprotocol .spec .McpSchema ;
39- import io .modelcontextprotocol .spec .McpTransportException ;
40- import io .modelcontextprotocol .spec .McpTransportSession ;
41- import io .modelcontextprotocol .spec .McpTransportSessionNotFoundException ;
42- import io .modelcontextprotocol .spec .McpTransportStream ;
43- import io .modelcontextprotocol .spec .ProtocolVersions ;
33+ import io .modelcontextprotocol .spec .*;
4434import io .modelcontextprotocol .util .Assert ;
4535import io .modelcontextprotocol .util .Utils ;
4636import org .reactivestreams .Publisher ;
@@ -189,14 +179,6 @@ private McpTransportSession<Disposable> createTransportSession() {
189179 return new DefaultMcpTransportSession (onClose );
190180 }
191181
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-
200182 private Publisher <Void > createDelete (String sessionId ) {
201183
202184 var uri = Utils .resolveUri (this .baseUri , this .endpoint );
@@ -240,7 +222,8 @@ private void handleException(Throwable t) {
240222 public Mono <Void > closeGracefully () {
241223 return Mono .defer (() -> {
242224 logger .debug ("Graceful close triggered" );
243- McpTransportSession <Disposable > currentSession = this .activeSession .getAndUpdate (this ::createClosedSession );
225+ McpTransportSession <Disposable > currentSession = this .activeSession
226+ .getAndSet (ClosedMcpTransportSession .INSTANCE );
244227 if (currentSession != null ) {
245228 return Mono .from (currentSession .closeGracefully ());
246229 }
@@ -250,6 +233,19 @@ public Mono<Void> closeGracefully() {
250233
251234 private Mono <Disposable > reconnect (McpTransportStream <Disposable > stream ) {
252235 return Mono .deferContextual (ctx -> {
236+ var rh = this .handler .get ();
237+ if (rh == null ) {
238+ logger .warn ("Transport has no request handler registered. Remember to call connect!" );
239+ }
240+
241+ final Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> requestHandler = rh != null
242+ ? rh : msg -> Mono .error (new IllegalStateException ("No request handler" ));
243+
244+ final McpTransportSession <Disposable > transportSession = this .activeSession .get ();
245+
246+ if (ClosedMcpTransportSession .INSTANCE .equals (transportSession )) {
247+ throw new McpTransportSessionClosedException ();
248+ }
253249
254250 if (stream != null ) {
255251 logger .debug ("Reconnecting stream {} with lastId {}" , stream .streamId (), stream .lastId ());
@@ -259,7 +255,7 @@ private Mono<Disposable> reconnect(McpTransportStream<Disposable> stream) {
259255 }
260256
261257 final AtomicReference <Disposable > disposableRef = new AtomicReference <>();
262- final McpTransportSession < Disposable > transportSession = this . activeSession . get ();
258+
263259 var uri = Utils .resolveUri (this .baseUri , this .endpoint );
264260
265261 Disposable connection = Mono .deferContextual (connectionCtx -> {
@@ -389,18 +385,18 @@ else if (statusCode == BAD_REQUEST) {
389385 "Received unrecognized SSE event type: " + sseResponseEvent .sseEvent ().event ()));
390386 })
391387 .retryWhen (authorizationErrorRetrySpec ())
392- .flatMap (jsonrpcMessage -> this . handler . get () .apply (Mono .just (jsonrpcMessage )))
388+ .flatMap (jsonrpcMessage -> requestHandler .apply (Mono .just (jsonrpcMessage )))
393389 .onErrorMap (CompletionException .class , t -> t .getCause ())
394- .onErrorComplete (t -> {
395- this .handleException (t );
396- return true ;
397- })
398390 .doFinally (s -> {
399391 Disposable ref = disposableRef .getAndSet (null );
400392 if (ref != null ) {
401393 transportSession .removeConnection (ref );
402394 }
403395 }))
396+ .onErrorComplete (t -> {
397+ this .handleException (t );
398+ return true ;
399+ })
404400 .contextWrite (ctx )
405401 .subscribe ();
406402
@@ -467,10 +463,23 @@ public String toString(McpSchema.JSONRPCMessage message) {
467463
468464 public Mono <Void > sendMessage (McpSchema .JSONRPCMessage sentMessage ) {
469465 return Mono .create (deliveredSink -> {
466+ var rh = this .handler .get ();
467+ if (rh == null ) {
468+ logger .warn ("Transport has no request handler registered. Remember to call connect!" );
469+ }
470+
471+ final Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> requestHandler = rh != null
472+ ? rh : msg -> Mono .error (new IllegalStateException ("No request handler" ));
473+
474+ var transportSession = this .activeSession .get ();
475+
476+ if (ClosedMcpTransportSession .INSTANCE .equals (transportSession )) {
477+ throw new McpTransportSessionClosedException ();
478+ }
479+
470480 logger .debug ("Sending message {}" , sentMessage );
471481
472482 final AtomicReference <Disposable > disposableRef = new AtomicReference <>();
473- final McpTransportSession <Disposable > transportSession = this .activeSession .get ();
474483
475484 var uri = Utils .resolveUri (this .baseUri , this .endpoint );
476485 String jsonBody = this .toString (sentMessage );
@@ -643,22 +652,26 @@ else if (statusCode == BAD_REQUEST) {
643652 new RuntimeException ("Failed to send message: " + responseEvent ));
644653 })
645654 .retryWhen (authorizationErrorRetrySpec ())
646- .flatMap (jsonRpcMessage -> this . handler . get () .apply (Mono .just (jsonRpcMessage )))
655+ .flatMap (jsonRpcMessage -> requestHandler .apply (Mono .just (jsonRpcMessage )))
647656 .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- })
655657 .doFinally (s -> {
656658 logger .debug ("SendMessage finally: {}" , s );
657659 Disposable ref = disposableRef .getAndSet (null );
658660 if (ref != null ) {
659661 transportSession .removeConnection (ref );
660662 }
661- })).contextWrite (deliveredSink .contextView ()).subscribe ();
663+ })).onErrorComplete (t -> {
664+ // handle the error first
665+ try {
666+ this .handleException (t );
667+ }
668+ catch (Exception e ) {
669+ logger .error ("Error handling exception {}" , t .getMessage (), e );
670+ }
671+ // inform the caller of sendMessage
672+ deliveredSink .error (t );
673+ return true ;
674+ }).contextWrite (deliveredSink .contextView ()).subscribe ();
662675
663676 disposableRef .set (connection );
664677 transportSession .addConnection (connection );
0 commit comments