@@ -123,56 +123,12 @@ func GetOrLaunch(l *Launcher, serverID string) (*mcp.Connection, error) {
123123 }
124124
125125 // stdio backends from this point
126- // Warn if using direct command in a container
127- isDirectCommand := serverCfg .Command != "docker"
128- if l .runningInContainer && isDirectCommand {
129- l .logSecurityWarning (serverID , serverCfg )
130- }
131-
132- // Log the command being executed
133- l .logLaunchStart (serverID , "" , serverCfg , isDirectCommand )
134-
135- // Check for environment variable passthrough (only check args after -e flags)
136- l .logEnvPassthrough (serverCfg .Args )
137-
138- if len (serverCfg .Env ) > 0 {
139- log .Printf ("[LAUNCHER] Additional env vars: %v" , sanitize .TruncateSecretMap (serverCfg .Env ))
140- }
141-
142- log .Printf ("[LAUNCHER] Starting server with %v timeout" , l .startupTimeout )
143- logLauncher .Printf ("Starting server with timeout: serverID=%s, timeout=%v" , serverID , l .startupTimeout )
144-
145- // Create a buffered channel to receive connection result
146- // Buffer size of 1 prevents goroutine leak if timeout occurs before connection completes
147- resultChan := make (chan connectionResult , 1 )
148-
149- logLauncher .Printf ("Starting connection goroutine: serverID=%s" , serverID )
150- // Launch connection in a goroutine
151- go func () {
152- conn , err := mcp .NewConnection (l .ctx , serverID , serverCfg .Command , serverCfg .Args , serverCfg .Env )
153- resultChan <- connectionResult {conn , err }
154- }()
155-
156- // Wait for connection with timeout
157- select {
158- case result := <- resultChan :
159- conn , err := result .conn , result .err
160- if err != nil {
161- // Enhanced error logging for command-based servers
162- l .logLaunchError (serverID , "" , err , serverCfg , isDirectCommand )
163- return nil , fmt .Errorf ("failed to create connection: %w" , err )
164- }
165-
166- l .logLaunchSuccess (serverID , "" )
167-
168- l .connections [serverID ] = conn
169- return conn , nil
170-
171- case <- time .After (l .startupTimeout ):
172- // Timeout occurred
173- l .logTimeoutError (serverID , "" )
174- return nil , fmt .Errorf ("server startup timeout after %v" , l .startupTimeout )
126+ conn , err := l .launchStdioConnection (serverID , "" , serverCfg )
127+ if err != nil {
128+ return nil , err
175129 }
130+ l .connections [serverID ] = conn
131+ return conn , nil
176132}
177133
178134// GetOrLaunchForSession returns a session-aware connection or launches a new one
@@ -219,6 +175,22 @@ func GetOrLaunchForSession(l *Launcher, serverID, sessionID string) (*mcp.Connec
219175 return conn , nil
220176 }
221177
178+ conn , err := l .launchStdioConnection (serverID , sessionID , serverCfg )
179+ if err != nil {
180+ // Record error in session pool
181+ l .sessionPool .RecordError (serverID , sessionID )
182+ return nil , err
183+ }
184+
185+ // Add to session pool
186+ l .sessionPool .Set (serverID , sessionID , conn )
187+ return conn , nil
188+ }
189+
190+ // launchStdioConnection creates a new stdio connection using a goroutine+timeout pattern.
191+ // It handles the security warning, launch logging, and the buffered-channel/select timeout.
192+ // Returns the raw *mcp.Connection on success; the caller is responsible for storing it.
193+ func (l * Launcher ) launchStdioConnection (serverID , sessionID string , serverCfg * config.ServerConfig ) (* mcp.Connection , error ) {
222194 // Warn if using direct command in a container
223195 isDirectCommand := serverCfg .Command != "docker"
224196 if l .runningInContainer && isDirectCommand {
@@ -235,13 +207,14 @@ func GetOrLaunchForSession(l *Launcher, serverID, sessionID string) (*mcp.Connec
235207 log .Printf ("[LAUNCHER] Additional env vars: %v" , sanitize .TruncateSecretMap (serverCfg .Env ))
236208 }
237209
238- log .Printf ("[LAUNCHER] Starting server for session with %v timeout" , l .startupTimeout )
239- logLauncher .Printf ("Starting session server with timeout: serverID=%s, sessionID=%s, timeout=%v" , serverID , sessionID , l .startupTimeout )
210+ log .Printf ("[LAUNCHER] Starting server with %v timeout" , l .startupTimeout )
211+ logLauncher .Printf ("Starting server with timeout: serverID=%s, sessionID=%s, timeout=%v" , serverID , sessionID , l .startupTimeout )
240212
241213 // Create a buffered channel to receive connection result
242214 // Buffer size of 1 prevents goroutine leak if timeout occurs before connection completes
243215 resultChan := make (chan connectionResult , 1 )
244216
217+ logLauncher .Printf ("Starting connection goroutine: serverID=%s" , serverID )
245218 // Launch connection in a goroutine
246219 go func () {
247220 conn , err := mcp .NewConnection (l .ctx , serverID , serverCfg .Command , serverCfg .Args , serverCfg .Env )
@@ -254,24 +227,13 @@ func GetOrLaunchForSession(l *Launcher, serverID, sessionID string) (*mcp.Connec
254227 conn , err := result .conn , result .err
255228 if err != nil {
256229 l .logLaunchError (serverID , sessionID , err , serverCfg , isDirectCommand )
257-
258- // Record error in session pool
259- l .sessionPool .RecordError (serverID , sessionID )
260-
261230 return nil , fmt .Errorf ("failed to create connection: %w" , err )
262231 }
263-
264232 l .logLaunchSuccess (serverID , sessionID )
265-
266- // Add to session pool
267- l .sessionPool .Set (serverID , sessionID , conn )
268233 return conn , nil
269234
270235 case <- time .After (l .startupTimeout ):
271- // Timeout occurred
272236 l .logTimeoutError (serverID , sessionID )
273- // Record error in session pool before returning
274- l .sessionPool .RecordError (serverID , sessionID )
275237 return nil , fmt .Errorf ("server startup timeout after %v" , l .startupTimeout )
276238 }
277239}
0 commit comments