@claude-flow/mcp 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.agentic-flow/intelligence.json +16 -0
  2. package/README.md +428 -0
  3. package/__tests__/integration.test.ts +449 -0
  4. package/__tests__/mcp.test.ts +641 -0
  5. package/dist/connection-pool.d.ts +36 -0
  6. package/dist/connection-pool.d.ts.map +1 -0
  7. package/dist/connection-pool.js +273 -0
  8. package/dist/connection-pool.js.map +1 -0
  9. package/dist/index.d.ts +75 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +85 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/oauth.d.ts +146 -0
  14. package/dist/oauth.d.ts.map +1 -0
  15. package/dist/oauth.js +318 -0
  16. package/dist/oauth.js.map +1 -0
  17. package/dist/prompt-registry.d.ts +90 -0
  18. package/dist/prompt-registry.d.ts.map +1 -0
  19. package/dist/prompt-registry.js +209 -0
  20. package/dist/prompt-registry.js.map +1 -0
  21. package/dist/rate-limiter.d.ts +86 -0
  22. package/dist/rate-limiter.d.ts.map +1 -0
  23. package/dist/rate-limiter.js +197 -0
  24. package/dist/rate-limiter.js.map +1 -0
  25. package/dist/resource-registry.d.ts +144 -0
  26. package/dist/resource-registry.d.ts.map +1 -0
  27. package/dist/resource-registry.js +405 -0
  28. package/dist/resource-registry.js.map +1 -0
  29. package/dist/sampling.d.ts +102 -0
  30. package/dist/sampling.d.ts.map +1 -0
  31. package/dist/sampling.js +268 -0
  32. package/dist/sampling.js.map +1 -0
  33. package/dist/schema-validator.d.ts +30 -0
  34. package/dist/schema-validator.d.ts.map +1 -0
  35. package/dist/schema-validator.js +182 -0
  36. package/dist/schema-validator.js.map +1 -0
  37. package/dist/server.d.ts +122 -0
  38. package/dist/server.d.ts.map +1 -0
  39. package/dist/server.js +829 -0
  40. package/dist/server.js.map +1 -0
  41. package/dist/session-manager.d.ts +55 -0
  42. package/dist/session-manager.d.ts.map +1 -0
  43. package/dist/session-manager.js +252 -0
  44. package/dist/session-manager.js.map +1 -0
  45. package/dist/task-manager.d.ts +81 -0
  46. package/dist/task-manager.d.ts.map +1 -0
  47. package/dist/task-manager.js +337 -0
  48. package/dist/task-manager.js.map +1 -0
  49. package/dist/tool-registry.d.ts +88 -0
  50. package/dist/tool-registry.d.ts.map +1 -0
  51. package/dist/tool-registry.js +353 -0
  52. package/dist/tool-registry.js.map +1 -0
  53. package/dist/transport/http.d.ts +55 -0
  54. package/dist/transport/http.d.ts.map +1 -0
  55. package/dist/transport/http.js +446 -0
  56. package/dist/transport/http.js.map +1 -0
  57. package/dist/transport/index.d.ts +50 -0
  58. package/dist/transport/index.d.ts.map +1 -0
  59. package/dist/transport/index.js +181 -0
  60. package/dist/transport/index.js.map +1 -0
  61. package/dist/transport/stdio.d.ts +43 -0
  62. package/dist/transport/stdio.d.ts.map +1 -0
  63. package/dist/transport/stdio.js +194 -0
  64. package/dist/transport/stdio.js.map +1 -0
  65. package/dist/transport/websocket.d.ts +65 -0
  66. package/dist/transport/websocket.d.ts.map +1 -0
  67. package/dist/transport/websocket.js +314 -0
  68. package/dist/transport/websocket.js.map +1 -0
  69. package/dist/types.d.ts +473 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +40 -0
  72. package/dist/types.js.map +1 -0
  73. package/package.json +42 -0
  74. package/src/connection-pool.ts +344 -0
  75. package/src/index.ts +253 -0
  76. package/src/oauth.ts +447 -0
  77. package/src/prompt-registry.ts +296 -0
  78. package/src/rate-limiter.ts +266 -0
  79. package/src/resource-registry.ts +530 -0
  80. package/src/sampling.ts +363 -0
  81. package/src/schema-validator.ts +213 -0
  82. package/src/server.ts +1134 -0
  83. package/src/session-manager.ts +339 -0
  84. package/src/task-manager.ts +427 -0
  85. package/src/tool-registry.ts +475 -0
  86. package/src/transport/http.ts +532 -0
  87. package/src/transport/index.ts +233 -0
  88. package/src/transport/stdio.ts +252 -0
  89. package/src/transport/websocket.ts +396 -0
  90. package/src/types.ts +664 -0
  91. package/tsconfig.json +20 -0
  92. package/vitest.config.ts +13 -0
