UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

1,132 lines (969 loc) 32.5 kB
/** * @claude-flow/mcp - MCP Server * * High-performance MCP server implementation */ import { EventEmitter } from 'events'; import { platform, arch } from 'os'; import type { MCPServerConfig, MCPRequest, MCPResponse, MCPNotification, MCPSession, MCPTool, MCPInitializeParams, MCPInitializeResult, MCPCapabilities, MCPProtocolVersion, MCPServerMetrics, ITransport, TransportType, ILogger, ToolContext, } from './types.js'; import { MCPServerError, ErrorCodes } from './types.js'; import { ToolRegistry, createToolRegistry } from './tool-registry.js'; import { SessionManager, createSessionManager } from './session-manager.js'; import { ConnectionPool, createConnectionPool } from './connection-pool.js'; import { ResourceRegistry, createResourceRegistry } from './resource-registry.js'; import { PromptRegistry, createPromptRegistry } from './prompt-registry.js'; import { TaskManager, createTaskManager } from './task-manager.js'; import { createTransport, TransportManager, createTransportManager } from './transport/index.js'; import { RateLimiter, createRateLimiter, type RateLimitConfig } from './rate-limiter.js'; import { SamplingManager, createSamplingManager, type SamplingConfig, type LLMProvider } from './sampling.js'; const DEFAULT_CONFIG: Partial<MCPServerConfig> = { name: 'Claude-Flow MCP Server V3', version: '3.0.0', transport: 'stdio', host: 'localhost', port: 3000, enableMetrics: true, enableCaching: true, cacheTTL: 10000, logLevel: 'info', requestTimeout: 30000, maxRequestSize: 10 * 1024 * 1024, }; export interface IMCPServer { start(): Promise<void>; stop(): Promise<void>; registerTool(tool: MCPTool): boolean; registerTools(tools: MCPTool[]): { registered: number; failed: string[] }; getHealthStatus(): Promise<{ healthy: boolean; error?: string; metrics?: Record<string, number>; }>; getMetrics(): MCPServerMetrics; getSessions(): MCPSession[]; getSession(sessionId: string): MCPSession | undefined; terminateSession(sessionId: string): boolean; } export class MCPServer extends EventEmitter implements IMCPServer { private readonly config: MCPServerConfig; private readonly toolRegistry: ToolRegistry; private readonly sessionManager: SessionManager; private readonly resourceRegistry: ResourceRegistry; private readonly promptRegistry: PromptRegistry; private readonly taskManager: TaskManager; private readonly connectionPool?: ConnectionPool; private readonly transportManager: TransportManager; private readonly rateLimiter: RateLimiter; private readonly samplingManager: SamplingManager; private transport?: ITransport; private running = false; private startTime?: Date; private startupDuration?: number; private currentSession?: MCPSession; private resourceSubscriptions: Map<string, Set<string>> = new Map(); // sessionId -> subscribed URIs private readonly serverInfo = { name: 'Claude-Flow MCP Server V3', version: '3.0.0', }; // MCP protocol version — spec-required YYYY-MM-DD date string (#1874). // Claude Code's Zod validator rejects any other shape. private readonly protocolVersion: MCPProtocolVersion = '2025-11-25'; // Full MCP 2025-11-25 capabilities private capabilities: MCPCapabilities = { logging: { level: 'info' }, tools: { listChanged: true }, resources: { listChanged: true, subscribe: true }, prompts: { listChanged: true }, sampling: {}, }; private requestStats = { total: 0, successful: 0, failed: 0, totalResponseTime: 0, }; constructor( config: Partial<MCPServerConfig>, private readonly logger: ILogger, private readonly orchestrator?: unknown, private readonly swarmCoordinator?: unknown ) { super(); this.config = { ...DEFAULT_CONFIG, ...config } as MCPServerConfig; this.toolRegistry = createToolRegistry(logger); this.sessionManager = createSessionManager(logger, { maxSessions: 100, sessionTimeout: 30 * 60 * 1000, }); this.resourceRegistry = createResourceRegistry(logger, { enableSubscriptions: true, cacheEnabled: true, cacheTTL: 60000, }); this.promptRegistry = createPromptRegistry(logger); this.taskManager = createTaskManager(logger, { maxConcurrentTasks: 10, taskTimeout: 300000, }); this.transportManager = createTransportManager(logger); this.rateLimiter = createRateLimiter(logger, { requestsPerSecond: 100, burstSize: 200, perSessionLimit: 50, }); this.samplingManager = createSamplingManager(logger); if (this.config.connectionPool) { this.connectionPool = createConnectionPool( this.config.connectionPool, logger, this.config.transport ); } this.setupEventHandlers(); } /** * Get resource registry for external registration */ getResourceRegistry(): ResourceRegistry { return this.resourceRegistry; } /** * Get prompt registry for external registration */ getPromptRegistry(): PromptRegistry { return this.promptRegistry; } /** * Get task manager for async operations */ getTaskManager(): TaskManager { return this.taskManager; } /** * Get rate limiter for configuration */ getRateLimiter(): RateLimiter { return this.rateLimiter; } /** * Get sampling manager for LLM provider registration */ getSamplingManager(): SamplingManager { return this.samplingManager; } /** * Register an LLM provider for sampling */ registerLLMProvider(provider: LLMProvider, isDefault: boolean = false): void { this.samplingManager.registerProvider(provider, isDefault); } async start(): Promise<void> { if (this.running) { throw new MCPServerError('Server already running'); } const startTime = performance.now(); this.startTime = new Date(); this.logger.info('Starting MCP server', { name: this.config.name, version: this.config.version, transport: this.config.transport, }); try { this.transport = createTransport(this.config.transport, this.logger, { type: this.config.transport, host: this.config.host, port: this.config.port, corsEnabled: this.config.corsEnabled, corsOrigins: this.config.corsOrigins, auth: this.config.auth, maxRequestSize: String(this.config.maxRequestSize), requestTimeout: this.config.requestTimeout, } as any); this.transport.onRequest(async (request) => { return await this.handleRequest(request); }); this.transport.onNotification(async (notification) => { await this.handleNotification(notification); }); await this.transport.start(); await this.registerBuiltInTools(); this.running = true; this.startupDuration = performance.now() - startTime; this.logger.info('MCP server started', { startupTime: `${this.startupDuration.toFixed(2)}ms`, tools: this.toolRegistry.getToolCount(), }); this.emit('server:started', { startupTime: this.startupDuration, tools: this.toolRegistry.getToolCount(), }); } catch (error) { this.logger.error('Failed to start MCP server', { error }); throw new MCPServerError('Failed to start server', ErrorCodes.INTERNAL_ERROR, { error }); } } async stop(): Promise<void> { if (!this.running) { return; } this.logger.info('Stopping MCP server'); try { if (this.transport) { await this.transport.stop(); this.transport = undefined; } this.sessionManager.clearAll(); this.taskManager.destroy(); this.resourceSubscriptions.clear(); this.rateLimiter.destroy(); if (this.connectionPool) { await this.connectionPool.clear(); } this.running = false; this.currentSession = undefined; this.logger.info('MCP server stopped'); this.emit('server:stopped'); } catch (error) { this.logger.error('Error stopping MCP server', { error }); throw error; } } registerTool(tool: MCPTool): boolean { return this.toolRegistry.register(tool); } registerTools(tools: MCPTool[]): { registered: number; failed: string[] } { return this.toolRegistry.registerBatch(tools); } unregisterTool(name: string): boolean { return this.toolRegistry.unregister(name); } async getHealthStatus(): Promise<{ healthy: boolean; error?: string; metrics?: Record<string, number>; }> { try { const transportHealth = this.transport ? await this.transport.getHealthStatus() : { healthy: false, error: 'Transport not initialized' }; const sessionMetrics = this.sessionManager.getSessionMetrics(); const poolStats = this.connectionPool?.getStats(); const metrics: Record<string, number> = { registeredTools: this.toolRegistry.getToolCount(), totalRequests: this.requestStats.total, successfulRequests: this.requestStats.successful, failedRequests: this.requestStats.failed, totalSessions: sessionMetrics.total, activeSessions: sessionMetrics.active, ...(transportHealth.metrics || {}), }; if (poolStats) { metrics.poolConnections = poolStats.totalConnections; metrics.poolIdleConnections = poolStats.idleConnections; metrics.poolBusyConnections = poolStats.busyConnections; } return { healthy: this.running && transportHealth.healthy, error: transportHealth.error, metrics, }; } catch (error) { return { healthy: false, error: error instanceof Error ? error.message : 'Unknown error', }; } } getMetrics(): MCPServerMetrics { const sessionMetrics = this.sessionManager.getSessionMetrics(); const registryStats = this.toolRegistry.getStats(); return { totalRequests: this.requestStats.total, successfulRequests: this.requestStats.successful, failedRequests: this.requestStats.failed, averageResponseTime: this.requestStats.total > 0 ? this.requestStats.totalResponseTime / this.requestStats.total : 0, activeSessions: sessionMetrics.active, toolInvocations: Object.fromEntries( registryStats.topTools.map((t) => [t.name, t.calls]) ), errors: {}, lastReset: this.startTime || new Date(), startupTime: this.startupDuration, uptime: this.startTime ? Date.now() - this.startTime.getTime() : 0, }; } getSessions(): MCPSession[] { return this.sessionManager.getActiveSessions(); } getSession(sessionId: string): MCPSession | undefined { return this.sessionManager.getSession(sessionId); } terminateSession(sessionId: string): boolean { const result = this.sessionManager.closeSession(sessionId, 'Terminated by server'); if (this.currentSession?.id === sessionId) { this.currentSession = undefined; } return result; } private async handleRequest(request: MCPRequest): Promise<MCPResponse> { const startTime = performance.now(); this.requestStats.total++; this.logger.debug('Handling request', { id: request.id, method: request.method, }); // Rate limiting check (skip for initialize) if (request.method !== 'initialize') { const sessionId = this.currentSession?.id; const rateLimitResult = this.rateLimiter.check(sessionId); if (!rateLimitResult.allowed) { this.requestStats.failed++; return { jsonrpc: '2.0', id: request.id, error: { code: -32000, message: 'Rate limit exceeded', data: { retryAfter: rateLimitResult.retryAfter }, }, }; } this.rateLimiter.consume(sessionId); } try { if (request.method === 'initialize') { return await this.handleInitialize(request); } const session = this.getOrCreateSession(); if (!session.isInitialized && request.method !== 'initialized') { return this.createErrorResponse( request.id, ErrorCodes.SERVER_NOT_INITIALIZED, 'Server not initialized' ); } this.sessionManager.updateActivity(session.id); const response = await this.routeRequest(request); const duration = performance.now() - startTime; this.requestStats.successful++; this.requestStats.totalResponseTime += duration; this.logger.debug('Request completed', { id: request.id, method: request.method, duration: `${duration.toFixed(2)}ms`, }); return response; } catch (error) { const duration = performance.now() - startTime; this.requestStats.failed++; this.requestStats.totalResponseTime += duration; this.logger.error('Request failed', { id: request.id, method: request.method, error, }); return this.createErrorResponse( request.id, ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Internal error' ); } } private async handleNotification(notification: MCPNotification): Promise<void> { this.logger.debug('Handling notification', { method: notification.method }); switch (notification.method) { case 'initialized': this.logger.info('Client initialized notification received'); break; case 'notifications/cancelled': this.logger.debug('Request cancelled', notification.params); break; default: this.logger.debug('Unknown notification', { method: notification.method }); } } private async handleInitialize(request: MCPRequest): Promise<MCPResponse> { const params = request.params as unknown as MCPInitializeParams | undefined; if (!params) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Invalid params' ); } const session = this.sessionManager.createSession(this.config.transport); this.sessionManager.initializeSession(session.id, params); this.currentSession = session; const result: MCPInitializeResult = { protocolVersion: this.protocolVersion, capabilities: this.capabilities, serverInfo: this.serverInfo, instructions: 'Claude-Flow MCP Server V3 ready for tool execution', }; this.logger.info('Session initialized', { sessionId: session.id, clientInfo: params.clientInfo, }); return { jsonrpc: '2.0', id: request.id, result, }; } private async routeRequest(request: MCPRequest): Promise<MCPResponse> { switch (request.method) { // Tool methods case 'tools/list': return this.handleToolsList(request); case 'tools/call': return this.handleToolsCall(request); // Resource methods (MCP 2025-11-25) case 'resources/list': return this.handleResourcesList(request); case 'resources/read': return this.handleResourcesRead(request); case 'resources/subscribe': return this.handleResourcesSubscribe(request); case 'resources/unsubscribe': return this.handleResourcesUnsubscribe(request); // Prompt methods (MCP 2025-11-25) case 'prompts/list': return this.handlePromptsList(request); case 'prompts/get': return this.handlePromptsGet(request); // Task methods (MCP 2025-11-25) case 'tasks/status': return this.handleTasksStatus(request); case 'tasks/cancel': return this.handleTasksCancel(request); // Completion (MCP 2025-11-25) case 'completion/complete': return this.handleCompletion(request); // Logging (MCP 2025-11-25) case 'logging/setLevel': return this.handleLoggingSetLevel(request); // Sampling (MCP 2025-11-25) case 'sampling/createMessage': return this.handleSamplingCreateMessage(request); // Utility case 'ping': return { jsonrpc: '2.0', id: request.id, result: { pong: true, timestamp: Date.now() }, }; default: // Check if it's a direct tool call if (this.toolRegistry.hasTool(request.method)) { return this.handleToolExecution(request); } return this.createErrorResponse( request.id, ErrorCodes.METHOD_NOT_FOUND, `Method not found: ${request.method}` ); } } private handleToolsList(request: MCPRequest): MCPResponse { const tools = this.toolRegistry.listTools().map((t) => ({ name: t.name, description: t.description, inputSchema: this.toolRegistry.getTool(t.name)?.inputSchema, })); return { jsonrpc: '2.0', id: request.id, result: { tools }, }; } private async handleToolsCall(request: MCPRequest): Promise<MCPResponse> { const params = request.params as { name: string; arguments?: Record<string, unknown> }; if (!params?.name) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Tool name is required' ); } const context: ToolContext = { sessionId: this.currentSession?.id || 'unknown', requestId: request.id, orchestrator: this.orchestrator, swarmCoordinator: this.swarmCoordinator, }; const result = await this.toolRegistry.execute( params.name, params.arguments || {}, context ); return { jsonrpc: '2.0', id: request.id, result, }; } private async handleToolExecution(request: MCPRequest): Promise<MCPResponse> { const context: ToolContext = { sessionId: this.currentSession?.id || 'unknown', requestId: request.id, orchestrator: this.orchestrator, swarmCoordinator: this.swarmCoordinator, }; const result = await this.toolRegistry.execute( request.method, (request.params as Record<string, unknown>) || {}, context ); return { jsonrpc: '2.0', id: request.id, result, }; } // ============================================================================ // Resource Handlers (MCP 2025-11-25) // ============================================================================ private handleResourcesList(request: MCPRequest): MCPResponse { const params = request.params as { cursor?: string } | undefined; const result = this.resourceRegistry.list(params?.cursor); return { jsonrpc: '2.0', id: request.id, result, }; } private async handleResourcesRead(request: MCPRequest): Promise<MCPResponse> { const params = request.params as { uri: string } | undefined; if (!params?.uri) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Resource URI is required' ); } try { const result = await this.resourceRegistry.read(params.uri); return { jsonrpc: '2.0', id: request.id, result, }; } catch (error) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, error instanceof Error ? error.message : 'Resource read failed' ); } } private handleResourcesSubscribe(request: MCPRequest): MCPResponse { const params = request.params as { uri: string } | undefined; const sessionId = this.currentSession?.id; if (!params?.uri) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Resource URI is required' ); } if (!sessionId) { return this.createErrorResponse( request.id, ErrorCodes.SERVER_NOT_INITIALIZED, 'No active session' ); } try { // Track subscription for this session let sessionSubs = this.resourceSubscriptions.get(sessionId); if (!sessionSubs) { sessionSubs = new Set(); this.resourceSubscriptions.set(sessionId, sessionSubs); } const subscriptionId = this.resourceRegistry.subscribe(params.uri, (uri, content) => { // Send notification when resource updates this.sendNotification('notifications/resources/updated', { uri }); }); sessionSubs.add(params.uri); return { jsonrpc: '2.0', id: request.id, result: { subscriptionId }, }; } catch (error) { return this.createErrorResponse( request.id, ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Subscription failed' ); } } private handleResourcesUnsubscribe(request: MCPRequest): MCPResponse { const params = request.params as { uri: string } | undefined; const sessionId = this.currentSession?.id; if (!params?.uri) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Resource URI is required' ); } if (sessionId) { const sessionSubs = this.resourceSubscriptions.get(sessionId); if (sessionSubs) { sessionSubs.delete(params.uri); } } return { jsonrpc: '2.0', id: request.id, result: { success: true }, }; } // ============================================================================ // Prompt Handlers (MCP 2025-11-25) // ============================================================================ private handlePromptsList(request: MCPRequest): MCPResponse { const params = request.params as { cursor?: string } | undefined; const result = this.promptRegistry.list(params?.cursor); return { jsonrpc: '2.0', id: request.id, result, }; } private async handlePromptsGet(request: MCPRequest): Promise<MCPResponse> { const params = request.params as { name: string; arguments?: Record<string, string> } | undefined; if (!params?.name) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Prompt name is required' ); } try { const result = await this.promptRegistry.get(params.name, params.arguments); return { jsonrpc: '2.0', id: request.id, result, }; } catch (error) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, error instanceof Error ? error.message : 'Prompt get failed' ); } } // ============================================================================ // Task Handlers (MCP 2025-11-25) // ============================================================================ private handleTasksStatus(request: MCPRequest): MCPResponse { const params = request.params as { taskId?: string } | undefined; if (params?.taskId) { const task = this.taskManager.getTask(params.taskId); if (!task) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, `Task not found: ${params.taskId}` ); } return { jsonrpc: '2.0', id: request.id, result: task, }; } // Return all tasks return { jsonrpc: '2.0', id: request.id, result: { tasks: this.taskManager.getAllTasks() }, }; } private handleTasksCancel(request: MCPRequest): MCPResponse { const params = request.params as { taskId: string; reason?: string } | undefined; if (!params?.taskId) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Task ID is required' ); } const success = this.taskManager.cancelTask(params.taskId, params.reason); return { jsonrpc: '2.0', id: request.id, result: { success }, }; } // ============================================================================ // Completion Handler (MCP 2025-11-25) // ============================================================================ private handleCompletion(request: MCPRequest): MCPResponse { const params = request.params as { ref: { type: string; name?: string; uri?: string }; argument: { name: string; value: string }; } | undefined; if (!params?.ref || !params?.argument) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Completion reference and argument are required' ); } // Basic completion implementation - can be extended const completions: string[] = []; if (params.ref.type === 'ref/prompt') { // Get prompt argument completions const prompt = this.promptRegistry.getPrompt(params.ref.name || ''); if (prompt?.arguments) { for (const arg of prompt.arguments) { if (arg.name === params.argument.name) { // Could add domain-specific completions here } } } } else if (params.ref.type === 'ref/resource') { // Get resource URI completions const { resources } = this.resourceRegistry.list(); for (const resource of resources) { if (resource.uri.startsWith(params.argument.value)) { completions.push(resource.uri); } } } return { jsonrpc: '2.0', id: request.id, result: { completion: { values: completions.slice(0, 10), total: completions.length, hasMore: completions.length > 10, }, }, }; } // ============================================================================ // Logging Handler (MCP 2025-11-25) // ============================================================================ private handleLoggingSetLevel(request: MCPRequest): MCPResponse { const params = request.params as { level: string } | undefined; if (!params?.level) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'Log level is required' ); } // Update capabilities this.capabilities.logging = { level: params.level as 'debug' | 'info' | 'warn' | 'error' }; this.logger.info('Log level updated', { level: params.level }); return { jsonrpc: '2.0', id: request.id, result: { success: true }, }; } // ============================================================================ // Sampling Handler (MCP 2025-11-25) // ============================================================================ private async handleSamplingCreateMessage(request: MCPRequest): Promise<MCPResponse> { const params = request.params as { messages: Array<{ role: string; content: { type: string; text?: string } }>; maxTokens: number; systemPrompt?: string; modelPreferences?: { hints?: Array<{ name?: string }>; intelligencePriority?: number; speedPriority?: number; costPriority?: number }; includeContext?: string; temperature?: number; stopSequences?: string[]; metadata?: Record<string, unknown>; } | undefined; if (!params?.messages || !params?.maxTokens) { return this.createErrorResponse( request.id, ErrorCodes.INVALID_PARAMS, 'messages and maxTokens are required' ); } // Check if sampling is available const available = await this.samplingManager.isAvailable(); if (!available) { return this.createErrorResponse( request.id, ErrorCodes.INTERNAL_ERROR, 'No LLM provider available for sampling' ); } try { const result = await this.samplingManager.createMessage( { messages: params.messages.map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content as any, })), maxTokens: params.maxTokens, systemPrompt: params.systemPrompt, modelPreferences: params.modelPreferences, includeContext: params.includeContext as 'none' | 'thisServer' | 'allServers' | undefined, temperature: params.temperature, stopSequences: params.stopSequences, metadata: params.metadata, }, { sessionId: this.currentSession?.id || 'unknown', serverId: this.serverInfo.name, } ); return { jsonrpc: '2.0', id: request.id, result, }; } catch (error) { return this.createErrorResponse( request.id, ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Sampling failed' ); } } // ============================================================================ // Notification Sender // ============================================================================ private async sendNotification(method: string, params?: Record<string, unknown>): Promise<void> { if (this.transport?.sendNotification) { await this.transport.sendNotification({ jsonrpc: '2.0', method, params, }); } } private getOrCreateSession(): MCPSession { if (this.currentSession) { return this.currentSession; } const session = this.sessionManager.createSession(this.config.transport); this.currentSession = session; return session; } private createErrorResponse( id: string | number | null, code: number, message: string ): MCPResponse { return { jsonrpc: '2.0', id, error: { code, message }, }; } private async registerBuiltInTools(): Promise<void> { this.registerTool({ name: 'system/info', description: 'Get system information', inputSchema: { type: 'object', properties: {} }, handler: async () => ({ name: this.serverInfo.name, version: this.serverInfo.version, platform: platform(), arch: arch(), runtime: 'Node.js', uptime: this.startTime ? Date.now() - this.startTime.getTime() : 0, }), category: 'system', }); this.registerTool({ name: 'system/health', description: 'Get system health status', inputSchema: { type: 'object', properties: {} }, handler: async () => await this.getHealthStatus(), category: 'system', cacheable: true, cacheTTL: 2000, }); this.registerTool({ name: 'system/metrics', description: 'Get server metrics', inputSchema: { type: 'object', properties: {} }, handler: async () => this.getMetrics(), category: 'system', cacheable: true, cacheTTL: 1000, }); this.registerTool({ name: 'tools/list-detailed', description: 'List all registered tools with details', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Filter by category' }, }, }, handler: async (input: unknown) => { const params = input as { category?: string }; if (params.category) { return this.toolRegistry.getByCategory(params.category); } return this.toolRegistry.listTools(); }, category: 'system', }); this.logger.info('Built-in tools registered', { count: 4, }); } private setupEventHandlers(): void { this.toolRegistry.on('tool:registered', (name) => { this.emit('tool:registered', name); }); this.toolRegistry.on('tool:called', (data) => { this.emit('tool:called', data); }); this.toolRegistry.on('tool:completed', (data) => { this.emit('tool:completed', data); }); this.toolRegistry.on('tool:error', (data) => { this.emit('tool:error', data); }); this.sessionManager.on('session:created', (session) => { this.emit('session:created', session); }); this.sessionManager.on('session:closed', (data) => { this.emit('session:closed', data); }); this.sessionManager.on('session:expired', (session) => { this.emit('session:expired', session); }); } } export function createMCPServer( config: Partial<MCPServerConfig>, logger: ILogger, orchestrator?: unknown, swarmCoordinator?: unknown ): MCPServer { return new MCPServer(config, logger, orchestrator, swarmCoordinator); }