@@ -21,6 +21,9 @@ const { StdioClientTransport } = await import(
2121const { SSEClientTransport } = await import (
2222 "@modelcontextprotocol/sdk/client/sse.js"
2323) ;
24+ const { StreamableHTTPClientTransport } = await import (
25+ "@modelcontextprotocol/sdk/client/streamableHttp.js"
26+ ) ;
2427
2528describe ( "MultiServerMCPClient" , ( ) => {
2629 // Setup and teardown
@@ -63,6 +66,17 @@ describe("MultiServerMCPClient", () => {
6366 // Additional assertions to verify the connection was processed correctly
6467 } ) ;
6568
69+ test ( "should process valid streamable HTTP connection config" , ( ) => {
70+ const client = new MultiServerMCPClient ( {
71+ "test-server" : {
72+ transport : "http" ,
73+ url : "http://localhost:8000/mcp" ,
74+ } ,
75+ } ) ;
76+ expect ( client ) . toBeDefined ( ) ;
77+ // Additional assertions to verify the connection was processed correctly
78+ } ) ;
79+
6680 test ( "should have a compile time error and a runtime error when the config is invalid" , ( ) => {
6781 expect ( ( ) => {
6882 // eslint-disable-next-line no-new
@@ -93,6 +107,7 @@ describe("MultiServerMCPClient", () => {
93107 command : "python" ,
94108 args : [ "./script.py" ] ,
95109 env : undefined ,
110+ stderr : "inherit" ,
96111 } ) ;
97112
98113 expect ( Client ) . toHaveBeenCalled ( ) ;
@@ -116,6 +131,24 @@ describe("MultiServerMCPClient", () => {
116131 expect ( Client . prototype . listTools ) . toHaveBeenCalled ( ) ;
117132 } ) ;
118133
134+ test ( "should initialize streamable HTTP connections correctly" , async ( ) => {
135+ const client = new MultiServerMCPClient ( {
136+ "test-server" : {
137+ transport : "http" ,
138+ url : "http://localhost:8000/mcp" ,
139+ } ,
140+ } ) ;
141+
142+ await client . initializeConnections ( ) ;
143+
144+ expect ( StreamableHTTPClientTransport ) . toHaveBeenCalledWith (
145+ new URL ( "http://localhost:8000/mcp" )
146+ ) ;
147+ expect ( Client ) . toHaveBeenCalled ( ) ;
148+ expect ( Client . prototype . connect ) . toHaveBeenCalled ( ) ;
149+ expect ( Client . prototype . listTools ) . toHaveBeenCalled ( ) ;
150+ } ) ;
151+
119152 test ( "should throw on connection failure" , async ( ) => {
120153 ( Client as Mock ) . mockImplementationOnce ( ( ) => ( {
121154 connect : vi
@@ -307,6 +340,10 @@ describe("MultiServerMCPClient", () => {
307340 transport : "sse" ,
308341 url : "http://localhost:8000/sse" ,
309342 } ,
343+ server3 : {
344+ transport : "http" ,
345+ url : "http://localhost:8000/mcp" ,
346+ } ,
310347 } ) ;
311348
312349 await client . initializeConnections ( ) ;
@@ -315,6 +352,7 @@ describe("MultiServerMCPClient", () => {
315352 // Verify that all transports were closed using the mock functions directly
316353 expect ( StdioClientTransport . prototype . close ) . toHaveBeenCalled ( ) ;
317354 expect ( SSEClientTransport . prototype . close ) . toHaveBeenCalled ( ) ;
355+ expect ( StreamableHTTPClientTransport . prototype . close ) . toHaveBeenCalled ( ) ;
318356 } ) ;
319357
320358 test ( "should handle errors during cleanup gracefully" , async ( ) => {
@@ -341,4 +379,105 @@ describe("MultiServerMCPClient", () => {
341379 expect ( closeMock ) . toHaveBeenCalledOnce ( ) ;
342380 } ) ;
343381 } ) ;
382+
383+ // Streamable HTTP specific tests
384+ describe ( "streamable HTTP transport" , ( ) => {
385+ test ( "should throw when streamable HTTP config is missing required fields" , ( ) => {
386+ expect ( ( ) => {
387+ // eslint-disable-next-line no-new
388+ new MultiServerMCPClient ( {
389+ // @ts -expect-error missing url field
390+ "test-server" : {
391+ transport : "http" ,
392+ // Missing url field
393+ } ,
394+ } ) ;
395+ } ) . toThrow ( ZodError ) ;
396+ } ) ;
397+
398+ test ( "should throw when streamable HTTP URL is invalid" , ( ) => {
399+ expect ( ( ) => {
400+ // eslint-disable-next-line no-new
401+ new MultiServerMCPClient ( {
402+ "test-server" : {
403+ transport : "http" ,
404+ url : "invalid-url" , // Invalid URL format
405+ } ,
406+ } ) ;
407+ } ) . toThrow ( ZodError ) ;
408+ } ) ;
409+
410+ test ( "should handle mixed transport types including streamable HTTP" , async ( ) => {
411+ const client = new MultiServerMCPClient ( {
412+ "stdio-server" : {
413+ transport : "stdio" ,
414+ command : "python" ,
415+ args : [ "./script.py" ] ,
416+ } ,
417+ "sse-server" : {
418+ transport : "sse" ,
419+ url : "http://localhost:8000/sse" ,
420+ } ,
421+ "streamable-server" : {
422+ transport : "http" ,
423+ url : "http://localhost:8000/mcp" ,
424+ } ,
425+ } ) ;
426+
427+ await client . initializeConnections ( ) ;
428+
429+ // Verify all transports were initialized
430+ expect ( StreamableHTTPClientTransport ) . toHaveBeenCalled ( ) ;
431+ expect ( SSEClientTransport ) . toHaveBeenCalled ( ) ;
432+ expect ( StdioClientTransport ) . toHaveBeenCalled ( ) ;
433+
434+ // Get tools from all servers
435+ const tools = await client . getTools ( ) ;
436+ expect ( tools . length ) . toBeGreaterThan ( 0 ) ;
437+ } ) ;
438+
439+ test ( "should throw on streamable HTTP connection failure" , async ( ) => {
440+ ( Client as Mock ) . mockImplementationOnce ( ( ) => ( {
441+ connect : vi
442+ . fn ( )
443+ . mockReturnValue ( Promise . reject ( new Error ( "Connection failed" ) ) ) ,
444+ listTools : vi . fn ( ) . mockReturnValue ( Promise . resolve ( { tools : [ ] } ) ) ,
445+ } ) ) ;
446+
447+ const client = new MultiServerMCPClient ( {
448+ "test-server" : {
449+ transport : "http" ,
450+ url : "http://localhost:8000/mcp" ,
451+ } ,
452+ } ) ;
453+
454+ await expect ( ( ) => client . initializeConnections ( ) ) . rejects . toThrow (
455+ MCPClientError
456+ ) ;
457+ } ) ;
458+
459+ test ( "should handle errors during streamable HTTP cleanup gracefully" , async ( ) => {
460+ const closeMock = vi
461+ . fn ( )
462+ . mockReturnValue ( Promise . reject ( new Error ( "Close failed" ) ) ) ;
463+
464+ // Mock close to throw an error
465+ ( StreamableHTTPClientTransport as Mock ) . mockImplementationOnce ( ( ) => ( {
466+ close : closeMock ,
467+ connect : vi . fn ( ) . mockReturnValue ( Promise . resolve ( ) ) ,
468+ } ) ) ;
469+
470+ const client = new MultiServerMCPClient ( {
471+ "test-server" : {
472+ transport : "http" ,
473+ url : "http://localhost:8000/mcp" ,
474+ } ,
475+ } ) ;
476+
477+ await client . initializeConnections ( ) ;
478+ await client . close ( ) ;
479+
480+ expect ( closeMock ) . toHaveBeenCalledOnce ( ) ;
481+ } ) ;
482+ } ) ;
344483} ) ;
0 commit comments