package/src/server.ts ADDED
@@ -0,0 +1,1134 @@
1
+ /**
2
+ * @claude-flow/mcp - MCP Server
3
+ *
4
+ * High-performance MCP server implementation
5
+ */
6
+
7
+ import { EventEmitter } from 'events';
8
+ import { platform, arch } from 'os';
9
+ import type {
10
+ MCPServerConfig,
11
+ MCPRequest,
12
+ MCPResponse,
13
+ MCPNotification,
14
+ MCPSession,
15
+ MCPTool,
16
+ MCPInitializeParams,
17
+ MCPInitializeResult,
18
+ MCPCapabilities,
19
+ MCPProtocolVersion,
20
+ MCPServerMetrics,
21
+ ITransport,
22
+ TransportType,
23
+ ILogger,
24
+ ToolContext,
25
+ } from './types.js';
26
+ import { MCPServerError, ErrorCodes } from './types.js';
27
+ import { ToolRegistry, createToolRegistry } from './tool-registry.js';
28
+ import { SessionManager, createSessionManager } from './session-manager.js';
29
+ import { ConnectionPool, createConnectionPool } from './connection-pool.js';
30
+ import { ResourceRegistry, createResourceRegistry } from './resource-registry.js';
31
+ import { PromptRegistry, createPromptRegistry } from './prompt-registry.js';
32
+ import { TaskManager, createTaskManager } from './task-manager.js';
33
+ import { createTransport, TransportManager, createTransportManager } from './transport/index.js';
34
+ import { RateLimiter, createRateLimiter, type RateLimitConfig } from './rate-limiter.js';
35
+ import { SamplingManager, createSamplingManager, type SamplingConfig, type LLMProvider } from './sampling.js';
36
+
37
+ const DEFAULT_CONFIG: Partial<MCPServerConfig> = {
38
+ name: 'Claude-Flow MCP Server V3',
39
+ version: '3.0.0',
40
+ transport: 'stdio',
41
+ host: 'localhost',
42
+ port: 3000,
43
+ enableMetrics: true,
44
+ enableCaching: true,
45
+ cacheTTL: 10000,
46
+ logLevel: 'info',
47
+ requestTimeout: 30000,
48
+ maxRequestSize: 10 * 1024 * 1024,
49
+ };
50
+
51
+ export interface IMCPServer {
52
+ start(): Promise<void>;
53
+ stop(): Promise<void>;
54
+ registerTool(tool: MCPTool): boolean;
55
+ registerTools(tools: MCPTool[]): { registered: number; failed: string[] };
56
+ getHealthStatus(): Promise<{
57
+ healthy: boolean;
58
+ error?: string;
59
+ metrics?: Record<string, number>;
60
+ }>;
61
+ getMetrics(): MCPServerMetrics;
62
+ getSessions(): MCPSession[];
63
+ getSession(sessionId: string): MCPSession | undefined;
64
+ terminateSession(sessionId: string): boolean;
65
+ }
66
+
67
+ export class MCPServer extends EventEmitter implements IMCPServer {
68
+ private readonly config: MCPServerConfig;
69
+ private readonly toolRegistry: ToolRegistry;
70
+ private readonly sessionManager: SessionManager;
71
+ private readonly resourceRegistry: ResourceRegistry;
72
+ private readonly promptRegistry: PromptRegistry;
73
+ private readonly taskManager: TaskManager;
74
+ private readonly connectionPool?: ConnectionPool;
75
+ private readonly transportManager: TransportManager;
76
+ private readonly rateLimiter: RateLimiter;
77
+ private readonly samplingManager: SamplingManager;
78
+ private transport?: ITransport;
79
+ private running = false;
80
+ private startTime?: Date;
81
+ private startupDuration?: number;
82
+ private currentSession?: MCPSession;
83
+ private resourceSubscriptions: Map<string, Set<string>> = new Map(); // sessionId -> subscribed URIs
84
+
85
+ private readonly serverInfo = {
86
+ name: 'Claude-Flow MCP Server V3',
87
+ version: '3.0.0',
88
+ };
89
+
90
+ // MCP 2025-11-25 protocol version
91
+ private readonly protocolVersion: MCPProtocolVersion = {
92
+ major: 2025,
93
+ minor: 11,
94
+ patch: 25,
95
+ };
96
+
97
+ // Full MCP 2025-11-25 capabilities
98
+ private capabilities: MCPCapabilities = {
99
+ logging: { level: 'info' },
100
+ tools: { listChanged: true },
101
+ resources: { listChanged: true, subscribe: true },
102
+ prompts: { listChanged: true },
103
+ sampling: {},
104
+ };
105
+
106
+ private requestStats = {
107
+ total: 0,
108
+ successful: 0,
109
+ failed: 0,
110
+ totalResponseTime: 0,
111
+ };
112
+
113
+ constructor(
114
+ config: Partial<MCPServerConfig>,
115
+ private readonly logger: ILogger,
116
+ private readonly orchestrator?: unknown,
117
+ private readonly swarmCoordinator?: unknown
118
+ ) {
119
+ super();
120
+ this.config = { ...DEFAULT_CONFIG, ...config } as MCPServerConfig;
121
+
122
+ this.toolRegistry = createToolRegistry(logger);
123
+ this.sessionManager = createSessionManager(logger, {
124
+ maxSessions: 100,
125
+ sessionTimeout: 30 * 60 * 1000,
126
+ });
127
+ this.resourceRegistry = createResourceRegistry(logger, {
128
+ enableSubscriptions: true,
129
+ cacheEnabled: true,
130
+ cacheTTL: 60000,
131
+ });
132
+ this.promptRegistry = createPromptRegistry(logger);
133
+ this.taskManager = createTaskManager(logger, {
134
+ maxConcurrentTasks: 10,
135
+ taskTimeout: 300000,
136
+ });
137
+ this.transportManager = createTransportManager(logger);
138
+ this.rateLimiter = createRateLimiter(logger, {
139
+ requestsPerSecond: 100,
140
+ burstSize: 200,
141
+ perSessionLimit: 50,
142
+ });
143
+ this.samplingManager = createSamplingManager(logger);
144
+
145
+ if (this.config.connectionPool) {
146
+ this.connectionPool = createConnectionPool(
147
+ this.config.connectionPool,
148
+ logger,
149
+ this.config.transport
150
+ );
151
+ }
152
+
153
+ this.setupEventHandlers();
154
+ }
155
+
156
+ /**
157
+ * Get resource registry for external registration
158
+ */
159
+ getResourceRegistry(): ResourceRegistry {
160
+ return this.resourceRegistry;
161
+ }
162
+
163
+ /**
164
+ * Get prompt registry for external registration
165
+ */
166
+ getPromptRegistry(): PromptRegistry {
167
+ return this.promptRegistry;
168
+ }
169
+
170
+ /**
171
+ * Get task manager for async operations
172
+ */
173
+ getTaskManager(): TaskManager {
174
+ return this.taskManager;
175
+ }
176
+
177
+ /**
178
+ * Get rate limiter for configuration
179
+ */
180
+ getRateLimiter(): RateLimiter {
181
+ return this.rateLimiter;
182
+ }
183
+
184
+ /**
185
+ * Get sampling manager for LLM provider registration
186
+ */
187
+ getSamplingManager(): SamplingManager {
188
+ return this.samplingManager;
189
+ }
190
+
191
+ /**
192
+ * Register an LLM provider for sampling
193
+ */
194
+ registerLLMProvider(provider: LLMProvider, isDefault: boolean = false): void {
195
+ this.samplingManager.registerProvider(provider, isDefault);
196
+ }
197
+
198
+ async start(): Promise<void> {
199
+ if (this.running) {
200
+ throw new MCPServerError('Server already running');
201
+ }
202
+
203
+ const startTime = performance.now();
204
+ this.startTime = new Date();
205
+
206
+ this.logger.info('Starting MCP server', {
207
+ name: this.config.name,
208
+ version: this.config.version,
209
+ transport: this.config.transport,
210
+ });
211
+
212
+ try {
213
+ this.transport = createTransport(this.config.transport, this.logger, {
214
+ type: this.config.transport,
215
+ host: this.config.host,
216
+ port: this.config.port,
217
+ corsEnabled: this.config.corsEnabled,
218
+ corsOrigins: this.config.corsOrigins,
219
+ auth: this.config.auth,
220
+ maxRequestSize: String(this.config.maxRequestSize),
221
+ requestTimeout: this.config.requestTimeout,
222
+ } as any);
223
+
224
+ this.transport.onRequest(async (request) => {
225
+ return await this.handleRequest(request);
226
+ });
227
+
228
+ this.transport.onNotification(async (notification) => {
229
+ await this.handleNotification(notification);
230
+ });
231
+
232
+ await this.transport.start();
233
+ await this.registerBuiltInTools();
234
+
235
+ this.running = true;
236
+ this.startupDuration = performance.now() - startTime;
237
+
238
+ this.logger.info('MCP server started', {
239
+ startupTime: `${this.startupDuration.toFixed(2)}ms`,
240
+ tools: this.toolRegistry.getToolCount(),
241
+ });
242
+
243
+ this.emit('server:started', {
244
+ startupTime: this.startupDuration,
245
+ tools: this.toolRegistry.getToolCount(),
246
+ });
247
+
248
+ } catch (error) {
249
+ this.logger.error('Failed to start MCP server', { error });
250
+ throw new MCPServerError('Failed to start server', ErrorCodes.INTERNAL_ERROR, { error });
251
+ }
252
+ }
253
+
254
+ async stop(): Promise<void> {
255
+ if (!this.running) {
256
+ return;
257
+ }
258
+
259
+ this.logger.info('Stopping MCP server');
260
+
261
+ try {
262
+ if (this.transport) {
263
+ await this.transport.stop();
264
+ this.transport = undefined;
265
+ }
266
+
267
+ this.sessionManager.clearAll();
268
+ this.taskManager.destroy();
269
+ this.resourceSubscriptions.clear();
270
+ this.rateLimiter.destroy();
271
+
272
+ if (this.connectionPool) {
273
+ await this.connectionPool.clear();
274
+ }
275
+
276
+ this.running = false;
277
+ this.currentSession = undefined;
278
+
279
+ this.logger.info('MCP server stopped');
280
+ this.emit('server:stopped');
281
+
282
+ } catch (error) {
283
+ this.logger.error('Error stopping MCP server', { error });
284
+ throw error;
285
+ }
286
+ }
287
+
288
+ registerTool(tool: MCPTool): boolean {
289
+ return this.toolRegistry.register(tool);
290
+ }
291
+
292
+ registerTools(tools: MCPTool[]): { registered: number; failed: string[] } {
293
+ return this.toolRegistry.registerBatch(tools);
294
+ }
295
+
296
+ unregisterTool(name: string): boolean {
297
+ return this.toolRegistry.unregister(name);
298
+ }
299
+
300
+ async getHealthStatus(): Promise<{
301
+ healthy: boolean;
302
+ error?: string;
303
+ metrics?: Record<string, number>;
304
+ }> {
305
+ try {
306
+ const transportHealth = this.transport
307
+ ? await this.transport.getHealthStatus()
308
+ : { healthy: false, error: 'Transport not initialized' };
309
+
310
+ const sessionMetrics = this.sessionManager.getSessionMetrics();
311
+ const poolStats = this.connectionPool?.getStats();
312
+
313
+ const metrics: Record<string, number> = {
314
+ registeredTools: this.toolRegistry.getToolCount(),
315
+ totalRequests: this.requestStats.total,
316
+ successfulRequests: this.requestStats.successful,
317
+ failedRequests: this.requestStats.failed,
318
+ totalSessions: sessionMetrics.total,
319
+ activeSessions: sessionMetrics.active,
320
+ ...(transportHealth.metrics || {}),
321
+ };
322
+
323
+ if (poolStats) {
324
+ metrics.poolConnections = poolStats.totalConnections;
325
+ metrics.poolIdleConnections = poolStats.idleConnections;
326
+ metrics.poolBusyConnections = poolStats.busyConnections;
327
+ }
328
+
329
+ return {
330
+ healthy: this.running && transportHealth.healthy,
331
+ error: transportHealth.error,
332
+ metrics,
333
+ };
334
+
335
+ } catch (error) {
336
+ return {
337
+ healthy: false,
338
+ error: error instanceof Error ? error.message : 'Unknown error',
339
+ };
340
+ }
341
+ }
342
+
343
+ getMetrics(): MCPServerMetrics {
344
+ const sessionMetrics = this.sessionManager.getSessionMetrics();
345
+ const registryStats = this.toolRegistry.getStats();
346
+
347
+ return {
348
+ totalRequests: this.requestStats.total,
349
+ successfulRequests: this.requestStats.successful,
350
+ failedRequests: this.requestStats.failed,
351
+ averageResponseTime: this.requestStats.total > 0
352
+ ? this.requestStats.totalResponseTime / this.requestStats.total
353
+ : 0,
354
+ activeSessions: sessionMetrics.active,
355
+ toolInvocations: Object.fromEntries(
356
+ registryStats.topTools.map((t) => [t.name, t.calls])
357
+ ),
358
+ errors: {},
359
+ lastReset: this.startTime || new Date(),
360
+ startupTime: this.startupDuration,
361
+ uptime: this.startTime ? Date.now() - this.startTime.getTime() : 0,
362
+ };
363
+ }
364
+
365
+ getSessions(): MCPSession[] {
366
+ return this.sessionManager.getActiveSessions();
367
+ }
368
+
369
+ getSession(sessionId: string): MCPSession | undefined {
370
+ return this.sessionManager.getSession(sessionId);
371
+ }
372
+
373
+ terminateSession(sessionId: string): boolean {
374
+ const result = this.sessionManager.closeSession(sessionId, 'Terminated by server');
375
+ if (this.currentSession?.id === sessionId) {
376
+ this.currentSession = undefined;
377
+ }
378
+ return result;
379
+ }
380
+
381
+ private async handleRequest(request: MCPRequest): Promise<MCPResponse> {
382
+ const startTime = performance.now();
383
+ this.requestStats.total++;
384
+
385
+ this.logger.debug('Handling request', {
386
+ id: request.id,
387
+ method: request.method,
388
+ });
389
+
390
+ // Rate limiting check (skip for initialize)
391
+ if (request.method !== 'initialize') {
392
+ const sessionId = this.currentSession?.id;
393
+ const rateLimitResult = this.rateLimiter.check(sessionId);
394
+ if (!rateLimitResult.allowed) {
395
+ this.requestStats.failed++;
396
+ return {
397
+ jsonrpc: '2.0',
398
+ id: request.id,
399
+ error: {
400
+ code: -32000,
401
+ message: 'Rate limit exceeded',
402
+ data: { retryAfter: rateLimitResult.retryAfter },
403
+ },
404
+ };
405
+ }
406
+ this.rateLimiter.consume(sessionId);
407
+ }
408
+
409
+ try {
410
+ if (request.method === 'initialize') {
411
+ return await this.handleInitialize(request);
412
+ }
413
+
414
+ const session = this.getOrCreateSession();
415
+
416
+ if (!session.isInitialized && request.method !== 'initialized') {
417
+ return this.createErrorResponse(
418
+ request.id,
419
+ ErrorCodes.SERVER_NOT_INITIALIZED,
420
+ 'Server not initialized'
421
+ );
422
+ }
423
+
424
+ this.sessionManager.updateActivity(session.id);
425
+
426
+ const response = await this.routeRequest(request);
427
+
428
+ const duration = performance.now() - startTime;
429
+ this.requestStats.successful++;
430
+ this.requestStats.totalResponseTime += duration;
431
+
432
+ this.logger.debug('Request completed', {
433
+ id: request.id,
434
+ method: request.method,
435
+ duration: `${duration.toFixed(2)}ms`,
436
+ });
437
+
438
+ return response;
439
+
440
+ } catch (error) {
441
+ const duration = performance.now() - startTime;
442
+ this.requestStats.failed++;
443
+ this.requestStats.totalResponseTime += duration;
444
+
445
+ this.logger.error('Request failed', {
446
+ id: request.id,
447
+ method: request.method,
448
+ error,
449
+ });
450
+
451
+ return this.createErrorResponse(
452
+ request.id,
453
+ ErrorCodes.INTERNAL_ERROR,
454
+ error instanceof Error ? error.message : 'Internal error'
455
+ );
456
+ }
457
+ }
458
+
459
+ private async handleNotification(notification: MCPNotification): Promise<void> {
460
+ this.logger.debug('Handling notification', { method: notification.method });
461
+
462
+ switch (notification.method) {
463
+ case 'initialized':
464
+ this.logger.info('Client initialized notification received');
465
+ break;
466
+
467
+ case 'notifications/cancelled':
468
+ this.logger.debug('Request cancelled', notification.params);
469
+ break;
470
+
471
+ default:
472
+ this.logger.debug('Unknown notification', { method: notification.method });
473
+ }
474
+ }
475
+
476
+ private async handleInitialize(request: MCPRequest): Promise<MCPResponse> {
477
+ const params = request.params as unknown as MCPInitializeParams | undefined;
478
+
479
+ if (!params) {
480
+ return this.createErrorResponse(
481
+ request.id,
482
+ ErrorCodes.INVALID_PARAMS,
483
+ 'Invalid params'
484
+ );
485
+ }
486
+
487
+ const session = this.sessionManager.createSession(this.config.transport);
488
+ this.sessionManager.initializeSession(session.id, params);
489
+ this.currentSession = session;
490
+
491
+ const result: MCPInitializeResult = {
492
+ protocolVersion: this.protocolVersion,
493
+ capabilities: this.capabilities,
494
+ serverInfo: this.serverInfo,
495
+ instructions: 'Claude-Flow MCP Server V3 ready for tool execution',
496
+ };
497
+
498
+ this.logger.info('Session initialized', {
499
+ sessionId: session.id,
500
+ clientInfo: params.clientInfo,
501
+ });
502
+
503
+ return {
504
+ jsonrpc: '2.0',
505
+ id: request.id,
506
+ result,
507
+ };
508
+ }
509
+
510
+ private async routeRequest(request: MCPRequest): Promise<MCPResponse> {
511
+ switch (request.method) {
512
+ // Tool methods
513
+ case 'tools/list':
514
+ return this.handleToolsList(request);
515
+ case 'tools/call':
516
+ return this.handleToolsCall(request);
517
+
518
+ // Resource methods (MCP 2025-11-25)
519
+ case 'resources/list':
520
+ return this.handleResourcesList(request);
521
+ case 'resources/read':
522
+ return this.handleResourcesRead(request);
523
+ case 'resources/subscribe':
524
+ return this.handleResourcesSubscribe(request);
525
+ case 'resources/unsubscribe':
526
+ return this.handleResourcesUnsubscribe(request);
527
+
528
+ // Prompt methods (MCP 2025-11-25)
529
+ case 'prompts/list':
530
+ return this.handlePromptsList(request);
531
+ case 'prompts/get':
532
+ return this.handlePromptsGet(request);
533
+
534
+ // Task methods (MCP 2025-11-25)
535
+ case 'tasks/status':
536
+ return this.handleTasksStatus(request);
537
+ case 'tasks/cancel':
538
+ return this.handleTasksCancel(request);
539
+
540
+ // Completion (MCP 2025-11-25)
541
+ case 'completion/complete':
542
+ return this.handleCompletion(request);
543
+
544
+ // Logging (MCP 2025-11-25)
545
+ case 'logging/setLevel':
546
+ return this.handleLoggingSetLevel(request);
547
+
548
+ // Sampling (MCP 2025-11-25)
549
+ case 'sampling/createMessage':
550
+ return this.handleSamplingCreateMessage(request);
551
+
552
+ // Utility
553
+ case 'ping':
554
+ return {
555
+ jsonrpc: '2.0',
556
+ id: request.id,
557
+ result: { pong: true, timestamp: Date.now() },
558
+ };
559
+
560
+ default:
561
+ // Check if it's a direct tool call
562
+ if (this.toolRegistry.hasTool(request.method)) {
563
+ return this.handleToolExecution(request);
564
+ }
565
+
566
+ return this.createErrorResponse(
567
+ request.id,
568
+ ErrorCodes.METHOD_NOT_FOUND,
569
+ `Method not found: ${request.method}`
570
+ );
571
+ }
572
+ }
573
+
574
+ private handleToolsList(request: MCPRequest): MCPResponse {
575
+ const tools = this.toolRegistry.listTools().map((t) => ({
576
+ name: t.name,
577
+ description: t.description,
578
+ inputSchema: this.toolRegistry.getTool(t.name)?.inputSchema,
579
+ }));
580
+
581
+ return {
582
+ jsonrpc: '2.0',
583
+ id: request.id,
584
+ result: { tools },
585
+ };
586
+ }
587
+
588
+ private async handleToolsCall(request: MCPRequest): Promise<MCPResponse> {
589
+ const params = request.params as { name: string; arguments?: Record<string, unknown> };
590
+
591
+ if (!params?.name) {
592
+ return this.createErrorResponse(
593
+ request.id,
594
+ ErrorCodes.INVALID_PARAMS,
595
+ 'Tool name is required'
596
+ );
597
+ }
598
+
599
+ const context: ToolContext = {
600
+ sessionId: this.currentSession?.id || 'unknown',
601
+ requestId: request.id,
602
+ orchestrator: this.orchestrator,
603
+ swarmCoordinator: this.swarmCoordinator,
604
+ };
605
+
606
+ const result = await this.toolRegistry.execute(
607
+ params.name,
608
+ params.arguments || {},
609
+ context
610
+ );
611
+
612
+ return {
613
+ jsonrpc: '2.0',
614
+ id: request.id,
615
+ result,
616
+ };
617
+ }
618
+
619
+ private async handleToolExecution(request: MCPRequest): Promise<MCPResponse> {
620
+ const context: ToolContext = {
621
+ sessionId: this.currentSession?.id || 'unknown',
622
+ requestId: request.id,
623
+ orchestrator: this.orchestrator,
624
+ swarmCoordinator: this.swarmCoordinator,
625
+ };
626
+
627
+ const result = await this.toolRegistry.execute(
628
+ request.method,
629
+ (request.params as Record<string, unknown>) || {},
630
+ context
631
+ );
632
+
633
+ return {
634
+ jsonrpc: '2.0',
635
+ id: request.id,
636
+ result,
637
+ };
638
+ }
639
+
640
+ // ============================================================================
641
+ // Resource Handlers (MCP 2025-11-25)
642
+ // ============================================================================
643
+
644
+ private handleResourcesList(request: MCPRequest): MCPResponse {
645
+ const params = request.params as { cursor?: string } | undefined;
646
+ const result = this.resourceRegistry.list(params?.cursor);
647
+
648
+ return {
649
+ jsonrpc: '2.0',
650
+ id: request.id,
651
+ result,
652
+ };
653
+ }
654
+
655
+ private async handleResourcesRead(request: MCPRequest): Promise<MCPResponse> {
656
+ const params = request.params as { uri: string } | undefined;
657
+
658
+ if (!params?.uri) {
659
+ return this.createErrorResponse(
660
+ request.id,
661
+ ErrorCodes.INVALID_PARAMS,
662
+ 'Resource URI is required'
663
+ );
664
+ }
665
+
666
+ try {
667
+ const result = await this.resourceRegistry.read(params.uri);
668
+ return {
669
+ jsonrpc: '2.0',
670
+ id: request.id,
671
+ result,
672
+ };
673
+ } catch (error) {
674
+ return this.createErrorResponse(
675
+ request.id,
676
+ ErrorCodes.INVALID_PARAMS,
677
+ error instanceof Error ? error.message : 'Resource read failed'
678
+ );
679
+ }
680
+ }
681
+
682
+ private handleResourcesSubscribe(request: MCPRequest): MCPResponse {
683
+ const params = request.params as { uri: string } | undefined;
684
+ const sessionId = this.currentSession?.id;
685
+
686
+ if (!params?.uri) {
687
+ return this.createErrorResponse(
688
+ request.id,
689
+ ErrorCodes.INVALID_PARAMS,
690
+ 'Resource URI is required'
691
+ );
692
+ }
693
+
694
+ if (!sessionId) {
695
+ return this.createErrorResponse(
696
+ request.id,
697
+ ErrorCodes.SERVER_NOT_INITIALIZED,
698
+ 'No active session'
699
+ );
700
+ }
701
+
702
+ try {
703
+ // Track subscription for this session
704
+ let sessionSubs = this.resourceSubscriptions.get(sessionId);
705
+ if (!sessionSubs) {
706
+ sessionSubs = new Set();
707
+ this.resourceSubscriptions.set(sessionId, sessionSubs);
708
+ }
709
+
710
+ const subscriptionId = this.resourceRegistry.subscribe(params.uri, (uri, content) => {
711
+ // Send notification when resource updates
712
+ this.sendNotification('notifications/resources/updated', { uri });
713
+ });
714
+
715
+ sessionSubs.add(params.uri);
716
+
717
+ return {
718
+ jsonrpc: '2.0',
719
+ id: request.id,
720
+ result: { subscriptionId },
721
+ };
722
+ } catch (error) {
723
+ return this.createErrorResponse(
724
+ request.id,
725
+ ErrorCodes.INTERNAL_ERROR,
726
+ error instanceof Error ? error.message : 'Subscription failed'
727
+ );
728
+ }
729
+ }
730
+
731
+ private handleResourcesUnsubscribe(request: MCPRequest): MCPResponse {
732
+ const params = request.params as { uri: string } | undefined;
733
+ const sessionId = this.currentSession?.id;
734
+
735
+ if (!params?.uri) {
736
+ return this.createErrorResponse(
737
+ request.id,
738
+ ErrorCodes.INVALID_PARAMS,
739
+ 'Resource URI is required'
740
+ );
741
+ }
742
+
743
+ if (sessionId) {
744
+ const sessionSubs = this.resourceSubscriptions.get(sessionId);
745
+ if (sessionSubs) {
746
+ sessionSubs.delete(params.uri);
747
+ }
748
+ }
749
+
750
+ return {
751
+ jsonrpc: '2.0',
752
+ id: request.id,
753
+ result: { success: true },
754
+ };
755
+ }
756
+
757
+ // ============================================================================
758
+ // Prompt Handlers (MCP 2025-11-25)
759
+ // ============================================================================
760
+
761
+ private handlePromptsList(request: MCPRequest): MCPResponse {
762
+ const params = request.params as { cursor?: string } | undefined;
763
+ const result = this.promptRegistry.list(params?.cursor);
764
+
765
+ return {
766
+ jsonrpc: '2.0',
767
+ id: request.id,
768
+ result,
769
+ };
770
+ }
771
+
772
+ private async handlePromptsGet(request: MCPRequest): Promise<MCPResponse> {
773
+ const params = request.params as { name: string; arguments?: Record<string, string> } | undefined;
774
+
775
+ if (!params?.name) {
776
+ return this.createErrorResponse(
777
+ request.id,
778
+ ErrorCodes.INVALID_PARAMS,
779
+ 'Prompt name is required'
780
+ );
781
+ }
782
+
783
+ try {
784
+ const result = await this.promptRegistry.get(params.name, params.arguments);
785
+ return {
786
+ jsonrpc: '2.0',
787
+ id: request.id,
788
+ result,
789
+ };
790
+ } catch (error) {
791
+ return this.createErrorResponse(
792
+ request.id,
793
+ ErrorCodes.INVALID_PARAMS,
794
+ error instanceof Error ? error.message : 'Prompt get failed'
795
+ );
796
+ }
797
+ }
798
+
799
+ // ============================================================================
800
+ // Task Handlers (MCP 2025-11-25)
801
+ // ============================================================================
802
+
803
+ private handleTasksStatus(request: MCPRequest): MCPResponse {
804
+ const params = request.params as { taskId?: string } | undefined;
805
+
806
+ if (params?.taskId) {
807
+ const task = this.taskManager.getTask(params.taskId);
808
+ if (!task) {
809
+ return this.createErrorResponse(
810
+ request.id,
811
+ ErrorCodes.INVALID_PARAMS,
812
+ `Task not found: ${params.taskId}`
813
+ );
814
+ }
815
+ return {
816
+ jsonrpc: '2.0',
817
+ id: request.id,
818
+ result: task,
819
+ };
820
+ }
821
+
822
+ // Return all tasks
823
+ return {
824
+ jsonrpc: '2.0',
825
+ id: request.id,
826
+ result: { tasks: this.taskManager.getAllTasks() },
827
+ };
828
+ }
829
+
830
+ private handleTasksCancel(request: MCPRequest): MCPResponse {
831
+ const params = request.params as { taskId: string; reason?: string } | undefined;
832
+
833
+ if (!params?.taskId) {
834
+ return this.createErrorResponse(
835
+ request.id,
836
+ ErrorCodes.INVALID_PARAMS,
837
+ 'Task ID is required'
838
+ );
839
+ }
840
+
841
+ const success = this.taskManager.cancelTask(params.taskId, params.reason);
842
+
843
+ return {
844
+ jsonrpc: '2.0',
845
+ id: request.id,
846
+ result: { success },
847
+ };
848
+ }
849
+
850
+ // ============================================================================
851
+ // Completion Handler (MCP 2025-11-25)
852
+ // ============================================================================
853
+
854
+ private handleCompletion(request: MCPRequest): MCPResponse {
855
+ const params = request.params as {
856
+ ref: { type: string; name?: string; uri?: string };
857
+ argument: { name: string; value: string };
858
+ } | undefined;
859
+
860
+ if (!params?.ref || !params?.argument) {
861
+ return this.createErrorResponse(
862
+ request.id,
863
+ ErrorCodes.INVALID_PARAMS,
864
+ 'Completion reference and argument are required'
865
+ );
866
+ }
867
+
868
+ // Basic completion implementation - can be extended
869
+ const completions: string[] = [];
870
+
871
+ if (params.ref.type === 'ref/prompt') {
872
+ // Get prompt argument completions
873
+ const prompt = this.promptRegistry.getPrompt(params.ref.name || '');
874
+ if (prompt?.arguments) {
875
+ for (const arg of prompt.arguments) {
876
+ if (arg.name === params.argument.name) {
877
+ // Could add domain-specific completions here
878
+ }
879
+ }
880
+ }
881
+ } else if (params.ref.type === 'ref/resource') {
882
+ // Get resource URI completions
883
+ const { resources } = this.resourceRegistry.list();
884
+ for (const resource of resources) {
885
+ if (resource.uri.startsWith(params.argument.value)) {
886
+ completions.push(resource.uri);
887
+ }
888
+ }
889
+ }
890
+
891
+ return {
892
+ jsonrpc: '2.0',
893
+ id: request.id,
894
+ result: {
895
+ completion: {
896
+ values: completions.slice(0, 10),
897
+ total: completions.length,
898
+ hasMore: completions.length > 10,
899
+ },
900
+ },
901
+ };
902
+ }
903
+
904
+ // ============================================================================
905
+ // Logging Handler (MCP 2025-11-25)
906
+ // ============================================================================
907
+
908
+ private handleLoggingSetLevel(request: MCPRequest): MCPResponse {
909
+ const params = request.params as { level: string } | undefined;
910
+
911
+ if (!params?.level) {
912
+ return this.createErrorResponse(
913
+ request.id,
914
+ ErrorCodes.INVALID_PARAMS,
915
+ 'Log level is required'
916
+ );
917
+ }
918
+
919
+ // Update capabilities
920
+ this.capabilities.logging = { level: params.level as 'debug' | 'info' | 'warn' | 'error' };
921
+
922
+ this.logger.info('Log level updated', { level: params.level });
923
+
924
+ return {
925
+ jsonrpc: '2.0',
926
+ id: request.id,
927
+ result: { success: true },
928
+ };
929
+ }
930
+
931
+ // ============================================================================
932
+ // Sampling Handler (MCP 2025-11-25)
933
+ // ============================================================================
934
+
935
+ private async handleSamplingCreateMessage(request: MCPRequest): Promise<MCPResponse> {
936
+ const params = request.params as {
937
+ messages: Array<{ role: string; content: { type: string; text?: string } }>;
938
+ maxTokens: number;
939
+ systemPrompt?: string;
940
+ modelPreferences?: { hints?: Array<{ name?: string }>; intelligencePriority?: number; speedPriority?: number; costPriority?: number };
941
+ includeContext?: string;
942
+ temperature?: number;
943
+ stopSequences?: string[];
944
+ metadata?: Record<string, unknown>;
945
+ } | undefined;
946
+
947
+ if (!params?.messages || !params?.maxTokens) {
948
+ return this.createErrorResponse(
949
+ request.id,
950
+ ErrorCodes.INVALID_PARAMS,
951
+ 'messages and maxTokens are required'
952
+ );
953
+ }
954
+
955
+ // Check if sampling is available
956
+ const available = await this.samplingManager.isAvailable();
957
+ if (!available) {
958
+ return this.createErrorResponse(
959
+ request.id,
960
+ ErrorCodes.INTERNAL_ERROR,
961
+ 'No LLM provider available for sampling'
962
+ );
963
+ }
964
+
965
+ try {
966
+ const result = await this.samplingManager.createMessage(
967
+ {
968
+ messages: params.messages.map((m) => ({
969
+ role: m.role as 'user' | 'assistant',
970
+ content: m.content as any,
971
+ })),
972
+ maxTokens: params.maxTokens,
973
+ systemPrompt: params.systemPrompt,
974
+ modelPreferences: params.modelPreferences,
975
+ includeContext: params.includeContext as 'none' | 'thisServer' | 'allServers' | undefined,
976
+ temperature: params.temperature,
977
+ stopSequences: params.stopSequences,
978
+ metadata: params.metadata,
979
+ },
980
+ {
981
+ sessionId: this.currentSession?.id || 'unknown',
982
+ serverId: this.serverInfo.name,
983
+ }
984
+ );
985
+
986
+ return {
987
+ jsonrpc: '2.0',
988
+ id: request.id,
989
+ result,
990
+ };
991
+ } catch (error) {
992
+ return this.createErrorResponse(
993
+ request.id,
994
+ ErrorCodes.INTERNAL_ERROR,
995
+ error instanceof Error ? error.message : 'Sampling failed'
996
+ );
997
+ }
998
+ }
999
+
1000
+ // ============================================================================
1001
+ // Notification Sender
1002
+ // ============================================================================
1003
+
1004
+ private async sendNotification(method: string, params?: Record<string, unknown>): Promise<void> {
1005
+ if (this.transport?.sendNotification) {
1006
+ await this.transport.sendNotification({
1007
+ jsonrpc: '2.0',
1008
+ method,
1009
+ params,
1010
+ });
1011
+ }
1012
+ }
1013
+
1014
+ private getOrCreateSession(): MCPSession {
1015
+ if (this.currentSession) {
1016
+ return this.currentSession;
1017
+ }
1018
+
1019
+ const session = this.sessionManager.createSession(this.config.transport);
1020
+ this.currentSession = session;
1021
+ return session;
1022
+ }
1023
+
1024
+ private createErrorResponse(
1025
+ id: string | number | null,
1026
+ code: number,
1027
+ message: string
1028
+ ): MCPResponse {
1029
+ return {
1030
+ jsonrpc: '2.0',
1031
+ id,
1032
+ error: { code, message },
1033
+ };
1034
+ }
1035
+
1036
+ private async registerBuiltInTools(): Promise<void> {
1037
+ this.registerTool({
1038
+ name: 'system/info',
1039
+ description: 'Get system information',
1040
+ inputSchema: { type: 'object', properties: {} },
1041
+ handler: async () => ({
1042
+ name: this.serverInfo.name,
1043
+ version: this.serverInfo.version,
1044
+ platform: platform(),
1045
+ arch: arch(),
1046
+ runtime: 'Node.js',
1047
+ uptime: this.startTime ? Date.now() - this.startTime.getTime() : 0,
1048
+ }),
1049
+ category: 'system',
1050
+ });
1051
+
1052
+ this.registerTool({
1053
+ name: 'system/health',
1054
+ description: 'Get system health status',
1055
+ inputSchema: { type: 'object', properties: {} },
1056
+ handler: async () => await this.getHealthStatus(),
1057
+ category: 'system',
1058
+ cacheable: true,
1059
+ cacheTTL: 2000,
1060
+ });
1061
+
1062
+ this.registerTool({
1063
+ name: 'system/metrics',
1064
+ description: 'Get server metrics',
1065
+ inputSchema: { type: 'object', properties: {} },
1066
+ handler: async () => this.getMetrics(),
1067
+ category: 'system',
1068
+ cacheable: true,
1069
+ cacheTTL: 1000,
1070
+ });
1071
+
1072
+ this.registerTool({
1073
+ name: 'tools/list-detailed',
1074
+ description: 'List all registered tools with details',
1075
+ inputSchema: {
1076
+ type: 'object',
1077
+ properties: {
1078
+ category: { type: 'string', description: 'Filter by category' },
1079
+ },
1080
+ },
1081
+ handler: async (input: unknown) => {
1082
+ const params = input as { category?: string };
1083
+ if (params.category) {
1084
+ return this.toolRegistry.getByCategory(params.category);
1085
+ }
1086
+ return this.toolRegistry.listTools();
1087
+ },
1088
+ category: 'system',
1089
+ });
1090
+
1091
+ this.logger.info('Built-in tools registered', {
1092
+ count: 4,
1093
+ });
1094
+ }
1095
+
1096
+ private setupEventHandlers(): void {
1097
+ this.toolRegistry.on('tool:registered', (name) => {
1098
+ this.emit('tool:registered', name);
1099
+ });
1100
+
1101
+ this.toolRegistry.on('tool:called', (data) => {
1102
+ this.emit('tool:called', data);
1103
+ });
1104
+
1105
+ this.toolRegistry.on('tool:completed', (data) => {
1106
+ this.emit('tool:completed', data);
1107
+ });
1108
+
1109
+ this.toolRegistry.on('tool:error', (data) => {
1110
+ this.emit('tool:error', data);
1111
+ });
1112
+
1113
+ this.sessionManager.on('session:created', (session) => {
1114
+ this.emit('session:created', session);
1115
+ });
1116
+
1117
+ this.sessionManager.on('session:closed', (data) => {
1118
+ this.emit('session:closed', data);
1119
+ });
1120
+
1121
+ this.sessionManager.on('session:expired', (session) => {
1122
+ this.emit('session:expired', session);
1123
+ });
1124
+ }
1125
+ }
1126
+
1127
+ export function createMCPServer(
1128
+ config: Partial<MCPServerConfig>,
1129
+ logger: ILogger,
1130
+ orchestrator?: unknown,
1131
+ swarmCoordinator?: unknown
1132
+ ): MCPServer {
1133
+ return new MCPServer(config, logger, orchestrator, swarmCoordinator);
1134
+ }