@@ -74,7 +74,7 @@ export class StreamableHttpRunner extends TransportRunnerBase {
74
74
jsonrpc : "2.0" ,
75
75
error : {
76
76
code : JSON_RPC_ERROR_CODE_SESSION_ID_INVALID ,
77
- message : ` session id is invalid` ,
77
+ message : " session id is invalid" ,
78
78
} ,
79
79
} ) ;
80
80
return ;
@@ -85,7 +85,7 @@ export class StreamableHttpRunner extends TransportRunnerBase {
85
85
jsonrpc : "2.0" ,
86
86
error : {
87
87
code : JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND ,
88
- message : ` session not found` ,
88
+ message : " session not found" ,
89
89
} ,
90
90
} ) ;
91
91
return ;
@@ -114,12 +114,42 @@ export class StreamableHttpRunner extends TransportRunnerBase {
114
114
}
115
115
116
116
const server = this . setupServer ( ) ;
117
+ let keepAliveLoop : NodeJS . Timeout ;
117
118
const transport = new StreamableHTTPServerTransport ( {
118
119
sessionIdGenerator : ( ) : string => randomUUID ( ) . toString ( ) ,
119
120
onsessioninitialized : ( sessionId ) : void => {
120
121
server . session . logger . setAttribute ( "sessionId" , sessionId ) ;
121
122
122
123
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 ) ;
123
153
} ,
124
154
onsessionclosed : async ( sessionId ) : Promise < void > => {
125
155
try {
@@ -135,6 +165,8 @@ export class StreamableHttpRunner extends TransportRunnerBase {
135
165
} ) ;
136
166
137
167
transport . onclose = ( ) : void => {
168
+ clearInterval ( keepAliveLoop ) ;
169
+
138
170
server . close ( ) . catch ( ( error ) => {
139
171
this . logger . error ( {
140
172
id : LogId . streamableHttpTransportCloseFailure ,
0 commit comments