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.
Files changed (73) hide show
  1. package/.claude/settings.local.json +9 -2
  2. package/.claude/skills/agentic-jujutsu/SKILL.md +645 -0
  3. package/CHANGELOG.md +75 -0
  4. package/bin/claude-flow +1 -1
  5. package/dist/src/cli/commands/mcp.js +61 -7
  6. package/dist/src/cli/commands/mcp.js.map +1 -1
  7. package/dist/src/cli/help-formatter.js +5 -3
  8. package/dist/src/cli/help-formatter.js.map +1 -1
  9. package/dist/src/cli/simple-cli.js +173 -79
  10. package/dist/src/cli/simple-cli.js.map +1 -1
  11. package/dist/src/cli/validation-helper.js.map +1 -1
  12. package/dist/src/core/version.js +2 -2
  13. package/dist/src/core/version.js.map +1 -1
  14. package/dist/src/mcp/async/job-manager-mcp25.js +240 -0
  15. package/dist/src/mcp/async/job-manager-mcp25.js.map +1 -0
  16. package/dist/src/mcp/index.js +8 -0
  17. package/dist/src/mcp/index.js.map +1 -1
  18. package/dist/src/mcp/protocol/version-negotiation.js +182 -0
  19. package/dist/src/mcp/protocol/version-negotiation.js.map +1 -0
  20. package/dist/src/mcp/registry/mcp-registry-client-2025.js +210 -0
  21. package/dist/src/mcp/registry/mcp-registry-client-2025.js.map +1 -0
  22. package/dist/src/mcp/server-factory.js +189 -0
  23. package/dist/src/mcp/server-factory.js.map +1 -0
  24. package/dist/src/mcp/server-mcp-2025.js +283 -0
  25. package/dist/src/mcp/server-mcp-2025.js.map +1 -0
  26. package/dist/src/mcp/tool-registry-progressive.js +319 -0
  27. package/dist/src/mcp/tool-registry-progressive.js.map +1 -0
  28. package/dist/src/mcp/tools/_template.js +62 -0
  29. package/dist/src/mcp/tools/_template.js.map +1 -0
  30. package/dist/src/mcp/tools/loader.js +228 -0
  31. package/dist/src/mcp/tools/loader.js.map +1 -0
  32. package/dist/src/mcp/tools/system/search.js +224 -0
  33. package/dist/src/mcp/tools/system/search.js.map +1 -0
  34. package/dist/src/mcp/tools/system/status.js +168 -0
  35. package/dist/src/mcp/tools/system/status.js.map +1 -0
  36. package/dist/src/mcp/validation/schema-validator-2025.js +198 -0
  37. package/dist/src/mcp/validation/schema-validator-2025.js.map +1 -0
  38. package/dist/src/memory/swarm-memory.js +340 -421
  39. package/dist/src/memory/swarm-memory.js.map +1 -1
  40. package/docs/.claude-flow/metrics/performance.json +3 -3
  41. package/docs/.claude-flow/metrics/task-metrics.json +3 -3
  42. package/docs/.github-release-issue-v2.7.33.md +488 -0
  43. package/docs/AGENTDB_BRANCH_MERGE_VERIFICATION.md +436 -0
  44. package/docs/BRANCH_REVIEW_SUMMARY.md +439 -0
  45. package/docs/DEEP_CODE_REVIEW_v2.7.33.md +1159 -0
  46. package/docs/MCP_2025_FEATURE_CONFIRMATION.md +698 -0
  47. package/docs/NPM_PUBLISH_GUIDE_v2.7.33.md +628 -0
  48. package/docs/REGRESSION_TEST_REPORT_v2.7.33.md +397 -0
  49. package/docs/RELEASE_NOTES_v2.7.33.md +618 -0
  50. package/docs/RELEASE_READINESS_SUMMARY.md +377 -0
  51. package/docs/RELEASE_SUMMARY_v2.7.33.md +456 -0
  52. package/docs/agentic-flow-agentdb-mcp-integration.md +1198 -0
  53. package/docs/mcp-2025-implementation-summary.md +459 -0
  54. package/docs/mcp-spec-2025-implementation-plan.md +1330 -0
  55. package/docs/phase-1-2-implementation-summary.md +676 -0
  56. package/docs/regression-analysis-phase-1-2.md +555 -0
  57. package/package.json +5 -1
  58. package/src/cli/commands/mcp.ts +86 -9
  59. package/src/mcp/async/job-manager-mcp25.ts +456 -0
  60. package/src/mcp/index.ts +60 -0
  61. package/src/mcp/protocol/version-negotiation.ts +329 -0
  62. package/src/mcp/registry/mcp-registry-client-2025.ts +334 -0
  63. package/src/mcp/server-factory.ts +426 -0
  64. package/src/mcp/server-mcp-2025.ts +507 -0
  65. package/src/mcp/tool-registry-progressive.ts +539 -0
  66. package/src/mcp/tools/_template.ts +174 -0
  67. package/src/mcp/tools/loader.ts +362 -0
  68. package/src/mcp/tools/system/search.ts +276 -0
  69. package/src/mcp/tools/system/status.ts +206 -0
  70. package/src/mcp/validation/schema-validator-2025.ts +294 -0
  71. package/docs/AGENTDB_V1.6.1_DEEP_REVIEW.md +0 -386
  72. package/docs/RECENT_RELEASES_SUMMARY.md +0 -375
  73. package/docs/V2.7.31_RELEASE_NOTES.md +0 -375
