claude-flow 2.7.32 → 2.7.34
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/.claude/settings.local.json +9 -2
- package/.claude/skills/agentic-jujutsu/SKILL.md +645 -0
- package/CHANGELOG.md +75 -0
- package/bin/claude-flow +1 -1
- package/dist/src/cli/commands/mcp.js +61 -7
- package/dist/src/cli/commands/mcp.js.map +1 -1
- package/dist/src/cli/help-formatter.js +5 -3
- package/dist/src/cli/help-formatter.js.map +1 -1
- package/dist/src/cli/simple-cli.js +173 -79
- package/dist/src/cli/simple-cli.js.map +1 -1
- package/dist/src/cli/validation-helper.js.map +1 -1
- package/dist/src/core/version.js +2 -2
- package/dist/src/core/version.js.map +1 -1
- package/dist/src/mcp/async/job-manager-mcp25.js +240 -0
- package/dist/src/mcp/async/job-manager-mcp25.js.map +1 -0
- package/dist/src/mcp/index.js +8 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/protocol/version-negotiation.js +182 -0
- package/dist/src/mcp/protocol/version-negotiation.js.map +1 -0
- package/dist/src/mcp/registry/mcp-registry-client-2025.js +210 -0
- package/dist/src/mcp/registry/mcp-registry-client-2025.js.map +1 -0
- package/dist/src/mcp/server-factory.js +189 -0
- package/dist/src/mcp/server-factory.js.map +1 -0
- package/dist/src/mcp/server-mcp-2025.js +283 -0
- package/dist/src/mcp/server-mcp-2025.js.map +1 -0
- package/dist/src/mcp/tool-registry-progressive.js +319 -0
- package/dist/src/mcp/tool-registry-progressive.js.map +1 -0
- package/dist/src/mcp/tools/_template.js +62 -0
- package/dist/src/mcp/tools/_template.js.map +1 -0
- package/dist/src/mcp/tools/loader.js +228 -0
- package/dist/src/mcp/tools/loader.js.map +1 -0
- package/dist/src/mcp/tools/system/search.js +224 -0
- package/dist/src/mcp/tools/system/search.js.map +1 -0
- package/dist/src/mcp/tools/system/status.js +168 -0
- package/dist/src/mcp/tools/system/status.js.map +1 -0
- package/dist/src/mcp/validation/schema-validator-2025.js +198 -0
- package/dist/src/mcp/validation/schema-validator-2025.js.map +1 -0
- package/dist/src/memory/swarm-memory.js +340 -421
- package/dist/src/memory/swarm-memory.js.map +1 -1
- package/docs/.claude-flow/metrics/performance.json +3 -3
- package/docs/.claude-flow/metrics/task-metrics.json +3 -3
- package/docs/.github-release-issue-v2.7.33.md +488 -0
- package/docs/AGENTDB_BRANCH_MERGE_VERIFICATION.md +436 -0
- package/docs/BRANCH_REVIEW_SUMMARY.md +439 -0
- package/docs/DEEP_CODE_REVIEW_v2.7.33.md +1159 -0
- package/docs/MCP_2025_FEATURE_CONFIRMATION.md +698 -0
- package/docs/NPM_PUBLISH_GUIDE_v2.7.33.md +628 -0
- package/docs/REGRESSION_TEST_REPORT_v2.7.33.md +397 -0
- package/docs/RELEASE_NOTES_v2.7.33.md +618 -0
- package/docs/RELEASE_READINESS_SUMMARY.md +377 -0
- package/docs/RELEASE_SUMMARY_v2.7.33.md +456 -0
- package/docs/agentic-flow-agentdb-mcp-integration.md +1198 -0
- package/docs/mcp-2025-implementation-summary.md +459 -0
- package/docs/mcp-spec-2025-implementation-plan.md +1330 -0
- package/docs/phase-1-2-implementation-summary.md +676 -0
- package/docs/regression-analysis-phase-1-2.md +555 -0
- package/package.json +5 -1
- package/src/cli/commands/mcp.ts +86 -9
- package/src/mcp/async/job-manager-mcp25.ts +456 -0
- package/src/mcp/index.ts +60 -0
- package/src/mcp/protocol/version-negotiation.ts +329 -0
- package/src/mcp/registry/mcp-registry-client-2025.ts +334 -0
- package/src/mcp/server-factory.ts +426 -0
- package/src/mcp/server-mcp-2025.ts +507 -0
- package/src/mcp/tool-registry-progressive.ts +539 -0
- package/src/mcp/tools/_template.ts +174 -0
- package/src/mcp/tools/loader.ts +362 -0
- package/src/mcp/tools/system/search.ts +276 -0
- package/src/mcp/tools/system/status.ts +206 -0
- package/src/mcp/validation/schema-validator-2025.ts +294 -0
- package/docs/AGENTDB_V1.6.1_DEEP_REVIEW.md +0 -386
- package/docs/RECENT_RELEASES_SUMMARY.md +0 -375
- package/docs/V2.7.31_RELEASE_NOTES.md +0 -375
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 2025-11 Enhanced Server
|
|
3
|
+
*
|
|
4
|
+
* Integrates all MCP 2025-11 features with full backward compatibility:
|
|
5
|
+
* - Version negotiation
|
|
6
|
+
* - Async job support
|
|
7
|
+
* - Registry integration
|
|
8
|
+
* - JSON Schema 1.1 validation
|
|
9
|
+
* - Dual-mode operation (2025-11 + legacy)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ILogger } from '../interfaces/logger.js';
|
|
13
|
+
import type { IEventBus } from '../interfaces/event-bus.js';
|
|
14
|
+
import { VersionNegotiator, BackwardCompatibilityAdapter, type MCPHandshake, type MCPVersion, type MCPCapability } from './protocol/version-negotiation.js';
|
|
15
|
+
import { MCPAsyncJobManager, type MCPToolRequest, type MCPJobHandle, type MCPJobResult } from './async/job-manager-mcp25.js';
|
|
16
|
+
import { MCPRegistryClient, type RegistryConfig } from './registry/mcp-registry-client-2025.js';
|
|
17
|
+
import { SchemaValidator, upgradeToolSchema } from './validation/schema-validator-2025.js';
|
|
18
|
+
import { ProgressiveToolRegistry } from './tool-registry-progressive.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* MCP 2025-11 server configuration
|
|
22
|
+
*/
|
|
23
|
+
export interface MCP2025ServerConfig {
|
|
24
|
+
serverId: string;
|
|
25
|
+
transport: 'stdio' | 'http' | 'ws';
|
|
26
|
+
|
|
27
|
+
// Version & capabilities
|
|
28
|
+
enableMCP2025: boolean; // Feature flag for gradual rollout
|
|
29
|
+
supportLegacyClients: boolean; // Backward compatibility
|
|
30
|
+
|
|
31
|
+
// Async jobs
|
|
32
|
+
async: {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
maxJobs?: number;
|
|
35
|
+
jobTTL?: number;
|
|
36
|
+
persistence?: 'memory' | 'redis' | 'sqlite';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Registry
|
|
40
|
+
registry: RegistryConfig;
|
|
41
|
+
|
|
42
|
+
// Schema validation
|
|
43
|
+
validation: {
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
strictMode?: boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Tool registry
|
|
49
|
+
toolsDirectory?: string;
|
|
50
|
+
|
|
51
|
+
// Existing config
|
|
52
|
+
orchestratorContext?: any;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* MCP 2025-11 Enhanced Server
|
|
57
|
+
*/
|
|
58
|
+
export class MCP2025Server {
|
|
59
|
+
private versionNegotiator: VersionNegotiator;
|
|
60
|
+
private compatibilityAdapter: BackwardCompatibilityAdapter;
|
|
61
|
+
private jobManager?: MCPAsyncJobManager;
|
|
62
|
+
private registryClient?: MCPRegistryClient;
|
|
63
|
+
private schemaValidator: SchemaValidator;
|
|
64
|
+
private toolRegistry: ProgressiveToolRegistry;
|
|
65
|
+
|
|
66
|
+
// Session state
|
|
67
|
+
private sessions: Map<string, {
|
|
68
|
+
clientId: string;
|
|
69
|
+
version: MCPVersion;
|
|
70
|
+
capabilities: MCPCapability[];
|
|
71
|
+
isLegacy: boolean;
|
|
72
|
+
createdAt: number;
|
|
73
|
+
lastAccess: number;
|
|
74
|
+
}> = new Map();
|
|
75
|
+
|
|
76
|
+
// Session management constants
|
|
77
|
+
private readonly MAX_SESSIONS = 10000;
|
|
78
|
+
private readonly SESSION_TTL = 3600000; // 1 hour
|
|
79
|
+
private sessionCleanupInterval?: NodeJS.Timeout;
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
private config: MCP2025ServerConfig,
|
|
83
|
+
private eventBus: IEventBus,
|
|
84
|
+
private logger: ILogger
|
|
85
|
+
) {
|
|
86
|
+
// Initialize version negotiation
|
|
87
|
+
this.versionNegotiator = new VersionNegotiator(logger);
|
|
88
|
+
this.compatibilityAdapter = new BackwardCompatibilityAdapter(logger);
|
|
89
|
+
|
|
90
|
+
// Initialize schema validator
|
|
91
|
+
this.schemaValidator = new SchemaValidator(logger);
|
|
92
|
+
|
|
93
|
+
// Initialize tool registry (progressive)
|
|
94
|
+
this.toolRegistry = new ProgressiveToolRegistry({
|
|
95
|
+
enableInProcess: true,
|
|
96
|
+
enableMetrics: true,
|
|
97
|
+
enableCaching: true,
|
|
98
|
+
orchestratorContext: config.orchestratorContext,
|
|
99
|
+
toolsDirectory: config.toolsDirectory,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.logger.info('MCP 2025-11 server created', {
|
|
103
|
+
serverId: config.serverId,
|
|
104
|
+
mcp2025Enabled: config.enableMCP2025,
|
|
105
|
+
legacySupport: config.supportLegacyClients,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Initialize server
|
|
111
|
+
*/
|
|
112
|
+
async initialize(): Promise<void> {
|
|
113
|
+
this.logger.info('Initializing MCP 2025-11 server');
|
|
114
|
+
|
|
115
|
+
// Initialize tool registry
|
|
116
|
+
await this.toolRegistry.initialize();
|
|
117
|
+
|
|
118
|
+
// Start session cleanup interval
|
|
119
|
+
this.sessionCleanupInterval = setInterval(
|
|
120
|
+
() => this.cleanupExpiredSessions(),
|
|
121
|
+
300000 // Every 5 minutes
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Initialize async job manager if enabled
|
|
125
|
+
if (this.config.async.enabled) {
|
|
126
|
+
this.jobManager = new MCPAsyncJobManager(
|
|
127
|
+
null, // Use memory persistence for now
|
|
128
|
+
this.logger,
|
|
129
|
+
{
|
|
130
|
+
maxJobs: this.config.async.maxJobs,
|
|
131
|
+
jobTTL: this.config.async.jobTTL,
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
this.logger.info('Async job manager initialized');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Initialize registry client if enabled
|
|
139
|
+
if (this.config.registry.enabled) {
|
|
140
|
+
this.registryClient = new MCPRegistryClient(
|
|
141
|
+
this.config.registry,
|
|
142
|
+
this.logger,
|
|
143
|
+
() => this.toolRegistry.getToolNames(),
|
|
144
|
+
() => this.versionNegotiator.getServerCapabilities(),
|
|
145
|
+
async () => this.getHealthStatus()
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Register with MCP Registry
|
|
149
|
+
try {
|
|
150
|
+
await this.registryClient.register();
|
|
151
|
+
} catch (error) {
|
|
152
|
+
this.logger.error('Failed to register with MCP Registry', { error });
|
|
153
|
+
// Don't fail initialization if registry is unavailable
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.logger.info('MCP 2025-11 server initialized successfully');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Handle client connection/handshake
|
|
162
|
+
*/
|
|
163
|
+
async handleHandshake(clientHandshake: any, sessionId: string): Promise<MCPHandshake> {
|
|
164
|
+
// Check if legacy client
|
|
165
|
+
const isLegacy = this.compatibilityAdapter.isLegacyRequest(clientHandshake);
|
|
166
|
+
|
|
167
|
+
let handshake: MCPHandshake;
|
|
168
|
+
if (isLegacy && this.config.supportLegacyClients) {
|
|
169
|
+
this.logger.info('Legacy client detected, enabling compatibility mode', { sessionId });
|
|
170
|
+
handshake = this.compatibilityAdapter.convertToModern(clientHandshake);
|
|
171
|
+
} else {
|
|
172
|
+
handshake = clientHandshake;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Negotiate version and capabilities
|
|
176
|
+
const negotiation = await this.versionNegotiator.negotiate(handshake);
|
|
177
|
+
|
|
178
|
+
if (!negotiation.success) {
|
|
179
|
+
throw new Error(`Version negotiation failed: ${negotiation.error}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Enforce session limit
|
|
183
|
+
if (this.sessions.size >= this.MAX_SESSIONS) {
|
|
184
|
+
// Remove oldest session
|
|
185
|
+
const oldestSession = Array.from(this.sessions.entries())
|
|
186
|
+
.sort((a, b) => a[1].createdAt - b[1].createdAt)[0];
|
|
187
|
+
if (oldestSession) {
|
|
188
|
+
this.sessions.delete(oldestSession[0]);
|
|
189
|
+
this.logger.warn('Session limit reached, removed oldest session', {
|
|
190
|
+
removedSessionId: oldestSession[0],
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Store session info
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
this.sessions.set(sessionId, {
|
|
198
|
+
clientId: handshake.client_id || 'unknown',
|
|
199
|
+
version: negotiation.agreed_version,
|
|
200
|
+
capabilities: negotiation.agreed_capabilities,
|
|
201
|
+
isLegacy,
|
|
202
|
+
createdAt: now,
|
|
203
|
+
lastAccess: now,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Create server handshake response
|
|
207
|
+
const serverHandshake = this.versionNegotiator.createServerHandshake(
|
|
208
|
+
this.config.serverId,
|
|
209
|
+
this.config.transport,
|
|
210
|
+
{
|
|
211
|
+
name: 'Claude Flow',
|
|
212
|
+
version: '2.7.32',
|
|
213
|
+
description: 'Enterprise AI orchestration with MCP 2025-11 support',
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Apply agreed version and capabilities
|
|
218
|
+
serverHandshake.mcp_version = negotiation.agreed_version;
|
|
219
|
+
serverHandshake.capabilities = negotiation.agreed_capabilities;
|
|
220
|
+
|
|
221
|
+
this.logger.info('Handshake completed', {
|
|
222
|
+
sessionId,
|
|
223
|
+
version: serverHandshake.mcp_version,
|
|
224
|
+
capabilities: serverHandshake.capabilities,
|
|
225
|
+
isLegacy,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return serverHandshake;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Handle tool call request (with async support)
|
|
233
|
+
*/
|
|
234
|
+
async handleToolCall(
|
|
235
|
+
request: MCPToolRequest | any,
|
|
236
|
+
sessionId: string
|
|
237
|
+
): Promise<MCPJobHandle | MCPJobResult | any> {
|
|
238
|
+
const session = this.sessions.get(sessionId);
|
|
239
|
+
|
|
240
|
+
// Update last access time
|
|
241
|
+
if (session) {
|
|
242
|
+
session.lastAccess = Date.now();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Handle legacy request format
|
|
246
|
+
if (session?.isLegacy) {
|
|
247
|
+
return this.handleLegacyToolCall(request, sessionId);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// MCP 2025-11 format
|
|
251
|
+
const mcpRequest = request as MCPToolRequest;
|
|
252
|
+
|
|
253
|
+
// Validate request
|
|
254
|
+
if (!mcpRequest.tool_id) {
|
|
255
|
+
throw new Error('Missing tool_id in request');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Get tool
|
|
259
|
+
const tool = await this.toolRegistry.getTool(mcpRequest.tool_id);
|
|
260
|
+
if (!tool) {
|
|
261
|
+
throw new Error(`Tool not found: ${mcpRequest.tool_id}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Validate input if validation enabled
|
|
265
|
+
if (this.config.validation.enabled) {
|
|
266
|
+
const validation = this.schemaValidator.validateInput(
|
|
267
|
+
upgradeToolSchema(tool.inputSchema),
|
|
268
|
+
mcpRequest.arguments
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
if (!validation.valid) {
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Invalid input: ${validation.errors?.map(e => e.message).join(', ')}`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check if async mode requested
|
|
279
|
+
const hasAsyncCapability = session?.capabilities.includes('async');
|
|
280
|
+
const isAsyncRequest = mcpRequest.mode === 'async' && hasAsyncCapability;
|
|
281
|
+
|
|
282
|
+
if (isAsyncRequest && this.jobManager) {
|
|
283
|
+
// Submit as async job
|
|
284
|
+
this.logger.info('Submitting async job', {
|
|
285
|
+
tool_id: mcpRequest.tool_id,
|
|
286
|
+
request_id: mcpRequest.request_id,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
return await this.jobManager.submitJob(
|
|
290
|
+
mcpRequest,
|
|
291
|
+
async (args, onProgress) => {
|
|
292
|
+
// Execute tool with progress tracking
|
|
293
|
+
return await tool.handler(args, {
|
|
294
|
+
orchestrator: this.config.orchestratorContext,
|
|
295
|
+
sessionId,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
// Execute synchronously
|
|
301
|
+
this.logger.info('Executing tool synchronously', {
|
|
302
|
+
tool_id: mcpRequest.tool_id,
|
|
303
|
+
request_id: mcpRequest.request_id,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const startTime = Date.now();
|
|
307
|
+
const result = await tool.handler(mcpRequest.arguments, {
|
|
308
|
+
orchestrator: this.config.orchestratorContext,
|
|
309
|
+
sessionId,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Validate output if validation enabled
|
|
313
|
+
if (this.config.validation.enabled && tool.metadata?.outputSchema) {
|
|
314
|
+
const validation = this.schemaValidator.validateOutput(
|
|
315
|
+
tool.metadata.outputSchema,
|
|
316
|
+
result
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (!validation.valid) {
|
|
320
|
+
this.logger.warn('Output validation failed', {
|
|
321
|
+
tool_id: mcpRequest.tool_id,
|
|
322
|
+
errors: validation.errors,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Return in MCP 2025-11 format
|
|
328
|
+
return {
|
|
329
|
+
request_id: mcpRequest.request_id,
|
|
330
|
+
status: 'success',
|
|
331
|
+
result,
|
|
332
|
+
metadata: {
|
|
333
|
+
duration_ms: Date.now() - startTime,
|
|
334
|
+
},
|
|
335
|
+
} as MCPJobResult;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Handle legacy tool call
|
|
341
|
+
*/
|
|
342
|
+
private async handleLegacyToolCall(request: any, sessionId: string): Promise<any> {
|
|
343
|
+
this.logger.info('Handling legacy tool call', {
|
|
344
|
+
toolName: request.name || request.method,
|
|
345
|
+
sessionId,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Convert to modern format internally
|
|
349
|
+
const toolId = request.name || request.method;
|
|
350
|
+
const args = request.arguments || request.params || {};
|
|
351
|
+
|
|
352
|
+
const tool = await this.toolRegistry.getTool(toolId);
|
|
353
|
+
if (!tool) {
|
|
354
|
+
throw new Error(`Tool not found: ${toolId}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const result = await tool.handler(args, {
|
|
358
|
+
orchestrator: this.config.orchestratorContext,
|
|
359
|
+
sessionId,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Return in legacy format
|
|
363
|
+
return this.compatibilityAdapter.convertToLegacy(
|
|
364
|
+
{ result, status: 'success' },
|
|
365
|
+
true
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Poll async job
|
|
371
|
+
*/
|
|
372
|
+
async pollJob(job_id: string): Promise<MCPJobHandle> {
|
|
373
|
+
if (!this.jobManager) {
|
|
374
|
+
throw new Error('Async jobs not enabled');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return await this.jobManager.pollJob(job_id);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Resume async job (get results)
|
|
382
|
+
*/
|
|
383
|
+
async resumeJob(job_id: string): Promise<MCPJobResult> {
|
|
384
|
+
if (!this.jobManager) {
|
|
385
|
+
throw new Error('Async jobs not enabled');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return await this.jobManager.resumeJob(job_id);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Cancel async job
|
|
393
|
+
*/
|
|
394
|
+
async cancelJob(job_id: string): Promise<boolean> {
|
|
395
|
+
if (!this.jobManager) {
|
|
396
|
+
throw new Error('Async jobs not enabled');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return await this.jobManager.cancelJob(job_id);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* List async jobs
|
|
404
|
+
*/
|
|
405
|
+
async listJobs(filter?: { status?: string; limit?: number }) {
|
|
406
|
+
if (!this.jobManager) {
|
|
407
|
+
throw new Error('Async jobs not enabled');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return await this.jobManager.listJobs(filter);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get health status
|
|
415
|
+
*/
|
|
416
|
+
private async getHealthStatus(): Promise<{
|
|
417
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
418
|
+
latency_ms: number;
|
|
419
|
+
}> {
|
|
420
|
+
const startTime = Date.now();
|
|
421
|
+
|
|
422
|
+
// Perform health checks
|
|
423
|
+
const latency = Date.now() - startTime;
|
|
424
|
+
|
|
425
|
+
// Determine status based on metrics
|
|
426
|
+
let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy';
|
|
427
|
+
|
|
428
|
+
if (latency > 100) {
|
|
429
|
+
status = 'degraded';
|
|
430
|
+
}
|
|
431
|
+
if (latency > 500) {
|
|
432
|
+
status = 'unhealthy';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return { status, latency_ms: latency };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Get metrics
|
|
440
|
+
*/
|
|
441
|
+
getMetrics() {
|
|
442
|
+
return {
|
|
443
|
+
sessions: {
|
|
444
|
+
total: this.sessions.size,
|
|
445
|
+
byVersion: this.getSessionsByVersion(),
|
|
446
|
+
legacy: Array.from(this.sessions.values()).filter(s => s.isLegacy).length,
|
|
447
|
+
},
|
|
448
|
+
jobs: this.jobManager?.getMetrics(),
|
|
449
|
+
tools: this.toolRegistry.getMetrics(),
|
|
450
|
+
validation: this.schemaValidator.getCacheStats(),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private getSessionsByVersion() {
|
|
455
|
+
const counts: Record<string, number> = {};
|
|
456
|
+
for (const session of this.sessions.values()) {
|
|
457
|
+
counts[session.version] = (counts[session.version] || 0) + 1;
|
|
458
|
+
}
|
|
459
|
+
return counts;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Cleanup expired sessions
|
|
464
|
+
*/
|
|
465
|
+
private cleanupExpiredSessions(): void {
|
|
466
|
+
const now = Date.now();
|
|
467
|
+
let cleaned = 0;
|
|
468
|
+
|
|
469
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
470
|
+
// Remove if last access was more than TTL ago
|
|
471
|
+
if (now - session.lastAccess > this.SESSION_TTL) {
|
|
472
|
+
this.sessions.delete(sessionId);
|
|
473
|
+
cleaned++;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (cleaned > 0) {
|
|
478
|
+
this.logger.info('Cleaned up expired sessions', { count: cleaned });
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Cleanup resources
|
|
484
|
+
*/
|
|
485
|
+
async cleanup(): Promise<void> {
|
|
486
|
+
this.logger.info('Cleaning up MCP 2025-11 server');
|
|
487
|
+
|
|
488
|
+
// Stop session cleanup interval
|
|
489
|
+
if (this.sessionCleanupInterval) {
|
|
490
|
+
clearInterval(this.sessionCleanupInterval);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Unregister from registry
|
|
494
|
+
if (this.registryClient) {
|
|
495
|
+
await this.registryClient.unregister();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Clear caches
|
|
499
|
+
this.schemaValidator.clearCache();
|
|
500
|
+
await this.toolRegistry.cleanup();
|
|
501
|
+
|
|
502
|
+
// Clear sessions
|
|
503
|
+
this.sessions.clear();
|
|
504
|
+
|
|
505
|
+
this.logger.info('Cleanup complete');
|
|
506
|
+
}
|
|
507
|
+
}
|