@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.
- package/.agentic-flow/intelligence.json +16 -0
- package/README.md +428 -0
- package/__tests__/integration.test.ts +449 -0
- package/__tests__/mcp.test.ts +641 -0
- package/dist/connection-pool.d.ts +36 -0
- package/dist/connection-pool.d.ts.map +1 -0
- package/dist/connection-pool.js +273 -0
- package/dist/connection-pool.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth.d.ts +146 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +318 -0
- package/dist/oauth.js.map +1 -0
- package/dist/prompt-registry.d.ts +90 -0
- package/dist/prompt-registry.d.ts.map +1 -0
- package/dist/prompt-registry.js +209 -0
- package/dist/prompt-registry.js.map +1 -0
- package/dist/rate-limiter.d.ts +86 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +197 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/resource-registry.d.ts +144 -0
- package/dist/resource-registry.d.ts.map +1 -0
- package/dist/resource-registry.js +405 -0
- package/dist/resource-registry.js.map +1 -0
- package/dist/sampling.d.ts +102 -0
- package/dist/sampling.d.ts.map +1 -0
- package/dist/sampling.js +268 -0
- package/dist/sampling.js.map +1 -0
- package/dist/schema-validator.d.ts +30 -0
- package/dist/schema-validator.d.ts.map +1 -0
- package/dist/schema-validator.js +182 -0
- package/dist/schema-validator.js.map +1 -0
- package/dist/server.d.ts +122 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +829 -0
- package/dist/server.js.map +1 -0
- package/dist/session-manager.d.ts +55 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +252 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/task-manager.d.ts +81 -0
- package/dist/task-manager.d.ts.map +1 -0
- package/dist/task-manager.js +337 -0
- package/dist/task-manager.js.map +1 -0
- package/dist/tool-registry.d.ts +88 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +353 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/transport/http.d.ts +55 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +446 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/index.d.ts +50 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +181 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/stdio.d.ts +43 -0
- package/dist/transport/stdio.d.ts.map +1 -0
- package/dist/transport/stdio.js +194 -0
- package/dist/transport/stdio.js.map +1 -0
- package/dist/transport/websocket.d.ts +65 -0
- package/dist/transport/websocket.d.ts.map +1 -0
- package/dist/transport/websocket.js +314 -0
- package/dist/transport/websocket.js.map +1 -0
- package/dist/types.d.ts +473 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +40 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -0
- package/src/connection-pool.ts +344 -0
- package/src/index.ts +253 -0
- package/src/oauth.ts +447 -0
- package/src/prompt-registry.ts +296 -0
- package/src/rate-limiter.ts +266 -0
- package/src/resource-registry.ts +530 -0
- package/src/sampling.ts +363 -0
- package/src/schema-validator.ts +213 -0
- package/src/server.ts +1134 -0
- package/src/session-manager.ts +339 -0
- package/src/task-manager.ts +427 -0
- package/src/tool-registry.ts +475 -0
- package/src/transport/http.ts +532 -0
- package/src/transport/index.ts +233 -0
- package/src/transport/stdio.ts +252 -0
- package/src/transport/websocket.ts +396 -0
- package/src/types.ts +664 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +13 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @claude-flow/mcp - MCP Server
|
|
3
|
+
*
|
|
4
|
+
* High-performance MCP server implementation
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { platform, arch } from 'os';
|
|
8
|
+
import { MCPServerError, ErrorCodes } from './types.js';
|
|
9
|
+
import { createToolRegistry } from './tool-registry.js';
|
|
10
|
+
import { createSessionManager } from './session-manager.js';
|
|
11
|
+
import { createConnectionPool } from './connection-pool.js';
|
|
12
|
+
import { createResourceRegistry } from './resource-registry.js';
|
|
13
|
+
import { createPromptRegistry } from './prompt-registry.js';
|
|
14
|
+
import { createTaskManager } from './task-manager.js';
|
|
15
|
+
import { createTransport, createTransportManager } from './transport/index.js';
|
|
16
|
+
import { createRateLimiter } from './rate-limiter.js';
|
|
17
|
+
import { createSamplingManager } from './sampling.js';
|
|
18
|
+
const DEFAULT_CONFIG = {
|
|
19
|
+
name: 'Claude-Flow MCP Server V3',
|
|
20
|
+
version: '3.0.0',
|
|
21
|
+
transport: 'stdio',
|
|
22
|
+
host: 'localhost',
|
|
23
|
+
port: 3000,
|
|
24
|
+
enableMetrics: true,
|
|
25
|
+
enableCaching: true,
|
|
26
|
+
cacheTTL: 10000,
|
|
27
|
+
logLevel: 'info',
|
|
28
|
+
requestTimeout: 30000,
|
|
29
|
+
maxRequestSize: 10 * 1024 * 1024,
|
|
30
|
+
};
|
|
31
|
+
export class MCPServer extends EventEmitter {
|
|
32
|
+
logger;
|
|
33
|
+
orchestrator;
|
|
34
|
+
swarmCoordinator;
|
|
35
|
+
config;
|
|
36
|
+
toolRegistry;
|
|
37
|
+
sessionManager;
|
|
38
|
+
resourceRegistry;
|
|
39
|
+
promptRegistry;
|
|
40
|
+
taskManager;
|
|
41
|
+
connectionPool;
|
|
42
|
+
transportManager;
|
|
43
|
+
rateLimiter;
|
|
44
|
+
samplingManager;
|
|
45
|
+
transport;
|
|
46
|
+
running = false;
|
|
47
|
+
startTime;
|
|
48
|
+
startupDuration;
|
|
49
|
+
currentSession;
|
|
50
|
+
resourceSubscriptions = new Map(); // sessionId -> subscribed URIs
|
|
51
|
+
serverInfo = {
|
|
52
|
+
name: 'Claude-Flow MCP Server V3',
|
|
53
|
+
version: '3.0.0',
|
|
54
|
+
};
|
|
55
|
+
// MCP 2025-11-25 protocol version
|
|
56
|
+
protocolVersion = {
|
|
57
|
+
major: 2025,
|
|
58
|
+
minor: 11,
|
|
59
|
+
patch: 25,
|
|
60
|
+
};
|
|
61
|
+
// Full MCP 2025-11-25 capabilities
|
|
62
|
+
capabilities = {
|
|
63
|
+
logging: { level: 'info' },
|
|
64
|
+
tools: { listChanged: true },
|
|
65
|
+
resources: { listChanged: true, subscribe: true },
|
|
66
|
+
prompts: { listChanged: true },
|
|
67
|
+
sampling: {},
|
|
68
|
+
};
|
|
69
|
+
requestStats = {
|
|
70
|
+
total: 0,
|
|
71
|
+
successful: 0,
|
|
72
|
+
failed: 0,
|
|
73
|
+
totalResponseTime: 0,
|
|
74
|
+
};
|
|
75
|
+
constructor(config, logger, orchestrator, swarmCoordinator) {
|
|
76
|
+
super();
|
|
77
|
+
this.logger = logger;
|
|
78
|
+
this.orchestrator = orchestrator;
|
|
79
|
+
this.swarmCoordinator = swarmCoordinator;
|
|
80
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
81
|
+
this.toolRegistry = createToolRegistry(logger);
|
|
82
|
+
this.sessionManager = createSessionManager(logger, {
|
|
83
|
+
maxSessions: 100,
|
|
84
|
+
sessionTimeout: 30 * 60 * 1000,
|
|
85
|
+
});
|
|
86
|
+
this.resourceRegistry = createResourceRegistry(logger, {
|
|
87
|
+
enableSubscriptions: true,
|
|
88
|
+
cacheEnabled: true,
|
|
89
|
+
cacheTTL: 60000,
|
|
90
|
+
});
|
|
91
|
+
this.promptRegistry = createPromptRegistry(logger);
|
|
92
|
+
this.taskManager = createTaskManager(logger, {
|
|
93
|
+
maxConcurrentTasks: 10,
|
|
94
|
+
taskTimeout: 300000,
|
|
95
|
+
});
|
|
96
|
+
this.transportManager = createTransportManager(logger);
|
|
97
|
+
this.rateLimiter = createRateLimiter(logger, {
|
|
98
|
+
requestsPerSecond: 100,
|
|
99
|
+
burstSize: 200,
|
|
100
|
+
perSessionLimit: 50,
|
|
101
|
+
});
|
|
102
|
+
this.samplingManager = createSamplingManager(logger);
|
|
103
|
+
if (this.config.connectionPool) {
|
|
104
|
+
this.connectionPool = createConnectionPool(this.config.connectionPool, logger, this.config.transport);
|
|
105
|
+
}
|
|
106
|
+
this.setupEventHandlers();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get resource registry for external registration
|
|
110
|
+
*/
|
|
111
|
+
getResourceRegistry() {
|
|
112
|
+
return this.resourceRegistry;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get prompt registry for external registration
|
|
116
|
+
*/
|
|
117
|
+
getPromptRegistry() {
|
|
118
|
+
return this.promptRegistry;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get task manager for async operations
|
|
122
|
+
*/
|
|
123
|
+
getTaskManager() {
|
|
124
|
+
return this.taskManager;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get rate limiter for configuration
|
|
128
|
+
*/
|
|
129
|
+
getRateLimiter() {
|
|
130
|
+
return this.rateLimiter;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get sampling manager for LLM provider registration
|
|
134
|
+
*/
|
|
135
|
+
getSamplingManager() {
|
|
136
|
+
return this.samplingManager;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Register an LLM provider for sampling
|
|
140
|
+
*/
|
|
141
|
+
registerLLMProvider(provider, isDefault = false) {
|
|
142
|
+
this.samplingManager.registerProvider(provider, isDefault);
|
|
143
|
+
}
|
|
144
|
+
async start() {
|
|
145
|
+
if (this.running) {
|
|
146
|
+
throw new MCPServerError('Server already running');
|
|
147
|
+
}
|
|
148
|
+
const startTime = performance.now();
|
|
149
|
+
this.startTime = new Date();
|
|
150
|
+
this.logger.info('Starting MCP server', {
|
|
151
|
+
name: this.config.name,
|
|
152
|
+
version: this.config.version,
|
|
153
|
+
transport: this.config.transport,
|
|
154
|
+
});
|
|
155
|
+
try {
|
|
156
|
+
this.transport = createTransport(this.config.transport, this.logger, {
|
|
157
|
+
type: this.config.transport,
|
|
158
|
+
host: this.config.host,
|
|
159
|
+
port: this.config.port,
|
|
160
|
+
corsEnabled: this.config.corsEnabled,
|
|
161
|
+
corsOrigins: this.config.corsOrigins,
|
|
162
|
+
auth: this.config.auth,
|
|
163
|
+
maxRequestSize: String(this.config.maxRequestSize),
|
|
164
|
+
requestTimeout: this.config.requestTimeout,
|
|
165
|
+
});
|
|
166
|
+
this.transport.onRequest(async (request) => {
|
|
167
|
+
return await this.handleRequest(request);
|
|
168
|
+
});
|
|
169
|
+
this.transport.onNotification(async (notification) => {
|
|
170
|
+
await this.handleNotification(notification);
|
|
171
|
+
});
|
|
172
|
+
await this.transport.start();
|
|
173
|
+
await this.registerBuiltInTools();
|
|
174
|
+
this.running = true;
|
|
175
|
+
this.startupDuration = performance.now() - startTime;
|
|
176
|
+
this.logger.info('MCP server started', {
|
|
177
|
+
startupTime: `${this.startupDuration.toFixed(2)}ms`,
|
|
178
|
+
tools: this.toolRegistry.getToolCount(),
|
|
179
|
+
});
|
|
180
|
+
this.emit('server:started', {
|
|
181
|
+
startupTime: this.startupDuration,
|
|
182
|
+
tools: this.toolRegistry.getToolCount(),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
this.logger.error('Failed to start MCP server', { error });
|
|
187
|
+
throw new MCPServerError('Failed to start server', ErrorCodes.INTERNAL_ERROR, { error });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async stop() {
|
|
191
|
+
if (!this.running) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.logger.info('Stopping MCP server');
|
|
195
|
+
try {
|
|
196
|
+
if (this.transport) {
|
|
197
|
+
await this.transport.stop();
|
|
198
|
+
this.transport = undefined;
|
|
199
|
+
}
|
|
200
|
+
this.sessionManager.clearAll();
|
|
201
|
+
this.taskManager.destroy();
|
|
202
|
+
this.resourceSubscriptions.clear();
|
|
203
|
+
this.rateLimiter.destroy();
|
|
204
|
+
if (this.connectionPool) {
|
|
205
|
+
await this.connectionPool.clear();
|
|
206
|
+
}
|
|
207
|
+
this.running = false;
|
|
208
|
+
this.currentSession = undefined;
|
|
209
|
+
this.logger.info('MCP server stopped');
|
|
210
|
+
this.emit('server:stopped');
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.logger.error('Error stopping MCP server', { error });
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
registerTool(tool) {
|
|
218
|
+
return this.toolRegistry.register(tool);
|
|
219
|
+
}
|
|
220
|
+
registerTools(tools) {
|
|
221
|
+
return this.toolRegistry.registerBatch(tools);
|
|
222
|
+
}
|
|
223
|
+
unregisterTool(name) {
|
|
224
|
+
return this.toolRegistry.unregister(name);
|
|
225
|
+
}
|
|
226
|
+
async getHealthStatus() {
|
|
227
|
+
try {
|
|
228
|
+
const transportHealth = this.transport
|
|
229
|
+
? await this.transport.getHealthStatus()
|
|
230
|
+
: { healthy: false, error: 'Transport not initialized' };
|
|
231
|
+
const sessionMetrics = this.sessionManager.getSessionMetrics();
|
|
232
|
+
const poolStats = this.connectionPool?.getStats();
|
|
233
|
+
const metrics = {
|
|
234
|
+
registeredTools: this.toolRegistry.getToolCount(),
|
|
235
|
+
totalRequests: this.requestStats.total,
|
|
236
|
+
successfulRequests: this.requestStats.successful,
|
|
237
|
+
failedRequests: this.requestStats.failed,
|
|
238
|
+
totalSessions: sessionMetrics.total,
|
|
239
|
+
activeSessions: sessionMetrics.active,
|
|
240
|
+
...(transportHealth.metrics || {}),
|
|
241
|
+
};
|
|
242
|
+
if (poolStats) {
|
|
243
|
+
metrics.poolConnections = poolStats.totalConnections;
|
|
244
|
+
metrics.poolIdleConnections = poolStats.idleConnections;
|
|
245
|
+
metrics.poolBusyConnections = poolStats.busyConnections;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
healthy: this.running && transportHealth.healthy,
|
|
249
|
+
error: transportHealth.error,
|
|
250
|
+
metrics,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
return {
|
|
255
|
+
healthy: false,
|
|
256
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
getMetrics() {
|
|
261
|
+
const sessionMetrics = this.sessionManager.getSessionMetrics();
|
|
262
|
+
const registryStats = this.toolRegistry.getStats();
|
|
263
|
+
return {
|
|
264
|
+
totalRequests: this.requestStats.total,
|
|
265
|
+
successfulRequests: this.requestStats.successful,
|
|
266
|
+
failedRequests: this.requestStats.failed,
|
|
267
|
+
averageResponseTime: this.requestStats.total > 0
|
|
268
|
+
? this.requestStats.totalResponseTime / this.requestStats.total
|
|
269
|
+
: 0,
|
|
270
|
+
activeSessions: sessionMetrics.active,
|
|
271
|
+
toolInvocations: Object.fromEntries(registryStats.topTools.map((t) => [t.name, t.calls])),
|
|
272
|
+
errors: {},
|
|
273
|
+
lastReset: this.startTime || new Date(),
|
|
274
|
+
startupTime: this.startupDuration,
|
|
275
|
+
uptime: this.startTime ? Date.now() - this.startTime.getTime() : 0,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
getSessions() {
|
|
279
|
+
return this.sessionManager.getActiveSessions();
|
|
280
|
+
}
|
|
281
|
+
getSession(sessionId) {
|
|
282
|
+
return this.sessionManager.getSession(sessionId);
|
|
283
|
+
}
|
|
284
|
+
terminateSession(sessionId) {
|
|
285
|
+
const result = this.sessionManager.closeSession(sessionId, 'Terminated by server');
|
|
286
|
+
if (this.currentSession?.id === sessionId) {
|
|
287
|
+
this.currentSession = undefined;
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
async handleRequest(request) {
|
|
292
|
+
const startTime = performance.now();
|
|
293
|
+
this.requestStats.total++;
|
|
294
|
+
this.logger.debug('Handling request', {
|
|
295
|
+
id: request.id,
|
|
296
|
+
method: request.method,
|
|
297
|
+
});
|
|
298
|
+
// Rate limiting check (skip for initialize)
|
|
299
|
+
if (request.method !== 'initialize') {
|
|
300
|
+
const sessionId = this.currentSession?.id;
|
|
301
|
+
const rateLimitResult = this.rateLimiter.check(sessionId);
|
|
302
|
+
if (!rateLimitResult.allowed) {
|
|
303
|
+
this.requestStats.failed++;
|
|
304
|
+
return {
|
|
305
|
+
jsonrpc: '2.0',
|
|
306
|
+
id: request.id,
|
|
307
|
+
error: {
|
|
308
|
+
code: -32000,
|
|
309
|
+
message: 'Rate limit exceeded',
|
|
310
|
+
data: { retryAfter: rateLimitResult.retryAfter },
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
this.rateLimiter.consume(sessionId);
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
if (request.method === 'initialize') {
|
|
318
|
+
return await this.handleInitialize(request);
|
|
319
|
+
}
|
|
320
|
+
const session = this.getOrCreateSession();
|
|
321
|
+
if (!session.isInitialized && request.method !== 'initialized') {
|
|
322
|
+
return this.createErrorResponse(request.id, ErrorCodes.SERVER_NOT_INITIALIZED, 'Server not initialized');
|
|
323
|
+
}
|
|
324
|
+
this.sessionManager.updateActivity(session.id);
|
|
325
|
+
const response = await this.routeRequest(request);
|
|
326
|
+
const duration = performance.now() - startTime;
|
|
327
|
+
this.requestStats.successful++;
|
|
328
|
+
this.requestStats.totalResponseTime += duration;
|
|
329
|
+
this.logger.debug('Request completed', {
|
|
330
|
+
id: request.id,
|
|
331
|
+
method: request.method,
|
|
332
|
+
duration: `${duration.toFixed(2)}ms`,
|
|
333
|
+
});
|
|
334
|
+
return response;
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
const duration = performance.now() - startTime;
|
|
338
|
+
this.requestStats.failed++;
|
|
339
|
+
this.requestStats.totalResponseTime += duration;
|
|
340
|
+
this.logger.error('Request failed', {
|
|
341
|
+
id: request.id,
|
|
342
|
+
method: request.method,
|
|
343
|
+
error,
|
|
344
|
+
});
|
|
345
|
+
return this.createErrorResponse(request.id, ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Internal error');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async handleNotification(notification) {
|
|
349
|
+
this.logger.debug('Handling notification', { method: notification.method });
|
|
350
|
+
switch (notification.method) {
|
|
351
|
+
case 'initialized':
|
|
352
|
+
this.logger.info('Client initialized notification received');
|
|
353
|
+
break;
|
|
354
|
+
case 'notifications/cancelled':
|
|
355
|
+
this.logger.debug('Request cancelled', notification.params);
|
|
356
|
+
break;
|
|
357
|
+
default:
|
|
358
|
+
this.logger.debug('Unknown notification', { method: notification.method });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async handleInitialize(request) {
|
|
362
|
+
const params = request.params;
|
|
363
|
+
if (!params) {
|
|
364
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Invalid params');
|
|
365
|
+
}
|
|
366
|
+
const session = this.sessionManager.createSession(this.config.transport);
|
|
367
|
+
this.sessionManager.initializeSession(session.id, params);
|
|
368
|
+
this.currentSession = session;
|
|
369
|
+
const result = {
|
|
370
|
+
protocolVersion: this.protocolVersion,
|
|
371
|
+
capabilities: this.capabilities,
|
|
372
|
+
serverInfo: this.serverInfo,
|
|
373
|
+
instructions: 'Claude-Flow MCP Server V3 ready for tool execution',
|
|
374
|
+
};
|
|
375
|
+
this.logger.info('Session initialized', {
|
|
376
|
+
sessionId: session.id,
|
|
377
|
+
clientInfo: params.clientInfo,
|
|
378
|
+
});
|
|
379
|
+
return {
|
|
380
|
+
jsonrpc: '2.0',
|
|
381
|
+
id: request.id,
|
|
382
|
+
result,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
async routeRequest(request) {
|
|
386
|
+
switch (request.method) {
|
|
387
|
+
// Tool methods
|
|
388
|
+
case 'tools/list':
|
|
389
|
+
return this.handleToolsList(request);
|
|
390
|
+
case 'tools/call':
|
|
391
|
+
return this.handleToolsCall(request);
|
|
392
|
+
// Resource methods (MCP 2025-11-25)
|
|
393
|
+
case 'resources/list':
|
|
394
|
+
return this.handleResourcesList(request);
|
|
395
|
+
case 'resources/read':
|
|
396
|
+
return this.handleResourcesRead(request);
|
|
397
|
+
case 'resources/subscribe':
|
|
398
|
+
return this.handleResourcesSubscribe(request);
|
|
399
|
+
case 'resources/unsubscribe':
|
|
400
|
+
return this.handleResourcesUnsubscribe(request);
|
|
401
|
+
// Prompt methods (MCP 2025-11-25)
|
|
402
|
+
case 'prompts/list':
|
|
403
|
+
return this.handlePromptsList(request);
|
|
404
|
+
case 'prompts/get':
|
|
405
|
+
return this.handlePromptsGet(request);
|
|
406
|
+
// Task methods (MCP 2025-11-25)
|
|
407
|
+
case 'tasks/status':
|
|
408
|
+
return this.handleTasksStatus(request);
|
|
409
|
+
case 'tasks/cancel':
|
|
410
|
+
return this.handleTasksCancel(request);
|
|
411
|
+
// Completion (MCP 2025-11-25)
|
|
412
|
+
case 'completion/complete':
|
|
413
|
+
return this.handleCompletion(request);
|
|
414
|
+
// Logging (MCP 2025-11-25)
|
|
415
|
+
case 'logging/setLevel':
|
|
416
|
+
return this.handleLoggingSetLevel(request);
|
|
417
|
+
// Sampling (MCP 2025-11-25)
|
|
418
|
+
case 'sampling/createMessage':
|
|
419
|
+
return this.handleSamplingCreateMessage(request);
|
|
420
|
+
// Utility
|
|
421
|
+
case 'ping':
|
|
422
|
+
return {
|
|
423
|
+
jsonrpc: '2.0',
|
|
424
|
+
id: request.id,
|
|
425
|
+
result: { pong: true, timestamp: Date.now() },
|
|
426
|
+
};
|
|
427
|
+
default:
|
|
428
|
+
// Check if it's a direct tool call
|
|
429
|
+
if (this.toolRegistry.hasTool(request.method)) {
|
|
430
|
+
return this.handleToolExecution(request);
|
|
431
|
+
}
|
|
432
|
+
return this.createErrorResponse(request.id, ErrorCodes.METHOD_NOT_FOUND, `Method not found: ${request.method}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
handleToolsList(request) {
|
|
436
|
+
const tools = this.toolRegistry.listTools().map((t) => ({
|
|
437
|
+
name: t.name,
|
|
438
|
+
description: t.description,
|
|
439
|
+
inputSchema: this.toolRegistry.getTool(t.name)?.inputSchema,
|
|
440
|
+
}));
|
|
441
|
+
return {
|
|
442
|
+
jsonrpc: '2.0',
|
|
443
|
+
id: request.id,
|
|
444
|
+
result: { tools },
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
async handleToolsCall(request) {
|
|
448
|
+
const params = request.params;
|
|
449
|
+
if (!params?.name) {
|
|
450
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Tool name is required');
|
|
451
|
+
}
|
|
452
|
+
const context = {
|
|
453
|
+
sessionId: this.currentSession?.id || 'unknown',
|
|
454
|
+
requestId: request.id,
|
|
455
|
+
orchestrator: this.orchestrator,
|
|
456
|
+
swarmCoordinator: this.swarmCoordinator,
|
|
457
|
+
};
|
|
458
|
+
const result = await this.toolRegistry.execute(params.name, params.arguments || {}, context);
|
|
459
|
+
return {
|
|
460
|
+
jsonrpc: '2.0',
|
|
461
|
+
id: request.id,
|
|
462
|
+
result,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
async handleToolExecution(request) {
|
|
466
|
+
const context = {
|
|
467
|
+
sessionId: this.currentSession?.id || 'unknown',
|
|
468
|
+
requestId: request.id,
|
|
469
|
+
orchestrator: this.orchestrator,
|
|
470
|
+
swarmCoordinator: this.swarmCoordinator,
|
|
471
|
+
};
|
|
472
|
+
const result = await this.toolRegistry.execute(request.method, request.params || {}, context);
|
|
473
|
+
return {
|
|
474
|
+
jsonrpc: '2.0',
|
|
475
|
+
id: request.id,
|
|
476
|
+
result,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
// ============================================================================
|
|
480
|
+
// Resource Handlers (MCP 2025-11-25)
|
|
481
|
+
// ============================================================================
|
|
482
|
+
handleResourcesList(request) {
|
|
483
|
+
const params = request.params;
|
|
484
|
+
const result = this.resourceRegistry.list(params?.cursor);
|
|
485
|
+
return {
|
|
486
|
+
jsonrpc: '2.0',
|
|
487
|
+
id: request.id,
|
|
488
|
+
result,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
async handleResourcesRead(request) {
|
|
492
|
+
const params = request.params;
|
|
493
|
+
if (!params?.uri) {
|
|
494
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Resource URI is required');
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
const result = await this.resourceRegistry.read(params.uri);
|
|
498
|
+
return {
|
|
499
|
+
jsonrpc: '2.0',
|
|
500
|
+
id: request.id,
|
|
501
|
+
result,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, error instanceof Error ? error.message : 'Resource read failed');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
handleResourcesSubscribe(request) {
|
|
509
|
+
const params = request.params;
|
|
510
|
+
const sessionId = this.currentSession?.id;
|
|
511
|
+
if (!params?.uri) {
|
|
512
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Resource URI is required');
|
|
513
|
+
}
|
|
514
|
+
if (!sessionId) {
|
|
515
|
+
return this.createErrorResponse(request.id, ErrorCodes.SERVER_NOT_INITIALIZED, 'No active session');
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
// Track subscription for this session
|
|
519
|
+
let sessionSubs = this.resourceSubscriptions.get(sessionId);
|
|
520
|
+
if (!sessionSubs) {
|
|
521
|
+
sessionSubs = new Set();
|
|
522
|
+
this.resourceSubscriptions.set(sessionId, sessionSubs);
|
|
523
|
+
}
|
|
524
|
+
const subscriptionId = this.resourceRegistry.subscribe(params.uri, (uri, content) => {
|
|
525
|
+
// Send notification when resource updates
|
|
526
|
+
this.sendNotification('notifications/resources/updated', { uri });
|
|
527
|
+
});
|
|
528
|
+
sessionSubs.add(params.uri);
|
|
529
|
+
return {
|
|
530
|
+
jsonrpc: '2.0',
|
|
531
|
+
id: request.id,
|
|
532
|
+
result: { subscriptionId },
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
return this.createErrorResponse(request.id, ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Subscription failed');
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
handleResourcesUnsubscribe(request) {
|
|
540
|
+
const params = request.params;
|
|
541
|
+
const sessionId = this.currentSession?.id;
|
|
542
|
+
if (!params?.uri) {
|
|
543
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Resource URI is required');
|
|
544
|
+
}
|
|
545
|
+
if (sessionId) {
|
|
546
|
+
const sessionSubs = this.resourceSubscriptions.get(sessionId);
|
|
547
|
+
if (sessionSubs) {
|
|
548
|
+
sessionSubs.delete(params.uri);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
jsonrpc: '2.0',
|
|
553
|
+
id: request.id,
|
|
554
|
+
result: { success: true },
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
// ============================================================================
|
|
558
|
+
// Prompt Handlers (MCP 2025-11-25)
|
|
559
|
+
// ============================================================================
|
|
560
|
+
handlePromptsList(request) {
|
|
561
|
+
const params = request.params;
|
|
562
|
+
const result = this.promptRegistry.list(params?.cursor);
|
|
563
|
+
return {
|
|
564
|
+
jsonrpc: '2.0',
|
|
565
|
+
id: request.id,
|
|
566
|
+
result,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
async handlePromptsGet(request) {
|
|
570
|
+
const params = request.params;
|
|
571
|
+
if (!params?.name) {
|
|
572
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Prompt name is required');
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
const result = await this.promptRegistry.get(params.name, params.arguments);
|
|
576
|
+
return {
|
|
577
|
+
jsonrpc: '2.0',
|
|
578
|
+
id: request.id,
|
|
579
|
+
result,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, error instanceof Error ? error.message : 'Prompt get failed');
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// ============================================================================
|
|
587
|
+
// Task Handlers (MCP 2025-11-25)
|
|
588
|
+
// ============================================================================
|
|
589
|
+
handleTasksStatus(request) {
|
|
590
|
+
const params = request.params;
|
|
591
|
+
if (params?.taskId) {
|
|
592
|
+
const task = this.taskManager.getTask(params.taskId);
|
|
593
|
+
if (!task) {
|
|
594
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, `Task not found: ${params.taskId}`);
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
jsonrpc: '2.0',
|
|
598
|
+
id: request.id,
|
|
599
|
+
result: task,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
// Return all tasks
|
|
603
|
+
return {
|
|
604
|
+
jsonrpc: '2.0',
|
|
605
|
+
id: request.id,
|
|
606
|
+
result: { tasks: this.taskManager.getAllTasks() },
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
handleTasksCancel(request) {
|
|
610
|
+
const params = request.params;
|
|
611
|
+
if (!params?.taskId) {
|
|
612
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Task ID is required');
|
|
613
|
+
}
|
|
614
|
+
const success = this.taskManager.cancelTask(params.taskId, params.reason);
|
|
615
|
+
return {
|
|
616
|
+
jsonrpc: '2.0',
|
|
617
|
+
id: request.id,
|
|
618
|
+
result: { success },
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
// ============================================================================
|
|
622
|
+
// Completion Handler (MCP 2025-11-25)
|
|
623
|
+
// ============================================================================
|
|
624
|
+
handleCompletion(request) {
|
|
625
|
+
const params = request.params;
|
|
626
|
+
if (!params?.ref || !params?.argument) {
|
|
627
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Completion reference and argument are required');
|
|
628
|
+
}
|
|
629
|
+
// Basic completion implementation - can be extended
|
|
630
|
+
const completions = [];
|
|
631
|
+
if (params.ref.type === 'ref/prompt') {
|
|
632
|
+
// Get prompt argument completions
|
|
633
|
+
const prompt = this.promptRegistry.getPrompt(params.ref.name || '');
|
|
634
|
+
if (prompt?.arguments) {
|
|
635
|
+
for (const arg of prompt.arguments) {
|
|
636
|
+
if (arg.name === params.argument.name) {
|
|
637
|
+
// Could add domain-specific completions here
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
else if (params.ref.type === 'ref/resource') {
|
|
643
|
+
// Get resource URI completions
|
|
644
|
+
const { resources } = this.resourceRegistry.list();
|
|
645
|
+
for (const resource of resources) {
|
|
646
|
+
if (resource.uri.startsWith(params.argument.value)) {
|
|
647
|
+
completions.push(resource.uri);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
jsonrpc: '2.0',
|
|
653
|
+
id: request.id,
|
|
654
|
+
result: {
|
|
655
|
+
completion: {
|
|
656
|
+
values: completions.slice(0, 10),
|
|
657
|
+
total: completions.length,
|
|
658
|
+
hasMore: completions.length > 10,
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
// ============================================================================
|
|
664
|
+
// Logging Handler (MCP 2025-11-25)
|
|
665
|
+
// ============================================================================
|
|
666
|
+
handleLoggingSetLevel(request) {
|
|
667
|
+
const params = request.params;
|
|
668
|
+
if (!params?.level) {
|
|
669
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'Log level is required');
|
|
670
|
+
}
|
|
671
|
+
// Update capabilities
|
|
672
|
+
this.capabilities.logging = { level: params.level };
|
|
673
|
+
this.logger.info('Log level updated', { level: params.level });
|
|
674
|
+
return {
|
|
675
|
+
jsonrpc: '2.0',
|
|
676
|
+
id: request.id,
|
|
677
|
+
result: { success: true },
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
// ============================================================================
|
|
681
|
+
// Sampling Handler (MCP 2025-11-25)
|
|
682
|
+
// ============================================================================
|
|
683
|
+
async handleSamplingCreateMessage(request) {
|
|
684
|
+
const params = request.params;
|
|
685
|
+
if (!params?.messages || !params?.maxTokens) {
|
|
686
|
+
return this.createErrorResponse(request.id, ErrorCodes.INVALID_PARAMS, 'messages and maxTokens are required');
|
|
687
|
+
}
|
|
688
|
+
// Check if sampling is available
|
|
689
|
+
const available = await this.samplingManager.isAvailable();
|
|
690
|
+
if (!available) {
|
|
691
|
+
return this.createErrorResponse(request.id, ErrorCodes.INTERNAL_ERROR, 'No LLM provider available for sampling');
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
const result = await this.samplingManager.createMessage({
|
|
695
|
+
messages: params.messages.map((m) => ({
|
|
696
|
+
role: m.role,
|
|
697
|
+
content: m.content,
|
|
698
|
+
})),
|
|
699
|
+
maxTokens: params.maxTokens,
|
|
700
|
+
systemPrompt: params.systemPrompt,
|
|
701
|
+
modelPreferences: params.modelPreferences,
|
|
702
|
+
includeContext: params.includeContext,
|
|
703
|
+
temperature: params.temperature,
|
|
704
|
+
stopSequences: params.stopSequences,
|
|
705
|
+
metadata: params.metadata,
|
|
706
|
+
}, {
|
|
707
|
+
sessionId: this.currentSession?.id || 'unknown',
|
|
708
|
+
serverId: this.serverInfo.name,
|
|
709
|
+
});
|
|
710
|
+
return {
|
|
711
|
+
jsonrpc: '2.0',
|
|
712
|
+
id: request.id,
|
|
713
|
+
result,
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
catch (error) {
|
|
717
|
+
return this.createErrorResponse(request.id, ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Sampling failed');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
// ============================================================================
|
|
721
|
+
// Notification Sender
|
|
722
|
+
// ============================================================================
|
|
723
|
+
async sendNotification(method, params) {
|
|
724
|
+
if (this.transport?.sendNotification) {
|
|
725
|
+
await this.transport.sendNotification({
|
|
726
|
+
jsonrpc: '2.0',
|
|
727
|
+
method,
|
|
728
|
+
params,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
getOrCreateSession() {
|
|
733
|
+
if (this.currentSession) {
|
|
734
|
+
return this.currentSession;
|
|
735
|
+
}
|
|
736
|
+
const session = this.sessionManager.createSession(this.config.transport);
|
|
737
|
+
this.currentSession = session;
|
|
738
|
+
return session;
|
|
739
|
+
}
|
|
740
|
+
createErrorResponse(id, code, message) {
|
|
741
|
+
return {
|
|
742
|
+
jsonrpc: '2.0',
|
|
743
|
+
id,
|
|
744
|
+
error: { code, message },
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
async registerBuiltInTools() {
|
|
748
|
+
this.registerTool({
|
|
749
|
+
name: 'system/info',
|
|
750
|
+
description: 'Get system information',
|
|
751
|
+
inputSchema: { type: 'object', properties: {} },
|
|
752
|
+
handler: async () => ({
|
|
753
|
+
name: this.serverInfo.name,
|
|
754
|
+
version: this.serverInfo.version,
|
|
755
|
+
platform: platform(),
|
|
756
|
+
arch: arch(),
|
|
757
|
+
runtime: 'Node.js',
|
|
758
|
+
uptime: this.startTime ? Date.now() - this.startTime.getTime() : 0,
|
|
759
|
+
}),
|
|
760
|
+
category: 'system',
|
|
761
|
+
});
|
|
762
|
+
this.registerTool({
|
|
763
|
+
name: 'system/health',
|
|
764
|
+
description: 'Get system health status',
|
|
765
|
+
inputSchema: { type: 'object', properties: {} },
|
|
766
|
+
handler: async () => await this.getHealthStatus(),
|
|
767
|
+
category: 'system',
|
|
768
|
+
cacheable: true,
|
|
769
|
+
cacheTTL: 2000,
|
|
770
|
+
});
|
|
771
|
+
this.registerTool({
|
|
772
|
+
name: 'system/metrics',
|
|
773
|
+
description: 'Get server metrics',
|
|
774
|
+
inputSchema: { type: 'object', properties: {} },
|
|
775
|
+
handler: async () => this.getMetrics(),
|
|
776
|
+
category: 'system',
|
|
777
|
+
cacheable: true,
|
|
778
|
+
cacheTTL: 1000,
|
|
779
|
+
});
|
|
780
|
+
this.registerTool({
|
|
781
|
+
name: 'tools/list-detailed',
|
|
782
|
+
description: 'List all registered tools with details',
|
|
783
|
+
inputSchema: {
|
|
784
|
+
type: 'object',
|
|
785
|
+
properties: {
|
|
786
|
+
category: { type: 'string', description: 'Filter by category' },
|
|
787
|
+
},
|
|
788
|
+
},
|
|
789
|
+
handler: async (input) => {
|
|
790
|
+
const params = input;
|
|
791
|
+
if (params.category) {
|
|
792
|
+
return this.toolRegistry.getByCategory(params.category);
|
|
793
|
+
}
|
|
794
|
+
return this.toolRegistry.listTools();
|
|
795
|
+
},
|
|
796
|
+
category: 'system',
|
|
797
|
+
});
|
|
798
|
+
this.logger.info('Built-in tools registered', {
|
|
799
|
+
count: 4,
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
setupEventHandlers() {
|
|
803
|
+
this.toolRegistry.on('tool:registered', (name) => {
|
|
804
|
+
this.emit('tool:registered', name);
|
|
805
|
+
});
|
|
806
|
+
this.toolRegistry.on('tool:called', (data) => {
|
|
807
|
+
this.emit('tool:called', data);
|
|
808
|
+
});
|
|
809
|
+
this.toolRegistry.on('tool:completed', (data) => {
|
|
810
|
+
this.emit('tool:completed', data);
|
|
811
|
+
});
|
|
812
|
+
this.toolRegistry.on('tool:error', (data) => {
|
|
813
|
+
this.emit('tool:error', data);
|
|
814
|
+
});
|
|
815
|
+
this.sessionManager.on('session:created', (session) => {
|
|
816
|
+
this.emit('session:created', session);
|
|
817
|
+
});
|
|
818
|
+
this.sessionManager.on('session:closed', (data) => {
|
|
819
|
+
this.emit('session:closed', data);
|
|
820
|
+
});
|
|
821
|
+
this.sessionManager.on('session:expired', (session) => {
|
|
822
|
+
this.emit('session:expired', session);
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
export function createMCPServer(config, logger, orchestrator, swarmCoordinator) {
|
|
827
|
+
return new MCPServer(config, logger, orchestrator, swarmCoordinator);
|
|
828
|
+
}
|
|
829
|
+
//# sourceMappingURL=server.js.map
|