claude-flow 2.7.33 → 2.7.35
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 +1 -1
- package/CHANGELOG.md +140 -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/init/index.js +55 -33
- package/dist/src/cli/init/index.js.map +1 -1
- package/dist/src/cli/simple-cli.js +182 -172
- package/dist/src/cli/simple-cli.js.map +1 -1
- package/dist/src/cli/simple-commands/init/agent-copier.js +9 -3
- package/dist/src/cli/simple-commands/init/agent-copier.js.map +1 -1
- package/dist/src/core/DatabaseManager.js +39 -9
- package/dist/src/core/DatabaseManager.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/utils/error-recovery.js +215 -0
- package/dist/src/utils/error-recovery.js.map +1 -0
- package/dist/src/utils/metrics-reader.js +10 -0
- package/dist/src/utils/metrics-reader.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/AUTOMATIC_ERROR_RECOVERY_v2.7.35.md +321 -0
- package/docs/BRANCH_REVIEW_SUMMARY.md +439 -0
- package/docs/CONFIRMATION_AUTOMATIC_ERROR_RECOVERY.md +384 -0
- package/docs/DEEP_CODE_REVIEW_v2.7.33.md +1159 -0
- package/docs/DOCKER_TEST_RESULTS_v2.7.35.md +305 -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/features/automatic-error-recovery.md +333 -0
- package/docs/github-issues/README.md +88 -0
- package/docs/github-issues/wsl-enotempty-automatic-recovery.md +470 -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/docs/troubleshooting/wsl-better-sqlite3-error.md +239 -0
- package/package.json +5 -2
- package/scripts/create-github-issue.sh +64 -0
- package/scripts/test-docker-wsl.sh +198 -0
- package/src/cli/commands/mcp.ts +86 -9
- package/src/cli/init/index.ts +72 -42
- package/src/cli/simple-commands/init/agent-copier.js +10 -5
- package/src/core/DatabaseManager.ts +55 -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/src/utils/error-recovery.ts +325 -0
- package/docs/AGENTDB_V1.6.1_DEEP_REVIEW.md +0 -386
- package/docs/AGENT_FOLDER_STRUCTURE_FIX.md +0 -192
- package/docs/RECENT_RELEASES_SUMMARY.md +0 -375
- package/docs/V2.7.31_RELEASE_NOTES.md +0 -375
- /package/.claude/agents/analysis/{analyze-code-quality.md → code-review/analyze-code-quality.md} +0 -0
- /package/.claude/agents/architecture/{arch-system-design.md → system-design/arch-system-design.md} +0 -0
- /package/.claude/agents/data/{data-ml-model.md → ml/data-ml-model.md} +0 -0
- /package/.claude/agents/development/{dev-backend-api.md → backend/dev-backend-api.md} +0 -0
- /package/.claude/agents/devops/{ops-cicd-github.md → ci-cd/ops-cicd-github.md} +0 -0
- /package/.claude/agents/documentation/{docs-api-openapi.md → api-docs/docs-api-openapi.md} +0 -0
- /package/.claude/agents/specialized/{spec-mobile-react-native.md → mobile/spec-mobile-react-native.md} +0 -0
- /package/.claude/agents/testing/{tdd-london-swarm.md → unit/tdd-london-swarm.md} +0 -0
- /package/.claude/agents/testing/{production-validator.md → validation/production-validator.md} +0 -0
|
@@ -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,
|