@@ -33,12 +33,23 @@ jest.mock("vscode", () => ({
3333jest . mock ( "fs/promises" )
3434jest . mock ( "../../../core/webview/ClineProvider" )
3535
36+ // Mock the MCP SDK modules
37+ jest . mock ( "@modelcontextprotocol/sdk/client/stdio" , ( ) => ( {
38+ StdioClientTransport : jest . fn ( ) ,
39+ getDefaultEnvironment : jest . fn ( ) . mockReturnValue ( { PATH : "/usr/bin" } ) ,
40+ } ) )
41+
42+ jest . mock ( "@modelcontextprotocol/sdk/client/index" , ( ) => ( {
43+ Client : jest . fn ( ) ,
44+ } ) )
45+
3646describe ( "McpHub" , ( ) => {
3747 let mcpHub : McpHubType
3848 let mockProvider : Partial < ClineProvider >
3949
4050 // Store original console methods
4151 const originalConsoleError = console . error
52+ const originalPlatform = Object . getOwnPropertyDescriptor ( process , "platform" )
4253
4354 beforeEach ( ( ) => {
4455 jest . clearAllMocks ( )
@@ -113,6 +124,10 @@ describe("McpHub", () => {
113124 afterEach ( ( ) => {
114125 // Restore original console methods
115126 console . error = originalConsoleError
127+ // Restore original platform
128+ if ( originalPlatform ) {
129+ Object . defineProperty ( process , "platform" , originalPlatform )
130+ }
116131 } )
117132
118133 describe ( "toggleToolAlwaysAllow" , ( ) => {
@@ -684,4 +699,148 @@ describe("McpHub", () => {
684699 } )
685700 } )
686701 } )
702+
703+ describe ( "Windows command wrapping" , ( ) => {
704+ let StdioClientTransport : jest . Mock
705+ let Client : jest . Mock
706+
707+ beforeEach ( ( ) => {
708+ // Reset mocks
709+ jest . clearAllMocks ( )
710+
711+ // Get references to the mocked constructors
712+ StdioClientTransport = require ( "@modelcontextprotocol/sdk/client/stdio" ) . StdioClientTransport as jest . Mock
713+ Client = require ( "@modelcontextprotocol/sdk/client/index" ) . Client as jest . Mock
714+
715+ // Mock Windows platform
716+ Object . defineProperty ( process , "platform" , {
717+ value : "win32" ,
718+ writable : true ,
719+ enumerable : true ,
720+ configurable : true ,
721+ } )
722+ } )
723+
724+ it ( "should wrap commands with cmd.exe on Windows" , async ( ) => {
725+ // Mock StdioClientTransport
726+ const mockTransport = {
727+ start : jest . fn ( ) . mockResolvedValue ( undefined ) ,
728+ close : jest . fn ( ) . mockResolvedValue ( undefined ) ,
729+ stderr : {
730+ on : jest . fn ( ) ,
731+ } ,
732+ onerror : null ,
733+ onclose : null ,
734+ }
735+
736+ StdioClientTransport . mockImplementation ( ( config : any ) => {
737+ // Verify that cmd.exe wrapping is applied
738+ expect ( config . command ) . toBe ( "cmd.exe" )
739+ expect ( config . args ) . toEqual ( [
740+ "/c" ,
741+ "npx" ,
742+ "-y" ,
743+ "@modelcontextprotocol/server-filesystem" ,
744+ "/test/path" ,
745+ ] )
746+ return mockTransport
747+ } )
748+
749+ // Mock Client
750+ Client . mockImplementation ( ( ) => ( {
751+ connect : jest . fn ( ) . mockResolvedValue ( undefined ) ,
752+ close : jest . fn ( ) . mockResolvedValue ( undefined ) ,
753+ getInstructions : jest . fn ( ) . mockReturnValue ( "test instructions" ) ,
754+ request : jest . fn ( ) . mockResolvedValue ( { tools : [ ] , resources : [ ] , resourceTemplates : [ ] } ) ,
755+ } ) )
756+
757+ // Create a new McpHub instance
758+ const mcpHub = new McpHub ( mockProvider as ClineProvider )
759+
760+ // Mock the config file read
761+ ; ( fs . readFile as jest . Mock ) . mockResolvedValue (
762+ JSON . stringify ( {
763+ mcpServers : {
764+ "test-npx-server" : {
765+ command : "npx" ,
766+ args : [ "-y" , "@modelcontextprotocol/server-filesystem" , "/test/path" ] ,
767+ } ,
768+ } ,
769+ } ) ,
770+ )
771+
772+ // Initialize servers (this will trigger connectToServer)
773+ await mcpHub [ "initializeGlobalMcpServers" ] ( )
774+
775+ // Verify StdioClientTransport was called with wrapped command
776+ expect ( StdioClientTransport ) . toHaveBeenCalledWith (
777+ expect . objectContaining ( {
778+ command : "cmd.exe" ,
779+ args : [ "/c" , "npx" , "-y" , "@modelcontextprotocol/server-filesystem" , "/test/path" ] ,
780+ } ) ,
781+ )
782+ } )
783+
784+ it ( "should not wrap commands on non-Windows platforms" , async ( ) => {
785+ // Mock non-Windows platform
786+ Object . defineProperty ( process , "platform" , {
787+ value : "darwin" ,
788+ writable : true ,
789+ enumerable : true ,
790+ configurable : true ,
791+ } )
792+
793+ // Mock StdioClientTransport
794+ const mockTransport = {
795+ start : jest . fn ( ) . mockResolvedValue ( undefined ) ,
796+ close : jest . fn ( ) . mockResolvedValue ( undefined ) ,
797+ stderr : {
798+ on : jest . fn ( ) ,
799+ } ,
800+ onerror : null ,
801+ onclose : null ,
802+ }
803+
804+ StdioClientTransport . mockImplementation ( ( config : any ) => {
805+ // Verify that no cmd.exe wrapping is applied
806+ expect ( config . command ) . toBe ( "npx" )
807+ expect ( config . args ) . toEqual ( [ "-y" , "@modelcontextprotocol/server-filesystem" , "/test/path" ] )
808+ return mockTransport
809+ } )
810+
811+ // Mock Client
812+ Client . mockImplementation ( ( ) => ( {
813+ connect : jest . fn ( ) . mockResolvedValue ( undefined ) ,
814+ close : jest . fn ( ) . mockResolvedValue ( undefined ) ,
815+ getInstructions : jest . fn ( ) . mockReturnValue ( "test instructions" ) ,
816+ request : jest . fn ( ) . mockResolvedValue ( { tools : [ ] , resources : [ ] , resourceTemplates : [ ] } ) ,
817+ } ) )
818+
819+ // Create a new McpHub instance
820+ const mcpHub = new McpHub ( mockProvider as ClineProvider )
821+
822+ // Mock the config file read
823+ ; ( fs . readFile as jest . Mock ) . mockResolvedValue (
824+ JSON . stringify ( {
825+ mcpServers : {
826+ "test-npx-server" : {
827+ command : "npx" ,
828+ args : [ "-y" , "@modelcontextprotocol/server-filesystem" , "/test/path" ] ,
829+ } ,
830+ } ,
831+ } ) ,
832+ )
833+
834+ // Initialize servers (this will trigger connectToServer)
835+ await mcpHub [ "initializeGlobalMcpServers" ] ( )
836+
837+ // Verify StdioClientTransport was called without wrapping
838+ expect ( StdioClientTransport ) . toHaveBeenCalledWith (
839+ expect . objectContaining ( {
840+ command : "npx" ,
841+ args : [ "-y" , "@modelcontextprotocol/server-filesystem" , "/test/path" ] ,
842+ } ) ,
843+ )
844+ } )
845+ } )
687846} )
0 commit comments