import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { Inject, Injectable, Logger, OnApplicationBootstrap, } from '@nestjs/common'; import { ContextIdFactory, ModuleRef } from '@nestjs/core'; import { McpTransportType } from '../interfaces'; import type { McpOptions } from '../interfaces'; import { McpExecutorService } from '../services/mcp-executor.service'; import { McpRegistryDiscoveryService } from '../services/mcp-registry-discovery.service'; import { createMcpLogger } from '../utils/mcp-logger.factory'; import { createMcpServer } from '../utils/mcp-server.factory'; @Injectable() export class StdioService implements OnApplicationBootstrap { private readonly logger: Logger; constructor( @Inject('MCP_OPTIONS') private readonly options: McpOptions, @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string, private readonly moduleRef: ModuleRef, private readonly toolRegistry: McpRegistryDiscoveryService, ) { this.logger = createMcpLogger(StdioService.name, this.options); } async onApplicationBootstrap() { const transports = Array.isArray(this.options.transport) ? this.options.transport : [this.options.transport]; if (!transports.includes(McpTransportType.STDIO)) return; this.logger.log('Bootstrapping MCP STDIO...'); // Warn about tools with @ToolGuards() - they require HTTP context const toolsWithGuards = this.toolRegistry .getTools(this.mcpModuleId) .filter( (tool) => tool.metadata.guards && tool.metadata.guards.length > 0, ); if (toolsWithGuards.length > 0) { const toolNames = toolsWithGuards.map((t) => t.metadata.name).join(', '); this.logger.warn( `@ToolGuards() are not supported with STDIO transport. ` + `The following tools have guards configured and will be hidden: ${toolNames}. ` + `Consider using HTTP transport.`, ); } const mcpServer = createMcpServer( this.mcpModuleId, this.toolRegistry, this.options, this.logger, ); const contextId = ContextIdFactory.create(); const executor = await this.moduleRef.resolve( McpExecutorService, contextId, { strict: false }, ); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument executor.registerRequestHandlers(mcpServer, {} as any); const transport = new StdioServerTransport(); await mcpServer.connect(transport); this.logger.log('MCP STDIO ready'); } }