@@ -2,15 +2,15 @@ import { IncomingMessage, ServerResponse } from "node:http";
22import { StreamableHTTPServerTransport } from "./streamable-http.js" ;
33import { JSONRPCMessage } from "../types.js" ;
44import { Readable } from "node:stream" ;
5-
5+ import { randomUUID } from "node:crypto" ;
66// Mock IncomingMessage
77function createMockRequest ( options : {
88 method : string ;
99 headers : Record < string , string | string [ ] | undefined > ;
1010 body ?: string ;
1111} ) : IncomingMessage {
1212 const readable = new Readable ( ) ;
13- readable . _read = ( ) => { } ;
13+ readable . _read = ( ) => { } ;
1414 if ( options . body ) {
1515 readable . push ( options . body ) ;
1616 readable . push ( null ) ;
@@ -37,12 +37,13 @@ function createMockResponse(): jest.Mocked<ServerResponse> {
3737}
3838
3939describe ( "StreamableHTTPServerTransport" , ( ) => {
40- const endpoint = "/mcp" ;
4140 let transport : StreamableHTTPServerTransport ;
4241 let mockResponse : jest . Mocked < ServerResponse > ;
4342
4443 beforeEach ( ( ) => {
45- transport = new StreamableHTTPServerTransport ( endpoint ) ;
44+ transport = new StreamableHTTPServerTransport ( {
45+ sessionId : randomUUID ( ) ,
46+ } ) ;
4647 mockResponse = createMockResponse ( ) ;
4748 } ) ;
4849
@@ -61,7 +62,7 @@ describe("StreamableHTTPServerTransport", () => {
6162 const initializeMessage : JSONRPCMessage = {
6263 jsonrpc : "2.0" ,
6364 method : "initialize" ,
64- params : {
65+ params : {
6566 clientInfo : { name : "test-client" , version : "1.0" } ,
6667 protocolVersion : "2025-03-26"
6768 } ,
@@ -119,49 +120,13 @@ describe("StreamableHTTPServerTransport", () => {
119120 expect ( mockResponse . end ) . toHaveBeenCalledWith ( expect . stringContaining ( '"jsonrpc":"2.0"' ) ) ;
120121 expect ( mockResponse . end ) . toHaveBeenCalledWith ( expect . stringContaining ( '"message":"Bad Request: Mcp-Session-Id header is required"' ) ) ;
121122 } ) ;
122-
123- it ( "should always include session ID in initialization response even in stateless mode" , async ( ) => {
124- // Create a stateless transport for this test
125- const statelessTransport = new StreamableHTTPServerTransport ( endpoint , { enableSessionManagement : false } ) ;
126-
127- // Create an initialization request
128- const initializeMessage : JSONRPCMessage = {
129- jsonrpc : "2.0" ,
130- method : "initialize" ,
131- params : {
132- clientInfo : { name : "test-client" , version : "1.0" } ,
133- protocolVersion : "2025-03-26"
134- } ,
135- id : "init-1" ,
136- } ;
137-
138- const req = createMockRequest ( {
139- method : "POST" ,
140- headers : {
141- "content-type" : "application/json" ,
142- "accept" : "application/json" ,
143- } ,
144- body : JSON . stringify ( initializeMessage ) ,
145- } ) ;
146-
147- await statelessTransport . handleRequest ( req , mockResponse ) ;
148-
149- // In stateless mode, session ID should also be included for initialize responses
150- expect ( mockResponse . writeHead ) . toHaveBeenCalledWith (
151- 200 ,
152- expect . objectContaining ( {
153- "mcp-session-id" : statelessTransport . sessionId ,
154- } )
155- ) ;
156- } ) ;
157123 } ) ;
158-
159124 describe ( "Stateless Mode" , ( ) => {
160125 let statelessTransport : StreamableHTTPServerTransport ;
161126 let mockResponse : jest . Mocked < ServerResponse > ;
162127
163128 beforeEach ( ( ) => {
164- statelessTransport = new StreamableHTTPServerTransport ( endpoint , { enableSessionManagement : false } ) ;
129+ statelessTransport = new StreamableHTTPServerTransport ( { sessionId : undefined } ) ;
165130 mockResponse = createMockResponse ( ) ;
166131 } ) ;
167132
@@ -268,7 +233,7 @@ describe("StreamableHTTPServerTransport", () => {
268233 } ) ;
269234
270235 await statelessTransport . handleRequest ( req2 , mockResponse ) ;
271-
236+
272237 // Should still succeed
273238 expect ( mockResponse . writeHead ) . toHaveBeenCalledWith (
274239 200 ,
@@ -278,12 +243,12 @@ describe("StreamableHTTPServerTransport", () => {
278243 ) ;
279244 } ) ;
280245
281- it ( "should handle initialization requests properly in both modes " , async ( ) => {
246+ it ( "should handle initialization requests properly in statefull mode " , async ( ) => {
282247 // Initialize message that would typically be sent during initialization
283248 const initializeMessage : JSONRPCMessage = {
284249 jsonrpc : "2.0" ,
285250 method : "initialize" ,
286- params : {
251+ params : {
287252 clientInfo : { name : "test-client" , version : "1.0" } ,
288253 protocolVersion : "2025-03-26"
289254 } ,
@@ -301,17 +266,27 @@ describe("StreamableHTTPServerTransport", () => {
301266 } ) ;
302267
303268 await transport . handleRequest ( statefulReq , mockResponse ) ;
304-
269+
305270 // In stateful mode, session ID should be included in the response header
306271 expect ( mockResponse . writeHead ) . toHaveBeenCalledWith (
307272 200 ,
308273 expect . objectContaining ( {
309274 "mcp-session-id" : transport . sessionId ,
310275 } )
311276 ) ;
277+ } ) ;
312278
313- // Reset mocks for stateless test
314- mockResponse . writeHead . mockClear ( ) ;
279+ it ( "should handle initialization requests properly in stateless mode" , async ( ) => {
280+ // Initialize message that would typically be sent during initialization
281+ const initializeMessage : JSONRPCMessage = {
282+ jsonrpc : "2.0" ,
283+ method : "initialize" ,
284+ params : {
285+ clientInfo : { name : "test-client" , version : "1.0" } ,
286+ protocolVersion : "2025-03-26"
287+ } ,
288+ id : "init-1" ,
289+ } ;
315290
316291 // Test stateless transport
317292 const statelessReq = createMockRequest ( {
@@ -324,10 +299,11 @@ describe("StreamableHTTPServerTransport", () => {
324299 } ) ;
325300
326301 await statelessTransport . handleRequest ( statelessReq , mockResponse ) ;
327-
302+
328303 // In stateless mode, session ID should also be included for initialize responses
329304 const headers = mockResponse . writeHead . mock . calls [ 0 ] [ 1 ] ;
330- expect ( headers ) . toHaveProperty ( "mcp-session-id" , statelessTransport . sessionId ) ;
305+ expect ( headers ) . not . toHaveProperty ( "mcp-session-id" ) ;
306+
331307 } ) ;
332308 } ) ;
333309
@@ -519,14 +495,14 @@ describe("StreamableHTTPServerTransport", () => {
519495
520496 // Send a message to first connection
521497 const message1 : JSONRPCMessage = {
522- jsonrpc : "2.0" ,
523- method : "test1" ,
524- params : { } ,
498+ jsonrpc : "2.0" ,
499+ method : "test1" ,
500+ params : { } ,
525501 id : 1
526502 } ;
527-
503+
528504 await transport . send ( message1 ) ;
529-
505+
530506 // Get message ID (captured from write call)
531507 const writeCall = mockResponse . write . mock . calls [ 0 ] [ 0 ] as string ;
532508 const idMatch = writeCall . match ( / i d : ( [ a - f 0 - 9 - ] + ) / ) ;
@@ -550,12 +526,12 @@ describe("StreamableHTTPServerTransport", () => {
550526
551527 // Send a second message
552528 const message2 : JSONRPCMessage = {
553- jsonrpc : "2.0" ,
554- method : "test2" ,
555- params : { } ,
529+ jsonrpc : "2.0" ,
530+ method : "test2" ,
531+ params : { } ,
556532 id : 2
557533 } ;
558-
534+
559535 await transport . send ( message2 ) ;
560536
561537 // Verify the second message was received by both connections
@@ -596,7 +572,7 @@ describe("StreamableHTTPServerTransport", () => {
596572 params : { } ,
597573 id : "test-id" ,
598574 } ;
599-
575+
600576 const reqPost = createMockRequest ( {
601577 method : "POST" ,
602578 headers : {
@@ -605,28 +581,28 @@ describe("StreamableHTTPServerTransport", () => {
605581 } ,
606582 body : JSON . stringify ( requestMessage ) ,
607583 } ) ;
608-
584+
609585 await transport . handleRequest ( reqPost , mockResponse1 ) ;
610-
586+
611587 // Send a response with matching ID
612588 const responseMessage : JSONRPCMessage = {
613589 jsonrpc : "2.0" ,
614590 result : { success : true } ,
615591 id : "test-id" ,
616592 } ;
617-
593+
618594 await transport . send ( responseMessage ) ;
619-
595+
620596 // Verify response was sent to the right connection
621597 expect ( mockResponse1 . write ) . toHaveBeenCalledWith (
622598 expect . stringContaining ( JSON . stringify ( responseMessage ) )
623599 ) ;
624-
600+
625601 // Check if write was called with this exact message on the second connection
626- const writeCallsOnSecondConn = mockResponse2 . write . mock . calls . filter ( call =>
602+ const writeCallsOnSecondConn = mockResponse2 . write . mock . calls . filter ( call =>
627603 typeof call [ 0 ] === 'string' && call [ 0 ] . includes ( JSON . stringify ( responseMessage ) )
628604 ) ;
629-
605+
630606 // Verify the response wasn't broadcast to all connections
631607 expect ( writeCallsOnSecondConn . length ) . toBe ( 0 ) ;
632608 } ) ;
@@ -680,7 +656,7 @@ describe("StreamableHTTPServerTransport", () => {
680656 const message : JSONRPCMessage = {
681657 jsonrpc : "2.0" ,
682658 method : "initialize" ,
683- params : {
659+ params : {
684660 clientInfo : { name : "test-client" , version : "1.0" } ,
685661 protocolVersion : "2025-03-26"
686662 } ,
@@ -715,17 +691,17 @@ describe("StreamableHTTPServerTransport", () => {
715691
716692 it ( "should handle pre-parsed batch messages" , async ( ) => {
717693 const batchMessages : JSONRPCMessage [ ] = [
718- {
719- jsonrpc : "2.0" ,
720- method : "method1" ,
694+ {
695+ jsonrpc : "2.0" ,
696+ method : "method1" ,
721697 params : { data : "test1" } ,
722- id : "batch1"
698+ id : "batch1"
723699 } ,
724- {
725- jsonrpc : "2.0" ,
726- method : "method2" ,
700+ {
701+ jsonrpc : "2.0" ,
702+ method : "method2" ,
727703 params : { data : "test2" } ,
728- id : "batch2"
704+ id : "batch2"
729705 } ,
730706 ] ;
731707
@@ -800,7 +776,7 @@ describe("StreamableHTTPServerTransport", () => {
800776 let mockResponse : jest . Mocked < ServerResponse > ;
801777
802778 beforeEach ( ( ) => {
803- transportWithHeaders = new StreamableHTTPServerTransport ( endpoint , { customHeaders } ) ;
779+ transportWithHeaders = new StreamableHTTPServerTransport ( { sessionId : randomUUID ( ) , customHeaders } ) ;
804780 mockResponse = createMockResponse ( ) ;
805781 } ) ;
806782
@@ -875,9 +851,10 @@ describe("StreamableHTTPServerTransport", () => {
875851 } ) ;
876852
877853 it ( "should not override essential headers with custom headers" , async ( ) => {
878- const transportWithConflictingHeaders = new StreamableHTTPServerTransport ( endpoint , {
854+ const transportWithConflictingHeaders = new StreamableHTTPServerTransport ( {
855+ sessionId : randomUUID ( ) ,
879856 customHeaders : {
880- "Content-Type" : "text/plain" , // 尝试覆盖必要的 Content-Type 头
857+ "Content-Type" : "text/plain" ,
881858 "X-Custom-Header" : "custom-value"
882859 }
883860 } ) ;
@@ -902,8 +879,10 @@ describe("StreamableHTTPServerTransport", () => {
902879 } ) ;
903880
904881 it ( "should work with empty custom headers" , async ( ) => {
905- const transportWithoutHeaders = new StreamableHTTPServerTransport ( endpoint ) ;
906-
882+ const transportWithoutHeaders = new StreamableHTTPServerTransport ( {
883+ sessionId : randomUUID ( ) ,
884+ } ) ;
885+
907886 const req = createMockRequest ( {
908887 method : "GET" ,
909888 headers : {
0 commit comments