@@ -797,112 +797,59 @@ export class McpHub {
797797 }
798798 this . connections . push ( connection )
799799
800- // For stdio transports, we need to handle the connection differently
800+ // For stdio transports, we need to handle the connection properly to avoid race conditions
801801 if ( configInjected . type === "stdio" ) {
802- // Store command and args for retry logic
803- const isWindows = process . platform === "win32"
804- const isAlreadyWrapped =
805- configInjected . command . toLowerCase ( ) === "cmd.exe" || configInjected . command . toLowerCase ( ) === "cmd"
806- const command = isWindows && ! isAlreadyWrapped ? "cmd.exe" : configInjected . command
807- const args =
808- isWindows && ! isAlreadyWrapped
809- ? [ "/c" , configInjected . command , ...( configInjected . args || [ ] ) ]
810- : configInjected . args
811-
812- // Try to connect with retry logic for stdio servers
813- let connected = false
814- let retryCount = 0
815- const maxRetries = 3
816- const retryDelay = 1000 // 1 second
817-
818- while ( ! connected && retryCount < maxRetries ) {
819- try {
820- // Start the transport and connect
821- await ( transport as StdioClientTransport ) . start ( )
822-
823- // Set up stderr handler after transport is started
824- const stderrStream = ( transport as StdioClientTransport ) . stderr
825- if ( stderrStream ) {
826- stderrStream . on ( "data" , async ( data : Buffer ) => {
827- const output = data . toString ( )
828- // Check if output contains INFO level log
829- const isInfoLog = / I N F O / i. test ( output )
830-
831- if ( isInfoLog ) {
832- // Log normal informational messages
833- console . log ( `Server "${ name } " info:` , output )
834- } else {
835- // Treat as error log
836- console . error ( `Server "${ name } " stderr:` , output )
837- const connection = this . findConnection ( name , source )
838- if ( connection ) {
839- this . appendErrorMessage ( connection , output )
840- if ( connection . server . status === "disconnected" ) {
841- await this . notifyWebviewOfServerChanges ( )
842- }
843- }
844- }
845- } )
846- }
847-
848- // Now connect the client
849- await client . connect ( transport )
850- connected = true
851- connection . server . status = "connected"
852- connection . server . error = ""
853- } catch ( connectError ) {
854- retryCount ++
855- console . error (
856- `Failed to connect to MCP server "${ name } " (attempt ${ retryCount } /${ maxRetries } ):` ,
857- connectError ,
858- )
859-
860- if ( retryCount < maxRetries ) {
861- // Wait before retrying
862- await new Promise ( ( resolve ) => setTimeout ( resolve , retryDelay ) )
863-
864- // Create a new transport for the retry
865- if ( configInjected . type === "stdio" ) {
866- transport = new StdioClientTransport ( {
867- command,
868- args,
869- cwd : configInjected . cwd ,
870- env : {
871- ...getDefaultEnvironment ( ) ,
872- ...( configInjected . env || { } ) ,
873- } ,
874- stderr : "pipe" ,
875- } )
876-
877- // Re-setup error handlers for the new transport
878- transport . onerror = async ( error ) => {
879- console . error ( `Transport error for "${ name } ":` , error )
880- const connection = this . findConnection ( name , source )
881- if ( connection ) {
882- connection . server . status = "disconnected"
883- this . appendErrorMessage (
884- connection ,
885- error instanceof Error ? error . message : `${ error } ` ,
886- )
887- }
888- await this . notifyWebviewOfServerChanges ( )
889- }
890-
891- transport . onclose = async ( ) => {
892- const connection = this . findConnection ( name , source )
893- if ( connection ) {
894- connection . server . status = "disconnected"
895- }
802+ // Set up stderr handler BEFORE starting the transport to avoid race conditions
803+ const stderrStream = ( transport as StdioClientTransport ) . stderr
804+ if ( stderrStream ) {
805+ stderrStream . on ( "data" , async ( data : Buffer ) => {
806+ const output = data . toString ( )
807+ // Check if output contains INFO level log
808+ const isInfoLog = / I N F O / i. test ( output )
809+
810+ if ( isInfoLog ) {
811+ // Log normal informational messages
812+ console . log ( `Server "${ name } " info:` , output )
813+ } else {
814+ // Treat as error log
815+ console . error ( `Server "${ name } " stderr:` , output )
816+ const connection = this . findConnection ( name , source )
817+ if ( connection ) {
818+ this . appendErrorMessage ( connection , output )
819+ if ( connection . server . status === "disconnected" ) {
896820 await this . notifyWebviewOfServerChanges ( )
897821 }
898-
899- // Update the connection's transport reference
900- connection . transport = transport
901822 }
902- } else {
903- throw connectError
904823 }
824+ } )
825+ }
826+
827+ try {
828+ // Start the transport AFTER setting up handlers
829+ await ( transport as StdioClientTransport ) . start ( )
830+
831+ // Now connect the client
832+ await client . connect ( transport )
833+ connection . server . status = "connected"
834+ connection . server . error = ""
835+ } catch ( connectError ) {
836+ console . error ( `Failed to connect to MCP server "${ name } ":` , connectError )
837+
838+ // Update status with error
839+ connection . server . status = "disconnected"
840+ this . appendErrorMessage (
841+ connection ,
842+ connectError instanceof Error ? connectError . message : `${ connectError } ` ,
843+ )
844+
845+ // Clean up on failure
846+ try {
847+ await transport . close ( )
848+ } catch ( closeError ) {
849+ console . error ( `Failed to close transport after connection error:` , closeError )
905850 }
851+
852+ throw connectError
906853 }
907854 } else {
908855 // For non-stdio transports, connect normally
0 commit comments