@@ -6,10 +6,17 @@ import { Command } from '@cliffy/command';
6
6
  import chalk from 'chalk';
7
7
  import { logger } from '../../core/logger.js';
8
8
  import { configManager } from '../../core/config.js';
9
- import { MCPServer } from '../../mcp/server.js';
9
+ import { MCPServer, type IMCPServer } from '../../mcp/server.js';
10
10
  import { eventBus } from '../../core/event-bus.js';
11
+ import {
12
+ createMCPServer,
13
+ isMCP2025Available,
14
+ getServerCapabilities,
15
+ type ExtendedMCPConfig,
16
+ } from '../../mcp/server-factory.js';
17
+ import type { MCP2025Server } from '../../mcp/server-mcp-2025.js';
11
18
 
12
- let mcpServer: MCPServer | null = null;
19
+ let mcpServer: IMCPServer | MCP2025Server | null = null;
13
20
 
14
21
  export const mcpCommand = new Command()
15
22
  .description('Manage MCP server and tools')
@@ -32,29 +39,95 @@ export const mcpCommand = new Command()
32
39
  .option('--transport <transport:string>', 'Transport type (stdio, http)', {
33
40
  default: 'stdio',
34
41
  })
42
+ .option('--mcp2025', 'Enable MCP 2025-11 features (version negotiation, async jobs, etc.)', {
43
+ default: false,
44
+ })
45
+ .option('--no-legacy', 'Disable legacy client support', { default: false })
35
46
  .action(async (options: any) => {
36
47
  try {
37
48
  const config = await configManager.load();
38
49
 
39
- // Override with CLI options
40
- const mcpConfig = {
50
+ // Check if MCP 2025-11 dependencies are available
51
+ const mcp2025Available = isMCP2025Available();
52
+ const enableMCP2025 = options.mcp2025 && mcp2025Available;
53
+
54
+ if (options.mcp2025 && !mcp2025Available) {
55
+ console.log(
56
+ chalk.yellow(
57
+ '⚠️ MCP 2025-11 dependencies not found. Install with: npm install uuid ajv ajv-formats ajv-errors'
58
+ )
59
+ );
60
+ console.log(chalk.yellow(' Falling back to legacy MCP server...'));
61
+ }
62
+
63
+ // Build extended configuration
64
+ const mcpConfig: ExtendedMCPConfig = {
41
65
  ...config.mcp,
42
66
  port: options.port,
43
67
  host: options.host,
44
68
  transport: options.transport,
69
+ features: {
70
+ enableMCP2025,
71
+ supportLegacyClients: options.legacy !== false,
72
+ enableVersionNegotiation: enableMCP2025,
73
+ enableAsyncJobs: enableMCP2025,
74
+ enableRegistryIntegration: false, // Opt-in via env var
75
+ enableSchemaValidation: enableMCP2025,
76
+ enableProgressiveDisclosure: true, // Phase 1 feature (always enabled)
77
+ },
78
+ mcp2025: enableMCP2025
79
+ ? {
80
+ async: {
81
+ enabled: true,
82
+ maxJobs: 100,
83
+ jobTTL: 3600000,
84
+ },
85
+ registry: {
86
+ enabled: process.env.MCP_REGISTRY_ENABLED === 'true',
87
+ url: process.env.MCP_REGISTRY_URL,
88
+ apiKey: process.env.MCP_REGISTRY_API_KEY,
89
+ },
90
+ validation: {
91
+ enabled: true,
92
+ strictMode: false,
93
+ },
94
+ }
95
+ : undefined,
45
96
  };
46
97
 
47
- mcpServer = new MCPServer(mcpConfig, eventBus, logger);
98
+ // Create server using factory
99
+ mcpServer = await createMCPServer(mcpConfig, eventBus, logger, {
100
+ autoDetectFeatures: false, // Use explicit config
101
+ });
102
+
48
103
  await mcpServer.start();
49
104
 
105
+ // Get capabilities
106
+ const capabilities = getServerCapabilities(mcpConfig);
107
+
50
108
  console.log(chalk.green(`✅ MCP server started on ${options.host}:${options.port}`));
51
- console.log(chalk.cyan(`📡 Server URL: http://${options.host}:${options.port}`));
52
- console.log(chalk.cyan(`🔧 Available tools: Research, Code, Terminal, Memory`));
53
109
  console.log(
54
- chalk.cyan(`📚 API documentation: http://${options.host}:${options.port}/docs`),
110
+ chalk.cyan(`🎯 Mode: ${enableMCP2025 ? 'MCP 2025-11 Enhanced' : 'Legacy Compatible'}`)
55
111
  );
112
+ console.log(chalk.cyan(`📡 Transport: ${options.transport}`));
113
+
114
+ if (capabilities.length > 0) {
115
+ console.log(chalk.cyan(`✨ Capabilities: ${capabilities.join(', ')}`));
116
+ }
117
+
118
+ if (enableMCP2025) {
119
+ console.log(chalk.green(' • Version negotiation (YYYY-MM format)'));
120
+ console.log(chalk.green(' • Async job support (poll/resume)'));
121
+ console.log(chalk.green(' • JSON Schema 1.1 validation'));
122
+ console.log(chalk.green(' • Progressive disclosure (98.7% token reduction)'));
123
+ }
124
+
125
+ if (options.transport === 'http') {
126
+ console.log(chalk.cyan(`📚 Server URL: http://${options.host}:${options.port}`));
127
+ }
56
128
  } catch (error) {
57
129
  console.error(chalk.red(`❌ Failed to start MCP server: ${(error as Error).message}`));
130
+ logger.error('MCP server startup failed', { error });
58
131
  process.exit(1);
59
132
  }
60
133
  }),
@@ -151,7 +224,11 @@ export const mcpCommand = new Command()
151
224
 
152
225
  console.log(chalk.yellow('🔄 Starting MCP server...'));
153
226
  const config = await configManager.load();
154
- mcpServer = new MCPServer(config.mcp, eventBus, logger);
227
+
228
+ // Use factory to create server with same capabilities as before
229
+ mcpServer = await createMCPServer(config.mcp, eventBus, logger, {
230
+ autoDetectFeatures: true, // Auto-detect on restart
231
+ });
155
232
  await mcpServer.start();
156
233
 
157
234
  console.log(
@@ -0,0 +1,456 @@
1
+ /**
2
+ * MCP 2025-11 Async Job Manager
3
+ *
4
+ * Implements async job lifecycle per MCP 2025-11 specification:
5
+ * - Job handles with request_id
6
+ * - Poll/resume semantics
7
+ * - Progress tracking
8
+ * - Job persistence
9
+ */
10
+
11
+ import { EventEmitter } from 'events';
12
+ import { v4 as uuidv4 } from 'uuid';
13
+ import type { ILogger } from '../../interfaces/logger.js';
14
+
15
+ /**
16
+ * MCP tool request (2025-11 format)
17
+ */
18
+ export interface MCPToolRequest {
19
+ request_id: string;
20
+ tool_id: string;
21
+ arguments: Record<string, any>;
22
+ session?: string;
23
+ mode: 'async' | 'sync';
24
+ context?: {
25
+ trace_id?: string;
26
+ client_name?: string;
27
+ [key: string]: any;
28
+ };
29
+ }
30
+
31
+ /**
32
+ * MCP job handle (2025-11 format)
33
+ */
34
+ export interface MCPJobHandle {
35
+ request_id: string;
36
+ job_id: string;
37
+ status: 'in_progress' | 'success' | 'error';
38
+ poll_after: number; // seconds
39
+ progress?: {
40
+ percent: number;
41
+ message?: string;
42
+ };
43
+ }
44
+
45
+ /**
46
+ * MCP job result (2025-11 format)
47
+ */
48
+ export interface MCPJobResult {
49
+ request_id: string;
50
+ status: 'success' | 'error' | 'in_progress';
51
+ result?: any;
52
+ error?: {
53
+ code: string;
54
+ message: string;
55
+ details?: any;
56
+ };
57
+ progress?: {
58
+ percent: number;
59
+ message?: string;
60
+ };
61
+ metadata: {
62
+ duration_ms?: number;
63
+ tokens_used?: number;
64
+ [key: string]: any;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Internal job state
70
+ */
71
+ interface AsyncJob {
72
+ request_id: string;
73
+ job_id: string;
74
+ tool_id: string;
75
+ arguments: Record<string, any>;
76
+ mode: 'async' | 'sync';
77
+ status: 'queued' | 'running' | 'success' | 'error' | 'cancelled';
78
+ progress: number;
79
+ progress_message?: string;
80
+ result?: any;
81
+ error?: any;
82
+ context?: any;
83
+ created_at: Date;
84
+ started_at?: Date;
85
+ completed_at?: Date;
86
+ tokens_used?: number;
87
+ abortController?: AbortController;
88
+ }
89
+
90
+ /**
91
+ * Job persistence interface
92
+ */
93
+ export interface JobPersistence {
94
+ save(job: AsyncJob): Promise<void>;
95
+ load(job_id: string): Promise<AsyncJob | null>;
96
+ list(filter?: { status?: string; limit?: number }): Promise<AsyncJob[]>;
97
+ delete(job_id: string): Promise<void>;
98
+ }
99
+
100
+ /**
101
+ * Simple in-memory job persistence (fallback)
102
+ */
103
+ export class MemoryJobPersistence implements JobPersistence {
104
+ private jobs: Map<string, AsyncJob> = new Map();
105
+
106
+ async save(job: AsyncJob): Promise<void> {
107
+ this.jobs.set(job.job_id, { ...job });
108
+ }
109
+
110
+ async load(job_id: string): Promise<AsyncJob | null> {
111
+ const job = this.jobs.get(job_id);
112
+ return job ? { ...job } : null;
113
+ }
114
+
115
+ async list(filter?: { status?: string; limit?: number }): Promise<AsyncJob[]> {
116
+ let jobs = Array.from(this.jobs.values());
117
+
118
+ if (filter?.status) {
119
+ jobs = jobs.filter(j => j.status === filter.status);
120
+ }
121
+
122
+ if (filter?.limit) {
123
+ jobs = jobs.slice(0, filter.limit);
124
+ }
125
+
126
+ return jobs;
127
+ }
128
+
129
+ async delete(job_id: string): Promise<void> {
130
+ this.jobs.delete(job_id);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * MCP 2025-11 Async Job Manager
136
+ */
137
+ export class MCPAsyncJobManager extends EventEmitter {
138
+ private jobs: Map<string, AsyncJob> = new Map();
139
+ private executors: Map<string, Promise<any>> = new Map();
140
+ private persistence: JobPersistence;
141
+
142
+ constructor(
143
+ persistence: JobPersistence | null,
144
+ private logger: ILogger,
145
+ private config: {
146
+ maxJobs?: number;
147
+ jobTTL?: number;
148
+ defaultPollInterval?: number;
149
+ } = {}
150
+ ) {
151
+ super();
152
+ this.persistence = persistence || new MemoryJobPersistence();
153
+
154
+ // Default config
155
+ this.config.maxJobs = this.config.maxJobs || 1000;
156
+ this.config.jobTTL = this.config.jobTTL || 86400000; // 24 hours
157
+ this.config.defaultPollInterval = this.config.defaultPollInterval || 5;
158
+
159
+ // Cleanup expired jobs periodically
160
+ setInterval(() => this.cleanupExpiredJobs(), 3600000); // Every hour
161
+ }
162
+
163
+ /**
164
+ * Submit async job (MCP 2025-11 format)
165
+ */
166
+ async submitJob(
167
+ request: MCPToolRequest,
168
+ executor: (args: any, onProgress: (percent: number, message?: string) => void) => Promise<any>
169
+ ): Promise<MCPJobHandle> {
170
+ // Check capacity
171
+ if (this.jobs.size >= this.config.maxJobs!) {
172
+ throw new Error('Job queue full. Please try again later.');
173
+ }
174
+
175
+ // Check for duplicate request_id (prevent race conditions)
176
+ const existingJob = Array.from(this.jobs.values()).find(
177
+ j => j.request_id === request.request_id &&
178
+ (j.status === 'queued' || j.status === 'running')
179
+ );
180
+ if (existingJob) {
181
+ throw new Error(`Duplicate request_id: ${request.request_id}. Job already submitted.`);
182
+ }
183
+
184
+ // Create job
185
+ const job: AsyncJob = {
186
+ request_id: request.request_id,
187
+ job_id: uuidv4(),
188
+ tool_id: request.tool_id,
189
+ arguments: request.arguments,
190
+ mode: request.mode,
191
+ status: 'queued',
192
+ progress: 0,
193
+ context: request.context,
194
+ created_at: new Date(),
195
+ };
196
+
197
+ // Save to persistence
198
+ await this.persistence.save(job);
199
+ this.jobs.set(job.job_id, job);
200
+
201
+ this.logger.info('Job submitted', {
202
+ job_id: job.job_id,
203
+ request_id: job.request_id,
204
+ tool_id: job.tool_id,
205
+ });
206
+
207
+ // Start execution in background
208
+ this.executeJob(job, executor);
209
+
210
+ // Return job handle immediately
211
+ return {
212
+ request_id: job.request_id,
213
+ job_id: job.job_id,
214
+ status: 'in_progress',
215
+ poll_after: this.config.defaultPollInterval!,
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Poll job status
221
+ */
222
+ async pollJob(job_id: string): Promise<MCPJobHandle> {
223
+ const job = await this.persistence.load(job_id);
224
+
225
+ if (!job) {
226
+ throw new Error(`Job not found: ${job_id}`);
227
+ }
228
+
229
+ const status = job.status === 'success' ? 'success' :
230
+ job.status === 'error' ? 'error' : 'in_progress';
231
+
232
+ const handle: MCPJobHandle = {
233
+ request_id: job.request_id,
234
+ job_id: job.job_id,
235
+ status,
236
+ poll_after: status === 'in_progress' ? this.config.defaultPollInterval! : 0,
237
+ };
238
+
239
+ if (status === 'in_progress') {
240
+ handle.progress = {
241
+ percent: job.progress,
242
+ message: job.progress_message,
243
+ };
244
+ }
245
+
246
+ return handle;
247
+ }
248
+
249
+ /**
250
+ * Resume job (get results)
251
+ */
252
+ async resumeJob(job_id: string): Promise<MCPJobResult> {
253
+ const job = await this.persistence.load(job_id);
254
+
255
+ if (!job) {
256
+ throw new Error(`Job not found: ${job_id}`);
257
+ }
258
+
259
+ const result: MCPJobResult = {
260
+ request_id: job.request_id,
261
+ status: job.status === 'success' ? 'success' :
262
+ job.status === 'error' ? 'error' : 'in_progress',
263
+ metadata: {},
264
+ };
265
+
266
+ if (job.status === 'success') {
267
+ result.result = job.result;
268
+ result.metadata.duration_ms = job.completed_at && job.started_at
269
+ ? job.completed_at.getTime() - job.started_at.getTime()
270
+ : undefined;
271
+ result.metadata.tokens_used = job.tokens_used;
272
+ } else if (job.status === 'error') {
273
+ result.error = {
274
+ code: 'EXECUTION_ERROR',
275
+ message: job.error?.message || 'Job execution failed',
276
+ details: job.error,
277
+ };
278
+ } else {
279
+ // Still in progress
280
+ result.progress = {
281
+ percent: job.progress,
282
+ message: job.progress_message,
283
+ };
284
+ }
285
+
286
+ return result;
287
+ }
288
+
289
+ /**
290
+ * Cancel a running job
291
+ */
292
+ async cancelJob(job_id: string): Promise<boolean> {
293
+ const job = this.jobs.get(job_id);
294
+
295
+ if (!job) {
296
+ return false;
297
+ }
298
+
299
+ if (job.status === 'success' || job.status === 'error') {
300
+ return false; // Already finished
301
+ }
302
+
303
+ // Abort execution if AbortController is available
304
+ if (job.abortController) {
305
+ job.abortController.abort();
306
+ }
307
+
308
+ job.status = 'cancelled';
309
+ job.completed_at = new Date();
310
+ await this.persistence.save(job);
311
+
312
+ this.emit('job:cancelled', job_id);
313
+ this.logger.info('Job cancelled', { job_id });
314
+
315
+ return true;
316
+ }
317
+
318
+ /**
319
+ * List jobs
320
+ */
321
+ async listJobs(filter?: {
322
+ status?: string;
323
+ limit?: number;
324
+ }): Promise<AsyncJob[]> {
325
+ return await this.persistence.list(filter);
326
+ }
327
+
328
+ /**
329
+ * Execute job in background
330
+ */
331
+ private async executeJob(
332
+ job: AsyncJob,
333
+ executor: (args: any, onProgress: (percent: number, message?: string) => void) => Promise<any>
334
+ ): Promise<void> {
335
+ // Update status to running
336
+ job.status = 'running';
337
+ job.started_at = new Date();
338
+
339
+ // Create AbortController for cancellation support
340
+ job.abortController = new AbortController();
341
+
342
+ await this.persistence.save(job);
343
+
344
+ this.emit('job:started', job.job_id);
345
+ this.logger.info('Job started', { job_id: job.job_id, tool_id: job.tool_id });
346
+
347
+ try {
348
+ // Progress callback
349
+ const onProgress = (percent: number, message?: string) => {
350
+ job.progress = Math.min(100, Math.max(0, percent));
351
+ job.progress_message = message;
352
+ this.persistence.save(job).catch(err =>
353
+ this.logger.error('Failed to save progress', { job_id: job.job_id, error: err })
354
+ );
355
+ this.emit('job:progress', job.job_id, job.progress, message);
356
+ };
357
+
358
+ // Check if already cancelled
359
+ if (job.abortController.signal.aborted) {
360
+ throw new Error('Job cancelled before execution');
361
+ }
362
+
363
+ // Execute with abort support
364
+ const result = await executor(job.arguments, onProgress);
365
+
366
+ // Mark successful
367
+ job.status = 'success';
368
+ job.result = result;
369
+ job.progress = 100;
370
+ job.completed_at = new Date();
371
+ await this.persistence.save(job);
372
+
373
+ this.emit('job:completed', job.job_id, result);
374
+ this.logger.info('Job completed', {
375
+ job_id: job.job_id,
376
+ duration_ms: job.completed_at.getTime() - job.started_at!.getTime(),
377
+ });
378
+ } catch (error: any) {
379
+ // Mark failed
380
+ job.status = 'error';
381
+ job.error = {
382
+ message: error.message,
383
+ stack: error.stack,
384
+ code: error.code,
385
+ };
386
+ job.completed_at = new Date();
387
+ await this.persistence.save(job);
388
+
389
+ this.emit('job:failed', job.job_id, error);
390
+ this.logger.error('Job failed', {
391
+ job_id: job.job_id,
392
+ error: error.message,
393
+ });
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Cleanup expired jobs
399
+ */
400
+ private async cleanupExpiredJobs(): Promise<number> {
401
+ const now = Date.now();
402
+ const jobs = await this.persistence.list();
403
+ let cleaned = 0;
404
+
405
+ for (const job of jobs) {
406
+ const age = now - job.created_at.getTime();
407
+
408
+ // Remove if expired and not running
409
+ if (age > this.config.jobTTL! && job.status !== 'running') {
410
+ await this.persistence.delete(job.job_id);
411
+ this.jobs.delete(job.job_id);
412
+ cleaned++;
413
+ }
414
+ }
415
+
416
+ if (cleaned > 0) {
417
+ this.logger.info('Cleaned up expired jobs', { count: cleaned });
418
+ }
419
+
420
+ return cleaned;
421
+ }
422
+
423
+ /**
424
+ * Get metrics
425
+ */
426
+ getMetrics() {
427
+ const jobs = Array.from(this.jobs.values());
428
+
429
+ return {
430
+ total: jobs.length,
431
+ byStatus: {
432
+ queued: jobs.filter(j => j.status === 'queued').length,
433
+ running: jobs.filter(j => j.status === 'running').length,
434
+ success: jobs.filter(j => j.status === 'success').length,
435
+ error: jobs.filter(j => j.status === 'error').length,
436
+ cancelled: jobs.filter(j => j.status === 'cancelled').length,
437
+ },
438
+ averageDuration: this.calculateAverageDuration(jobs),
439
+ };
440
+ }
441
+
442
+ private calculateAverageDuration(jobs: AsyncJob[]): number {
443
+ const completed = jobs.filter(j =>
444
+ (j.status === 'success' || j.status === 'error') &&
445
+ j.started_at && j.completed_at
446
+ );
447
+
448
+ if (completed.length === 0) return 0;
449
+
450
+ const total = completed.reduce((sum, j) =>
451
+ sum + (j.completed_at!.getTime() - j.started_at!.getTime()), 0
452
+ );
453
+
454
+ return total / completed.length;
455
+ }
456
+ }
package/src/mcp/index.ts CHANGED
@@ -27,6 +27,66 @@ export type { SDKIntegrationConfig } from './sdk-integration.js';
27
27
  // Core MCP Server
28
28
  export { MCPServer, type IMCPServer } from './server.js';
29
29
 
30
+ // MCP 2025-11 Server and Components
31
+ export { MCP2025Server, type MCP2025ServerConfig } from './server-mcp-2025.js';
32
+ export {
33
+ MCPServerFactory,
34
+ createMCPServer,
35
+ isMCP2025Available,
36
+ getServerCapabilities,
37
+ type MCPFeatureFlags,
38
+ type ExtendedMCPConfig,
39
+ } from './server-factory.js';
40
+
41
+ // MCP 2025-11 Protocol Components
42
+ export {
43
+ VersionNegotiator,
44
+ BackwardCompatibilityAdapter,
45
+ type MCPHandshake,
46
+ type MCPVersion,
47
+ type MCPCapability,
48
+ type NegotiationResult as MCP2025NegotiationResult,
49
+ } from './protocol/version-negotiation.js';
50
+
51
+ // MCP 2025-11 Async Job Management
52
+ export {
53
+ MCPAsyncJobManager,
54
+ type MCPToolRequest,
55
+ type MCPJobHandle,
56
+ type MCPJobResult,
57
+ type AsyncJob,
58
+ type JobStatus,
59
+ } from './async/job-manager-mcp25.js';
60
+
61
+ // MCP 2025-11 Registry Integration
62
+ export {
63
+ MCPRegistryClient,
64
+ type RegistryConfig,
65
+ type ServerRegistryEntry,
66
+ } from './registry/mcp-registry-client-2025.js';
67
+
68
+ // MCP 2025-11 Schema Validation
69
+ export {
70
+ SchemaValidator,
71
+ upgradeToolSchema,
72
+ type ValidationResult,
73
+ } from './validation/schema-validator-2025.js';
74
+
75
+ // Progressive Tool Registry (Phase 1 & 2)
76
+ export {
77
+ ProgressiveToolRegistry,
78
+ createProgressiveToolRegistry,
79
+ createProgressiveClaudeFlowSdkServer,
80
+ type ProgressiveToolRegistryConfig,
81
+ } from './tool-registry-progressive.js';
82
+
83
+ // Dynamic Tool Loader (Phase 1)
84
+ export {
85
+ DynamicToolLoader,
86
+ type ToolMetadata,
87
+ type ToolSearchQuery,
88
+ } from './tools/loader.js';
89
+
30
90
  // Lifecycle Management
31
91
  export {
32
92
  MCPLifecycleManager,