@@ -242,13 +242,122 @@ export async function createServer() {
242242
243243 // Notify tool discovery manager of configuration changes with changes parameter
244244 await toolDiscoveryManager . handleConfigurationUpdate ( config , changes ) ;
245+
246+ // Auto-spawn stdio processes when servers are added
247+ if ( changes && changes . addedServers . length > 0 ) {
248+ for ( const serverName of changes . addedServers ) {
249+ const serverConfig = config . servers [ serverName ] ;
250+ const transportType = serverConfig . transport_type || serverConfig . type ;
251+
252+ if ( transportType === 'stdio' ) {
253+ try {
254+ server . log . info ( {
255+ operation : 'auto_spawn_stdio_server' ,
256+ server_name : serverName ,
257+ transport_type : transportType
258+ } , `Auto-spawning stdio server: ${ serverName } ` ) ;
259+
260+ // Check if already running
261+ const existing = runtimeState . getProcessByName ( serverName ) ;
262+ if ( existing && existing . status === 'running' ) {
263+ server . log . warn ( {
264+ operation : 'auto_spawn_already_running' ,
265+ server_name : serverName
266+ } , `stdio server already running, skipping spawn: ${ serverName } ` ) ;
267+ continue ;
268+ }
269+
270+ // Build MCP server config for process spawning
271+ const processConfig = {
272+ installation_id : serverConfig . installation_id || serverName ,
273+ installation_name : serverName ,
274+ team_id : serverConfig . team_id || 'unknown' ,
275+ command : serverConfig . command ! ,
276+ args : serverConfig . args ! ,
277+ env : serverConfig . env || { }
278+ } ;
279+
280+ // Spawn the process
281+ const processInfo = await processManager . spawnProcess ( processConfig ) ;
282+
283+ // Add to runtime state
284+ runtimeState . addProcess (
285+ processInfo ,
286+ processConfig . installation_id ,
287+ processConfig . installation_name ,
288+ processConfig . team_id
289+ ) ;
290+
291+ server . log . info ( {
292+ operation : 'auto_spawn_stdio_success' ,
293+ server_name : serverName ,
294+ pid : processInfo . process . pid
295+ } , `stdio server spawned successfully: ${ serverName } ` ) ;
296+
297+ } catch ( error ) {
298+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
299+ server . log . error ( {
300+ operation : 'auto_spawn_stdio_failed' ,
301+ server_name : serverName ,
302+ error : errorMessage
303+ } , `Failed to auto-spawn stdio server: ${ errorMessage } ` ) ;
304+ }
305+ }
306+ }
307+ }
245308 } ) ;
246309
310+ // Set up automatic tool discovery when stdio processes are spawned
311+ processManager . on ( 'processSpawned' , async ( processInfo ) => {
312+ try {
313+ server . log . info ( {
314+ operation : 'trigger_stdio_tool_discovery' ,
315+ installation_name : processInfo . config . installation_name ,
316+ team_id : processInfo . config . team_id
317+ } , `Process spawned successfully - triggering tool discovery for ${ processInfo . config . installation_name } ` ) ;
318+
319+ // FIXED: Add delay after handshake to allow MCP server to fully initialize
320+ // Some servers (especially npm-based ones) need time to register all tools
321+ server . log . debug ( {
322+ operation : 'tool_discovery_delay' ,
323+ installation_name : processInfo . config . installation_name ,
324+ delay_ms : 500
325+ } , `Waiting 500ms before tool discovery to ensure server is fully initialized` ) ;
326+
327+ await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
328+
329+ await toolDiscoveryManager . discoverStdioTools ( processInfo . config . installation_name ) ;
330+ } catch ( error ) {
331+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
332+ server . log . error ( {
333+ operation : 'stdio_tool_discovery_error' ,
334+ installation_name : processInfo . config . installation_name ,
335+ error : errorMessage
336+ } , `Failed to discover stdio tools after process spawn: ${ errorMessage } ` ) ;
337+ }
338+ } ) ;
339+
340+ server . log . info ( {
341+ operation : 'stdio_tool_discovery_handler_registered'
342+ } , 'Automatic stdio tool discovery handler registered for process spawn events' ) ;
343+
344+
247345 // Initialize MCP Protocol Handler (after HTTP Proxy Manager and Tool Discovery Manager)
248346 const mcpProtocolHandler = new McpProtocolHandler ( httpProxyManager , toolDiscoveryManager , server . log ) ;
249347
250- // Initialize Command Processor
251- const commandProcessor = new CommandProcessor ( server . log , dynamicConfigManager ) ;
348+ // Initialize Command Processor with stdio process management dependencies
349+ const commandProcessor = new CommandProcessor (
350+ server . log ,
351+ dynamicConfigManager ,
352+ processManager ,
353+ runtimeState ,
354+ stdioToolDiscoveryManager
355+ ) ;
356+
357+ server . log . info ( {
358+ operation : 'command_processor_initialized' ,
359+ stdio_support : true
360+ } , 'Command Processor initialized with stdio process management support' ) ;
252361
253362 // Initialize Command Polling Service (will be started after registration)
254363 let commandPollingService : CommandPollingService | undefined ;
@@ -535,6 +644,27 @@ export async function createServer() {
535644 // Set command processor for process reporting
536645 heartbeatService . setCommandProcessor ( commandProcessor ) ;
537646
647+ // Initialize HeartbeatDataBuilder for normalized heartbeat data
648+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
649+ const teamIsolationService = ( server as any ) . teamIsolationService ;
650+ const heartbeatDataBuilder = new ( await import ( './services/heartbeat-data-builder' ) ) . HeartbeatDataBuilder (
651+ processManager ,
652+ runtimeState ,
653+ toolDiscoveryManager ,
654+ dynamicConfigManager ,
655+ teamIsolationService ,
656+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
657+ server . log as any
658+ ) ;
659+
660+ // Set heartbeat data builder for normalized data
661+ heartbeatService . setHeartbeatDataBuilder ( heartbeatDataBuilder ) ;
662+
663+ server . log . info ( {
664+ operation : 'heartbeat_data_builder_initialized' ,
665+ satellite_id : satelliteId
666+ } , 'Heartbeat data builder initialized for normalized heartbeat data' ) ;
667+
538668 // Store heartbeat service on server instance for potential future access
539669 server . decorate ( 'heartbeatService' , heartbeatService ) ;
540670
0 commit comments