1- use std:: { borrow:: Cow , collections:: HashMap , sync:: Arc , time:: Duration } ;
1+ use std:: {
2+ borrow:: Cow ,
3+ collections:: HashMap ,
4+ sync:: { Arc , RwLock } ,
5+ time:: Duration ,
6+ } ;
27
38use futures:: { Stream , StreamExt , future:: BoxFuture , stream:: BoxStream } ;
49use http:: { HeaderName , HeaderValue } ;
@@ -16,13 +21,38 @@ use crate::{
1621 ServerResult ,
1722 } ,
1823 transport:: {
24+ TransportSessionIdHandle , TransportSessionIdProvider ,
1925 common:: client_side_sse:: SseAutoReconnectStream ,
2026 worker:: { Worker , WorkerQuitReason , WorkerSendRequest , WorkerTransport } ,
2127 } ,
2228} ;
2329
2430type BoxedSseStream = BoxStream < ' static , Result < Sse , SseError > > ;
2531
32+ /// Cloneable read-only handle for the negotiated streamable HTTP session ID.
33+ #[ derive( Debug , Clone , Default ) ]
34+ pub struct StreamableHttpClientSession {
35+ session_id : Arc < RwLock < Option < Arc < str > > > > ,
36+ }
37+
38+ impl StreamableHttpClientSession {
39+ pub fn session_id ( & self ) -> Option < Arc < str > > {
40+ self . session_id . read ( ) . ok ( ) . and_then ( |guard| guard. clone ( ) )
41+ }
42+
43+ fn set_session_id ( & self , session_id : Option < Arc < str > > ) {
44+ if let Ok ( mut guard) = self . session_id . write ( ) {
45+ * guard = session_id;
46+ }
47+ }
48+ }
49+
50+ impl TransportSessionIdProvider for StreamableHttpClientSession {
51+ fn session_id ( & self ) -> Option < Arc < str > > {
52+ self . session_id ( )
53+ }
54+ }
55+
2656#[ derive( Debug ) ]
2757#[ non_exhaustive]
2858pub struct AuthRequiredError {
@@ -277,6 +307,7 @@ struct SessionCleanupInfo<C> {
277307pub struct StreamableHttpClientWorker < C : StreamableHttpClient > {
278308 pub client : C ,
279309 pub config : StreamableHttpClientTransportConfig ,
310+ session : StreamableHttpClientSession ,
280311}
281312
282313impl < C : StreamableHttpClient + Default > StreamableHttpClientWorker < C > {
@@ -287,13 +318,18 @@ impl<C: StreamableHttpClient + Default> StreamableHttpClientWorker<C> {
287318 uri : url. into ( ) ,
288319 ..Default :: default ( )
289320 } ,
321+ session : StreamableHttpClientSession :: default ( ) ,
290322 }
291323 }
292324}
293325
294326impl < C : StreamableHttpClient > StreamableHttpClientWorker < C > {
295327 pub fn new ( client : C , config : StreamableHttpClientTransportConfig ) -> Self {
296- Self { client, config }
328+ Self {
329+ client,
330+ config,
331+ session : StreamableHttpClientSession :: default ( ) ,
332+ }
297333 }
298334}
299335
@@ -447,6 +483,11 @@ impl<C: StreamableHttpClient> Worker for StreamableHttpClientWorker<C> {
447483 fn err_join ( e : tokio:: task:: JoinError ) -> Self :: Error {
448484 StreamableHttpError :: TokioJoinError ( e)
449485 }
486+ fn session_id_handle ( & self ) -> Option < TransportSessionIdHandle > {
487+ Some ( TransportSessionIdHandle :: new ( Arc :: new (
488+ self . session . clone ( ) ,
489+ ) ) )
490+ }
450491 fn config ( & self ) -> super :: worker:: WorkerConfig {
451492 super :: worker:: WorkerConfig {
452493 name : Some ( "StreamableHttpClientWorker" . into ( ) ) ,
@@ -505,6 +546,7 @@ impl<C: StreamableHttpClient> Worker for StreamableHttpClientWorker<C> {
505546 }
506547 None
507548 } ;
549+ self . session . set_session_id ( session_id. clone ( ) ) ;
508550 // Extract the negotiated protocol version from the init response
509551 // and build a custom headers map that includes MCP-Protocol-Version
510552 // for all subsequent HTTP requests (per MCP 2025-06-18 spec).
@@ -684,6 +726,7 @@ impl<C: StreamableHttpClient> Worker for StreamableHttpClientWorker<C> {
684726 streams. abort_all ( ) ;
685727
686728 session_id = new_session_id;
729+ self . session . set_session_id ( session_id. clone ( ) ) ;
687730 protocol_headers = new_protocol_headers;
688731 session_cleanup_info =
689732 session_id. as_ref ( ) . map ( |sid| SessionCleanupInfo {
@@ -872,6 +915,7 @@ impl<C: StreamableHttpClient> Worker for StreamableHttpClientWorker<C> {
872915 }
873916 }
874917 }
918+ self . session . set_session_id ( None ) ;
875919
876920 loop_result
877921 }
0 commit comments