@@ -25,14 +25,18 @@ export class StreamableHTTPError extends Error {
25
25
*/
26
26
interface StartSSEOptions {
27
27
/**
28
- * The ID of the last received event, used for resuming a disconnected stream
28
+ * The resumption token used to continue long-running requests that were interrupted.
29
+ *
30
+ * This allows clients to reconnect and continue from where they left off.
29
31
*/
30
- lastEventId ?: string ;
32
+ resumptionToken ?: string ;
31
33
32
34
/**
33
- * The callback function that is invoked when the last event ID changes
35
+ * A callback that is invoked when the resumption token changes.
36
+ *
37
+ * This allows clients to persist the latest token for potential reconnection.
34
38
*/
35
- onLastEventIdUpdate ?: ( event : string ) => void
39
+ onresumptiontoken ?: ( token : string ) => void ;
36
40
37
41
/**
38
42
* Override Message ID to associate with the replay message
@@ -152,7 +156,7 @@ export class StreamableHTTPClientTransport implements Transport {
152
156
throw new UnauthorizedError ( ) ;
153
157
}
154
158
155
- return await this . _startOrAuthSse ( { lastEventId : undefined } ) ;
159
+ return await this . _startOrAuthSse ( { resumptionToken : undefined } ) ;
156
160
}
157
161
158
162
private async _commonHeaders ( ) : Promise < Headers > {
@@ -175,16 +179,16 @@ export class StreamableHTTPClientTransport implements Transport {
175
179
176
180
177
181
private async _startOrAuthSse ( options : StartSSEOptions ) : Promise < void > {
178
- const { lastEventId } = options ;
182
+ const { resumptionToken } = options ;
179
183
try {
180
184
// Try to open an initial SSE stream with GET to listen for server messages
181
185
// This is optional according to the spec - server may not support it
182
186
const headers = await this . _commonHeaders ( ) ;
183
187
headers . set ( "Accept" , "text/event-stream" ) ;
184
188
185
189
// Include Last-Event-ID header for resumable streams if provided
186
- if ( lastEventId ) {
187
- headers . set ( "last-event-id" , lastEventId ) ;
190
+ if ( resumptionToken ) {
191
+ headers . set ( "last-event-id" , resumptionToken ) ;
188
192
}
189
193
190
194
const response = await fetch ( this . _url , {
@@ -270,7 +274,7 @@ export class StreamableHTTPClientTransport implements Transport {
270
274
if ( ! stream ) {
271
275
return ;
272
276
}
273
- const { onLastEventIdUpdate , replayMessageId } = options ;
277
+ const { onresumptiontoken , replayMessageId } = options ;
274
278
275
279
let lastEventId : string | undefined ;
276
280
const processStream = async ( ) => {
@@ -293,7 +297,7 @@ export class StreamableHTTPClientTransport implements Transport {
293
297
// Update last event ID if provided
294
298
if ( event . id ) {
295
299
lastEventId = event . id ;
296
- onLastEventIdUpdate ?.( lastEventId ) ;
300
+ onresumptiontoken ?.( event . id ) ;
297
301
}
298
302
299
303
if ( ! event . event || event . event === "message" ) {
@@ -317,7 +321,11 @@ export class StreamableHTTPClientTransport implements Transport {
317
321
// Use the exponential backoff reconnection strategy
318
322
if ( lastEventId !== undefined ) {
319
323
try {
320
- this . _scheduleReconnection ( options , 0 ) ;
324
+ this . _scheduleReconnection ( {
325
+ resumptionToken : lastEventId ,
326
+ onresumptiontoken,
327
+ replayMessageId
328
+ } , 0 ) ;
321
329
}
322
330
catch ( error ) {
323
331
this . onerror ?.( new Error ( `Failed to reconnect: ${ error instanceof Error ? error . message : String ( error ) } ` ) ) ;
@@ -363,13 +371,11 @@ export class StreamableHTTPClientTransport implements Transport {
363
371
364
372
async send ( message : JSONRPCMessage | JSONRPCMessage [ ] , options ?: { resumptionToken ?: string , onresumptiontoken ?: ( token : string ) => void } ) : Promise < void > {
365
373
try {
366
- // If client passes in a lastEventId in the request options, we need to reconnect the SSE stream
367
- const lastEventId = options ?. resumptionToken
368
- const onLastEventIdUpdate = options ?. onresumptiontoken ;
369
- if ( lastEventId ) {
374
+ const { resumptionToken, onresumptiontoken } = options || { } ;
370
375
376
+ if ( resumptionToken ) {
371
377
// If we have at last event ID, we need to reconnect the SSE stream
372
- this . _startOrAuthSse ( { lastEventId , replayMessageId : isJSONRPCRequest ( message ) ? message . id : undefined } ) . catch ( err => this . onerror ?.( err ) ) ;
378
+ this . _startOrAuthSse ( { resumptionToken , replayMessageId : isJSONRPCRequest ( message ) ? message . id : undefined } ) . catch ( err => this . onerror ?.( err ) ) ;
373
379
return ;
374
380
}
375
381
@@ -416,7 +422,7 @@ export class StreamableHTTPClientTransport implements Transport {
416
422
// if it's supported by the server
417
423
if ( isJSONRPCNotification ( message ) && message . method === "notifications/initialized" ) {
418
424
// Start without a lastEventId since this is a fresh connection
419
- this . _startOrAuthSse ( { lastEventId : undefined } ) . catch ( err => this . onerror ?.( err ) ) ;
425
+ this . _startOrAuthSse ( { resumptionToken : undefined } ) . catch ( err => this . onerror ?.( err ) ) ;
420
426
}
421
427
return ;
422
428
}
@@ -434,7 +440,7 @@ export class StreamableHTTPClientTransport implements Transport {
434
440
// Handle SSE stream responses for requests
435
441
// We use the same handler as standalone streams, which now supports
436
442
// reconnection with the last event ID
437
- this . _handleSseStream ( response . body , { onLastEventIdUpdate } ) ;
443
+ this . _handleSseStream ( response . body , { onresumptiontoken } ) ;
438
444
} else if ( contentType ?. includes ( "application/json" ) ) {
439
445
// For non-streaming servers, we might get direct JSON responses
440
446
const data = await response . json ( ) ;
0 commit comments