1
1
import { IncomingMessage , ServerResponse } from "node:http" ;
2
2
import { Transport } from "../shared/transport.js" ;
3
- import { MessageExtraInfo , RequestInfo , isInitializeRequest , isJSONRPCError , isJSONRPCRequest , isJSONRPCResponse , JSONRPCMessage , JSONRPCMessageSchema , RequestId , SUPPORTED_PROTOCOL_VERSIONS , DEFAULT_NEGOTIATED_PROTOCOL_VERSION } from "../types.js" ;
3
+ import { MessageExtraInfo , RequestInfo , isInitializeRequest , isJSONRPCError , isJSONRPCRequest , isJSONRPCResponse , JSONRPCMessage , JSONRPCMessageSchema , RequestId , ServerEvents , SUPPORTED_PROTOCOL_VERSIONS , DEFAULT_NEGOTIATED_PROTOCOL_VERSION , EventListener } from "../types.js" ;
4
4
import getRawBody from "raw-body" ;
5
5
import contentType from "content-type" ;
6
6
import { randomUUID } from "node:crypto" ;
7
7
import { AuthInfo } from "./auth/types.js" ;
8
+ import { EventEmitter } from "node:events" ;
8
9
9
10
const MAXIMUM_MESSAGE_SIZE = "4mb" ;
10
11
@@ -46,6 +47,13 @@ export interface StreamableHTTPServerTransportOptions {
46
47
*/
47
48
sessionIdGenerator : ( ( ) => string ) | undefined ;
48
49
50
+ /**
51
+ * Disabling local SSE means that the transport will not automatically send SSE messages to the local handler.
52
+ * You need to implement your own pub/sub mechanism to handle SSE messages by listening to the `responseSse` event
53
+ * and emitting them to the `sse` for any events that match the transport sessionId
54
+ */
55
+ disableLocalSse ?: boolean ;
56
+
49
57
/**
50
58
* A callback for session initialization events
51
59
* This is called when the server initializes a new session.
@@ -149,6 +157,8 @@ export class StreamableHTTPServerTransport implements Transport {
149
157
private _allowedHosts ?: string [ ] ;
150
158
private _allowedOrigins ?: string [ ] ;
151
159
private _enableDnsRebindingProtection : boolean ;
160
+ readonly events = new EventEmitter < ServerEvents > ( ) ;
161
+
152
162
153
163
sessionId ?: string ;
154
164
onclose ?: ( ) => void ;
@@ -168,6 +178,10 @@ export class StreamableHTTPServerTransport implements Transport {
168
178
this . sessionId = options . sessionId ;
169
179
this . _initialized = true ; // Assume initialized if session ID is provided
170
180
}
181
+ if ( ! options . disableLocalSse ) {
182
+ // If we are not disabling local SSE, we pipe see responses to the local handler
183
+ this . events . on ( 'responseSse' , ( data ) => this . events . emit ( 'sse' , data ) ) ;
184
+ }
171
185
}
172
186
173
187
/**
@@ -307,11 +321,19 @@ export class StreamableHTTPServerTransport implements Transport {
307
321
// otherwise the client will just wait for the first message
308
322
res . writeHead ( 200 , headers ) . flushHeaders ( ) ;
309
323
310
- // Assign the response to the standalone SSE stream
324
+ // Write any message matching the sessionId to the SSE stream
325
+ const listener : EventListener < ServerEvents [ 'sse' ] > = ( { sessionId, message, eventId } ) => {
326
+ if ( sessionId === this . sessionId ) {
327
+ this . writeSSEEvent ( res , message , eventId ) ;
328
+ }
329
+ } ;
330
+ this . events . on ( 'sse' , listener ) ;
331
+
311
332
this . _streamMapping . set ( this . _standaloneSseStreamId , res ) ;
312
333
// Set up close handler for client disconnects
313
334
res . on ( "close" , ( ) => {
314
335
this . _streamMapping . delete ( this . _standaloneSseStreamId ) ;
336
+ this . events . removeListener ( 'sse' , listener ) ;
315
337
} ) ;
316
338
}
317
339
@@ -469,7 +491,7 @@ export class StreamableHTTPServerTransport implements Transport {
469
491
470
492
// If we have a session ID and an onsessioninitialized handler, call it immediately
471
493
// This is needed in cases where the server needs to keep track of multiple sessions
472
- if ( this . sessionId && this . _onsessioninitialized ) {
494
+ if ( this . sessionId && this . _onsessioninitialized ) {
473
495
await Promise . resolve ( this . _onsessioninitialized ( this . sessionId ) ) ;
474
496
}
475
497
@@ -690,8 +712,11 @@ export class StreamableHTTPServerTransport implements Transport {
690
712
eventId = await this . _eventStore . storeEvent ( this . _standaloneSseStreamId , message ) ;
691
713
}
692
714
693
- // Send the message to the standalone SSE stream
694
- this . writeSSEEvent ( standaloneSse , message , eventId ) ;
715
+ // We emit the responseSse event. If disableLocalSse is set to true this message will not be automatically sent to any local SSE handler
716
+ // You can listen for the responseSse and emit to a pub/sub. You can also listen for sse events from pubs/sub and emit them
717
+ // to events['sse'] to send them to the local SSE handler.
718
+ this . events . emit ( 'responseSse' , { sessionId : this . sessionId , message, eventId} ) ;
719
+
695
720
return ;
696
721
}
697
722
0 commit comments