@@ -74,7 +74,7 @@ export class StreamableHttpRunner extends TransportRunnerBase {
7474 jsonrpc : "2.0" ,
7575 error : {
7676 code : JSON_RPC_ERROR_CODE_SESSION_ID_INVALID ,
77- message : ` session id is invalid` ,
77+ message : " session id is invalid" ,
7878 } ,
7979 } ) ;
8080 return ;
@@ -85,7 +85,7 @@ export class StreamableHttpRunner extends TransportRunnerBase {
8585 jsonrpc : "2.0" ,
8686 error : {
8787 code : JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND ,
88- message : ` session not found` ,
88+ message : " session not found" ,
8989 } ,
9090 } ) ;
9191 return ;
@@ -114,12 +114,42 @@ export class StreamableHttpRunner extends TransportRunnerBase {
114114 }
115115
116116 const server = this . setupServer ( ) ;
117+ let keepAliveLoop : NodeJS . Timeout ;
117118 const transport = new StreamableHTTPServerTransport ( {
118119 sessionIdGenerator : ( ) : string => randomUUID ( ) . toString ( ) ,
119120 onsessioninitialized : ( sessionId ) : void => {
120121 server . session . logger . setAttribute ( "sessionId" , sessionId ) ;
121122
122123 this . sessionStore . setSession ( sessionId , transport , server . session . logger ) ;
124+
125+ let failedPings = 0 ;
126+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
127+ keepAliveLoop = setInterval ( async ( ) => {
128+ try {
129+ this . logger . debug ( {
130+ id : LogId . streamableHttpTransportKeepAlive ,
131+ context : "streamableHttpTransport" ,
132+ message : "Sending ping" ,
133+ } ) ;
134+
135+ await transport . send ( {
136+ jsonrpc : "2.0" ,
137+ method : "ping" ,
138+ } ) ;
139+ failedPings = 0 ;
140+ } catch ( err ) {
141+ this . logger . warning ( {
142+ id : LogId . streamableHttpTransportKeepAliveFailure ,
143+ context : "streamableHttpTransport" ,
144+ message : `Error sending ping (attempt #${ failedPings + 1 } ): ${ err instanceof Error ? err . message : String ( err ) } ` ,
145+ } ) ;
146+
147+ if ( ++ failedPings > 3 ) {
148+ clearInterval ( keepAliveLoop ) ;
149+ await transport . close ( ) ;
150+ }
151+ }
152+ } , 30_000 ) ;
123153 } ,
124154 onsessionclosed : async ( sessionId ) : Promise < void > => {
125155 try {
@@ -135,6 +165,8 @@ export class StreamableHttpRunner extends TransportRunnerBase {
135165 } ) ;
136166
137167 transport . onclose = ( ) : void => {
168+ clearInterval ( keepAliveLoop ) ;
169+
138170 server . close ( ) . catch ( ( error ) => {
139171 this . logger . error ( {
140172 id : LogId . streamableHttpTransportCloseFailure ,
0 commit comments