mcp-probe-kit 3.3.0 → 3.6.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/README.md +781 -765
- package/build/index.js +198 -191
- package/build/lib/__tests__/agents-md-template.unit.test.js +1 -0
- package/build/lib/__tests__/dev-workflow.unit.test.d.ts +1 -0
- package/build/lib/__tests__/dev-workflow.unit.test.js +22 -0
- package/build/lib/__tests__/handles.unit.test.d.ts +1 -0
- package/build/lib/__tests__/handles.unit.test.js +19 -0
- package/build/lib/__tests__/mcp-apps.unit.test.d.ts +1 -0
- package/build/lib/__tests__/mcp-apps.unit.test.js +52 -0
- package/build/lib/__tests__/mcp-tool-skill-registry.unit.test.d.ts +1 -0
- package/build/lib/__tests__/mcp-tool-skill-registry.unit.test.js +18 -0
- package/build/lib/__tests__/memory-client.unit.test.js +105 -0
- package/build/lib/__tests__/spec-gate.unit.test.d.ts +1 -0
- package/build/lib/__tests__/spec-gate.unit.test.js +54 -0
- package/build/lib/__tests__/task-defaults.unit.test.d.ts +1 -0
- package/build/lib/__tests__/task-defaults.unit.test.js +27 -0
- package/build/lib/__tests__/workflow-skill-installer.unit.test.d.ts +1 -0
- package/build/lib/__tests__/workflow-skill-installer.unit.test.js +110 -0
- package/build/lib/__tests__/workspace-root.unit.test.d.ts +1 -0
- package/build/lib/__tests__/workspace-root.unit.test.js +31 -0
- package/build/lib/agents-md-template.js +10 -2
- package/build/lib/dev-workflow.d.ts +36 -0
- package/build/lib/dev-workflow.js +497 -0
- package/build/lib/handles.d.ts +31 -0
- package/build/lib/handles.js +36 -0
- package/build/lib/mcp-apps.d.ts +14 -0
- package/build/lib/mcp-apps.js +234 -0
- package/build/lib/mcp-tool-skill-registry.d.ts +46 -0
- package/build/lib/mcp-tool-skill-registry.js +256 -0
- package/build/lib/memory-client.d.ts +19 -0
- package/build/lib/memory-client.js +69 -0
- package/build/lib/memory-orchestration.d.ts +5 -0
- package/build/lib/memory-orchestration.js +20 -0
- package/build/lib/merge-agents-md.d.ts +2 -2
- package/build/lib/merge-agents-md.js +7 -4
- package/build/lib/output-schema-registry.d.ts +11 -0
- package/build/lib/output-schema-registry.js +80 -0
- package/build/lib/spec-gate.d.ts +32 -0
- package/build/lib/spec-gate.js +83 -0
- package/build/lib/task-defaults.d.ts +6 -0
- package/build/lib/task-defaults.js +20 -0
- package/build/lib/tool-annotations.js +3 -0
- package/build/lib/toolset-manager.js +2 -0
- package/build/lib/workflow-skill-installer.d.ts +40 -0
- package/build/lib/workflow-skill-installer.js +172 -0
- package/build/lib/workflow-skill-template.d.ts +9 -0
- package/build/lib/workflow-skill-template.js +85 -0
- package/build/lib/workflow-skill-version.d.ts +15 -0
- package/build/lib/workflow-skill-version.js +68 -0
- package/build/lib/workspace-root.d.ts +4 -0
- package/build/lib/workspace-root.js +33 -0
- package/build/schemas/basic-tools.d.ts +4 -0
- package/build/schemas/basic-tools.js +4 -0
- package/build/schemas/index.d.ts +114 -0
- package/build/schemas/index.js +1 -0
- package/build/schemas/memory-tools.d.ts +83 -0
- package/build/schemas/memory-tools.js +43 -0
- package/build/schemas/orchestration-tools.d.ts +8 -0
- package/build/schemas/orchestration-tools.js +8 -0
- package/build/schemas/output/code-insight-tools.d.ts +65 -0
- package/build/schemas/output/code-insight-tools.js +42 -0
- package/build/schemas/output/memory-tools.d.ts +576 -0
- package/build/schemas/output/memory-tools.js +148 -0
- package/build/schemas/project-tools.d.ts +19 -0
- package/build/schemas/project-tools.js +20 -0
- package/build/tools/__tests__/delete_memory_asset.unit.test.d.ts +1 -0
- package/build/tools/__tests__/delete_memory_asset.unit.test.js +87 -0
- package/build/tools/__tests__/search_memory.unit.test.js +1 -0
- package/build/tools/__tests__/start_bugfix.unit.test.js +34 -1
- package/build/tools/__tests__/update_memory_asset.unit.test.d.ts +1 -0
- package/build/tools/__tests__/update_memory_asset.unit.test.js +82 -0
- package/build/tools/__tests__/workflow.unit.test.d.ts +1 -0
- package/build/tools/__tests__/workflow.unit.test.js +24 -0
- package/build/tools/code_insight.js +5 -1
- package/build/tools/delete_memory_asset.d.ts +7 -0
- package/build/tools/delete_memory_asset.js +57 -0
- package/build/tools/index.d.ts +3 -0
- package/build/tools/index.js +3 -0
- package/build/tools/init_project.js +170 -158
- package/build/tools/search_memory.js +5 -2
- package/build/tools/start_bugfix.js +37 -4
- package/build/tools/start_feature.js +4 -3
- package/build/tools/start_ui.js +5 -4
- package/build/tools/update_memory_asset.d.ts +7 -0
- package/build/tools/update_memory_asset.js +98 -0
- package/build/tools/workflow.d.ts +13 -0
- package/build/tools/workflow.js +69 -0
- package/package.json +5 -3
package/build/index.js
CHANGED
|
@@ -5,11 +5,17 @@ import { InMemoryTaskMessageQueue, InMemoryTaskStore, } from "@modelcontextproto
|
|
|
5
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ProgressNotificationSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
-
import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, checkSpec, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport, searchMemory, readMemoryAsset, memorizeAsset, scanAndExtractPatterns } from "./tools/index.js";
|
|
8
|
+
import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, checkSpec, workflow, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport, searchMemory, readMemoryAsset, memorizeAsset, deleteMemoryAsset, updateMemoryAsset, scanAndExtractPatterns } from "./tools/index.js";
|
|
9
9
|
import { VERSION, NAME } from "./version.js";
|
|
10
10
|
import { allToolSchemas } from "./schemas/index.js";
|
|
11
11
|
import { filterTools, getToolsetFromEnv } from "./lib/toolset-manager.js";
|
|
12
12
|
import { withToolAnnotations } from "./lib/tool-annotations.js";
|
|
13
|
+
import { withOutputSchema } from "./lib/output-schema-registry.js";
|
|
14
|
+
import { shouldAutoEscalateToTask } from "./lib/task-defaults.js";
|
|
15
|
+
import { attachHandles } from "./lib/handles.js";
|
|
16
|
+
import { buildMcpAppHtml, isMcpUiAppTool } from "./lib/mcp-apps.js";
|
|
17
|
+
import { ensureMcpProbeKitBootstrapForToolCall, } from "./lib/workflow-skill-installer.js";
|
|
18
|
+
import { resolveWorkspaceRoot } from "./lib/workspace-root.js";
|
|
13
19
|
import { isAbortError, } from "./lib/tool-execution-context.js";
|
|
14
20
|
const EXTENSIONS_CAPABILITY_KEY = "io.github.mybolide/extensions";
|
|
15
21
|
const MAX_UI_APP_RESOURCES = 30;
|
|
@@ -82,80 +88,6 @@ function withTraceMeta(result, traceMeta) {
|
|
|
82
88
|
},
|
|
83
89
|
};
|
|
84
90
|
}
|
|
85
|
-
function escapeHtml(value) {
|
|
86
|
-
return value
|
|
87
|
-
.replace(/&/g, "&")
|
|
88
|
-
.replace(/</g, "<")
|
|
89
|
-
.replace(/>/g, ">")
|
|
90
|
-
.replace(/"/g, """)
|
|
91
|
-
.replace(/'/g, "'");
|
|
92
|
-
}
|
|
93
|
-
function isUiTool(name) {
|
|
94
|
-
return [
|
|
95
|
-
"ui_design_system",
|
|
96
|
-
"ui_search",
|
|
97
|
-
"sync_ui_data",
|
|
98
|
-
"start_ui",
|
|
99
|
-
"start_product",
|
|
100
|
-
].includes(name);
|
|
101
|
-
}
|
|
102
|
-
function buildUiResourceHtml(name, args, result) {
|
|
103
|
-
const structured = result.structuredContent
|
|
104
|
-
? JSON.stringify(result.structuredContent, null, 2)
|
|
105
|
-
: "{}";
|
|
106
|
-
const argJson = JSON.stringify(args ?? {}, null, 2);
|
|
107
|
-
const textBlocks = Array.isArray(result.content)
|
|
108
|
-
? result.content
|
|
109
|
-
.map((item) => {
|
|
110
|
-
if (!item || typeof item !== "object") {
|
|
111
|
-
return "";
|
|
112
|
-
}
|
|
113
|
-
const text = item.text;
|
|
114
|
-
return typeof text === "string" ? text : "";
|
|
115
|
-
})
|
|
116
|
-
.filter(Boolean)
|
|
117
|
-
.join("\n\n")
|
|
118
|
-
: "";
|
|
119
|
-
const now = new Date().toISOString();
|
|
120
|
-
return `<!doctype html>
|
|
121
|
-
<html lang="zh-CN">
|
|
122
|
-
<head>
|
|
123
|
-
<meta charset="utf-8">
|
|
124
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
125
|
-
<title>${escapeHtml(name)} · MCP Apps</title>
|
|
126
|
-
<style>
|
|
127
|
-
:root { color-scheme: light; }
|
|
128
|
-
body { font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; margin: 0; background: #f4f7fb; color: #1e2a35; }
|
|
129
|
-
.wrap { max-width: 960px; margin: 0 auto; padding: 24px; }
|
|
130
|
-
.card { background: #fff; border-radius: 14px; padding: 18px; box-shadow: 0 4px 18px rgba(30,42,53,.08); margin-bottom: 16px; }
|
|
131
|
-
h1 { margin: 0 0 8px; font-size: 24px; }
|
|
132
|
-
h2 { margin: 0 0 10px; font-size: 16px; color: #2f4a65; }
|
|
133
|
-
pre { white-space: pre-wrap; word-break: break-word; background: #0f1720; color: #d9e7f7; border-radius: 10px; padding: 12px; font-size: 12px; line-height: 1.45; }
|
|
134
|
-
.meta { color: #4f6880; font-size: 12px; }
|
|
135
|
-
</style>
|
|
136
|
-
</head>
|
|
137
|
-
<body>
|
|
138
|
-
<div class="wrap">
|
|
139
|
-
<div class="card">
|
|
140
|
-
<h1>${escapeHtml(name)}</h1>
|
|
141
|
-
<div class="meta">Generated at ${escapeHtml(now)} · MCP Apps preview</div>
|
|
142
|
-
</div>
|
|
143
|
-
<div class="card">
|
|
144
|
-
<h2>Text Output</h2>
|
|
145
|
-
<pre>${escapeHtml(textBlocks || "(no text output)")}</pre>
|
|
146
|
-
</div>
|
|
147
|
-
<div class="card">
|
|
148
|
-
<h2>Structured Content</h2>
|
|
149
|
-
<pre>${escapeHtml(structured)}</pre>
|
|
150
|
-
</div>
|
|
151
|
-
<div class="card">
|
|
152
|
-
<h2>Arguments</h2>
|
|
153
|
-
<pre>${escapeHtml(argJson)}</pre>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
</body>
|
|
157
|
-
</html>`;
|
|
158
|
-
}
|
|
159
91
|
function putUiAppResource(toolName, args, result) {
|
|
160
92
|
const uid = `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
161
93
|
const uri = `ui://mcp-probe-kit/${toolName}/${uid}`;
|
|
@@ -164,7 +96,7 @@ function putUiAppResource(toolName, args, result) {
|
|
|
164
96
|
name: `UI Preview · ${toolName}`,
|
|
165
97
|
description: `MCP Apps preview generated by ${toolName}`,
|
|
166
98
|
mimeType: "text/html",
|
|
167
|
-
text:
|
|
99
|
+
text: buildMcpAppHtml(toolName, args, result),
|
|
168
100
|
createdAt: new Date().toISOString(),
|
|
169
101
|
};
|
|
170
102
|
uiAppResources.set(uri, entry);
|
|
@@ -253,6 +185,53 @@ function renderGraphSnapshotMarkdown(snapshot) {
|
|
|
253
185
|
"",
|
|
254
186
|
].join("\n");
|
|
255
187
|
}
|
|
188
|
+
function buildGraphHistorySummary(summaryLimit = 200) {
|
|
189
|
+
const items = graphSnapshotOrder
|
|
190
|
+
.slice()
|
|
191
|
+
.reverse()
|
|
192
|
+
.map((id) => graphSnapshots.get(id))
|
|
193
|
+
.filter((item) => Boolean(item))
|
|
194
|
+
.map((item) => ({
|
|
195
|
+
id: item.id,
|
|
196
|
+
uri: item.uri,
|
|
197
|
+
toolName: item.toolName,
|
|
198
|
+
createdAt: item.createdAt,
|
|
199
|
+
status: item.status,
|
|
200
|
+
summary: trimText(item.summary, summaryLimit),
|
|
201
|
+
files: {
|
|
202
|
+
json: item.jsonFilePath ?? null,
|
|
203
|
+
markdown: item.markdownFilePath ?? null,
|
|
204
|
+
},
|
|
205
|
+
}));
|
|
206
|
+
return { count: items.length, items };
|
|
207
|
+
}
|
|
208
|
+
function buildGraphFilesIndex(fileLimit = 40) {
|
|
209
|
+
const latestId = graphSnapshotOrder[graphSnapshotOrder.length - 1];
|
|
210
|
+
const latest = latestId ? (graphSnapshots.get(latestId) ?? null) : null;
|
|
211
|
+
const hasDir = fs.existsSync(graphSnapshotDir);
|
|
212
|
+
const files = hasDir
|
|
213
|
+
? fs
|
|
214
|
+
.readdirSync(graphSnapshotDir, { withFileTypes: true })
|
|
215
|
+
.filter((entry) => entry.isFile() && /\.(json|md)$/i.test(entry.name))
|
|
216
|
+
.map((entry) => toPosixPath(path.join(graphSnapshotDir, entry.name)))
|
|
217
|
+
.sort((a, b) => b.localeCompare(a))
|
|
218
|
+
.slice(0, fileLimit)
|
|
219
|
+
: [];
|
|
220
|
+
return {
|
|
221
|
+
snapshotDir: toPosixPath(graphSnapshotDir),
|
|
222
|
+
exists: hasDir,
|
|
223
|
+
latest: latest
|
|
224
|
+
? {
|
|
225
|
+
id: latest.id,
|
|
226
|
+
uri: latest.uri,
|
|
227
|
+
toolName: latest.toolName,
|
|
228
|
+
jsonFilePath: latest.jsonFilePath ?? null,
|
|
229
|
+
markdownFilePath: latest.markdownFilePath ?? null,
|
|
230
|
+
}
|
|
231
|
+
: null,
|
|
232
|
+
files,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
256
235
|
function persistGraphSnapshot(snapshot) {
|
|
257
236
|
try {
|
|
258
237
|
ensureGraphSnapshotDir();
|
|
@@ -384,13 +363,49 @@ function rememberGraphSnapshot(toolName, result) {
|
|
|
384
363
|
}
|
|
385
364
|
return snapshot;
|
|
386
365
|
}
|
|
387
|
-
function
|
|
366
|
+
function withStructuredHandles(result, handles) {
|
|
367
|
+
if (!result.structuredContent || typeof result.structuredContent !== "object" || Array.isArray(result.structuredContent)) {
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
return {
|
|
371
|
+
...result,
|
|
372
|
+
structuredContent: attachHandles(result.structuredContent, handles),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function withBootstrapMeta(result, bootstrap) {
|
|
376
|
+
if (!bootstrap) {
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
const base = result.structuredContent &&
|
|
380
|
+
typeof result.structuredContent === "object" &&
|
|
381
|
+
!Array.isArray(result.structuredContent)
|
|
382
|
+
? result.structuredContent
|
|
383
|
+
: {};
|
|
384
|
+
return {
|
|
385
|
+
...result,
|
|
386
|
+
structuredContent: {
|
|
387
|
+
...base,
|
|
388
|
+
mcp_probe_bootstrap: {
|
|
389
|
+
projectRoot: bootstrap.projectRoot,
|
|
390
|
+
skill: bootstrap.skill,
|
|
391
|
+
agentsMd: bootstrap.agentsMd,
|
|
392
|
+
workspaceWarning: bootstrap.workspaceWarning ?? null,
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function decorateResult(toolName, args, raw, traceMeta, bootstrap = null) {
|
|
388
398
|
let result = withTraceMeta(raw, traceMeta);
|
|
399
|
+
result = withBootstrapMeta(result, bootstrap);
|
|
389
400
|
const snapshot = rememberGraphSnapshot(toolName, result);
|
|
390
401
|
if (snapshot) {
|
|
391
402
|
result = withGraphSnapshotMeta(result, snapshot);
|
|
403
|
+
result = withStructuredHandles(result, {
|
|
404
|
+
graph_snapshot: snapshot.uri,
|
|
405
|
+
graph_resource: snapshot.uri,
|
|
406
|
+
});
|
|
392
407
|
}
|
|
393
|
-
if (uiAppsEnabled &&
|
|
408
|
+
if (uiAppsEnabled && isMcpUiAppTool(toolName) && !result.isError) {
|
|
394
409
|
const resourceUri = putUiAppResource(toolName, args, result);
|
|
395
410
|
result = withUiResourceMeta(result, resourceUri);
|
|
396
411
|
}
|
|
@@ -411,68 +426,122 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
411
426
|
const filteredTools = filterTools(allToolSchemas, toolset);
|
|
412
427
|
console.error(`[MCP Probe Kit] 当前工具集: ${toolset} (${filteredTools.length}/${allToolSchemas.length} 个工具)`);
|
|
413
428
|
return {
|
|
414
|
-
tools: filteredTools.map(withToolAnnotations),
|
|
429
|
+
tools: filteredTools.map((tool) => withOutputSchema(withToolAnnotations(tool))),
|
|
415
430
|
};
|
|
416
431
|
});
|
|
417
432
|
async function executeTool(name, args, context) {
|
|
433
|
+
const bootstrap = ensureMcpProbeKitBootstrapForToolCall(name, args);
|
|
434
|
+
if (bootstrap?.workspaceWarning) {
|
|
435
|
+
console.error(`[MCP Probe Kit] ${bootstrap.workspaceWarning}`);
|
|
436
|
+
}
|
|
437
|
+
if (bootstrap?.skill.created) {
|
|
438
|
+
console.error(`[MCP Probe Kit] 已创建 MCP Skill: ${bootstrap.skill.skillRelPath} (v${bootstrap.skill.version})`);
|
|
439
|
+
}
|
|
440
|
+
else if (bootstrap?.skill.updated) {
|
|
441
|
+
console.error(`[MCP Probe Kit] 已升级 MCP Skill: ${bootstrap.skill.skillRelPath} ${bootstrap.skill.previousVersion ?? "?"} → v${bootstrap.skill.version}`);
|
|
442
|
+
}
|
|
443
|
+
if (bootstrap?.agentsMd.created) {
|
|
444
|
+
console.error(`[MCP Probe Kit] 已创建 AGENTS.md(含 Skill 引用): ${bootstrap.agentsMd.path}`);
|
|
445
|
+
}
|
|
446
|
+
else if (bootstrap?.agentsMd.updated) {
|
|
447
|
+
console.error(`[MCP Probe Kit] 已更新 AGENTS.md(添加 Skill 引用): ${bootstrap.agentsMd.path}`);
|
|
448
|
+
}
|
|
449
|
+
let result;
|
|
418
450
|
switch (name) {
|
|
419
451
|
case "init_project":
|
|
420
|
-
|
|
452
|
+
result = (await initProject(args));
|
|
453
|
+
break;
|
|
421
454
|
case "gencommit":
|
|
422
|
-
|
|
455
|
+
result = (await gencommit(args));
|
|
456
|
+
break;
|
|
423
457
|
case "code_review":
|
|
424
|
-
|
|
458
|
+
result = (await codeReview(args));
|
|
459
|
+
break;
|
|
425
460
|
case "code_insight":
|
|
426
|
-
|
|
461
|
+
result = (await codeInsight(args, context));
|
|
462
|
+
break;
|
|
427
463
|
case "gentest":
|
|
428
|
-
|
|
464
|
+
result = (await gentest(args));
|
|
465
|
+
break;
|
|
429
466
|
case "refactor":
|
|
430
|
-
|
|
467
|
+
result = (await refactor(args));
|
|
468
|
+
break;
|
|
431
469
|
case "init_project_context":
|
|
432
|
-
|
|
470
|
+
result = (await initProjectContext(args));
|
|
471
|
+
break;
|
|
472
|
+
case "workflow":
|
|
473
|
+
result = (await workflow(args));
|
|
474
|
+
break;
|
|
433
475
|
case "add_feature":
|
|
434
|
-
|
|
476
|
+
result = (await addFeature(args));
|
|
477
|
+
break;
|
|
435
478
|
case "check_spec":
|
|
436
|
-
|
|
479
|
+
result = (await checkSpec(args));
|
|
480
|
+
break;
|
|
437
481
|
case "fix_bug":
|
|
438
|
-
|
|
482
|
+
result = (await fixBug(args));
|
|
483
|
+
break;
|
|
439
484
|
case "estimate":
|
|
440
|
-
|
|
485
|
+
result = (await estimate(args));
|
|
486
|
+
break;
|
|
441
487
|
case "start_feature":
|
|
442
|
-
|
|
488
|
+
result = (await startFeature(args, context));
|
|
489
|
+
break;
|
|
443
490
|
case "start_bugfix":
|
|
444
|
-
|
|
491
|
+
result = (await startBugfix(args, context));
|
|
492
|
+
break;
|
|
445
493
|
case "start_onboard":
|
|
446
|
-
|
|
494
|
+
result = (await startOnboard(args, context));
|
|
495
|
+
break;
|
|
447
496
|
case "start_ralph":
|
|
448
|
-
|
|
497
|
+
result = (await startRalph(args, context));
|
|
498
|
+
break;
|
|
449
499
|
case "interview":
|
|
450
|
-
|
|
500
|
+
result = (await interview(args));
|
|
501
|
+
break;
|
|
451
502
|
case "ask_user":
|
|
452
|
-
|
|
503
|
+
result = (await askUser(args));
|
|
504
|
+
break;
|
|
453
505
|
case "ui_design_system":
|
|
454
|
-
|
|
506
|
+
result = (await uiDesignSystem(args));
|
|
507
|
+
break;
|
|
455
508
|
case "ui_search":
|
|
456
|
-
|
|
509
|
+
result = (await uiSearch(args));
|
|
510
|
+
break;
|
|
457
511
|
case "sync_ui_data":
|
|
458
|
-
|
|
512
|
+
result = (await syncUiData(args, context));
|
|
513
|
+
break;
|
|
459
514
|
case "start_ui":
|
|
460
|
-
|
|
515
|
+
result = (await startUi(args, context));
|
|
516
|
+
break;
|
|
461
517
|
case "start_product":
|
|
462
|
-
|
|
518
|
+
result = (await startProduct((args ?? {}), context));
|
|
519
|
+
break;
|
|
463
520
|
case "git_work_report":
|
|
464
|
-
|
|
521
|
+
result = (await gitWorkReport(args));
|
|
522
|
+
break;
|
|
465
523
|
case "search_memory":
|
|
466
|
-
|
|
524
|
+
result = (await searchMemory(args));
|
|
525
|
+
break;
|
|
467
526
|
case "read_memory_asset":
|
|
468
|
-
|
|
527
|
+
result = (await readMemoryAsset(args));
|
|
528
|
+
break;
|
|
469
529
|
case "memorize_asset":
|
|
470
|
-
|
|
530
|
+
result = (await memorizeAsset(args));
|
|
531
|
+
break;
|
|
532
|
+
case "delete_memory_asset":
|
|
533
|
+
result = (await deleteMemoryAsset(args));
|
|
534
|
+
break;
|
|
535
|
+
case "update_memory_asset":
|
|
536
|
+
result = (await updateMemoryAsset(args));
|
|
537
|
+
break;
|
|
471
538
|
case "scan_and_extract_patterns":
|
|
472
|
-
|
|
539
|
+
result = (await scanAndExtractPatterns(args));
|
|
540
|
+
break;
|
|
473
541
|
default:
|
|
474
542
|
throw new Error(`未知工具: ${name}`);
|
|
475
543
|
}
|
|
544
|
+
return { bootstrap, result };
|
|
476
545
|
}
|
|
477
546
|
function makeToolError(errorMessage) {
|
|
478
547
|
return {
|
|
@@ -491,8 +560,11 @@ function isTerminalTaskStatus(status) {
|
|
|
491
560
|
// 处理工具调用
|
|
492
561
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
493
562
|
const { name, arguments: args } = request.params;
|
|
494
|
-
|
|
563
|
+
let taskRequest = request.params.task;
|
|
495
564
|
const traceMeta = getTraceMeta(extra._meta);
|
|
565
|
+
if (shouldAutoEscalateToTask(name, Boolean(taskRequest))) {
|
|
566
|
+
taskRequest = taskRequest ?? {};
|
|
567
|
+
}
|
|
496
568
|
const emitProgress = async (progress, message) => {
|
|
497
569
|
const progressToken = extra._meta?.progressToken;
|
|
498
570
|
if (progressToken === undefined) {
|
|
@@ -562,11 +634,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
562
634
|
void (async () => {
|
|
563
635
|
try {
|
|
564
636
|
await taskContext.reportProgress?.(5, `开始执行工具: ${name}`);
|
|
565
|
-
const rawResult = await executeTool(name, args, taskContext);
|
|
637
|
+
const { bootstrap, result: rawResult } = await executeTool(name, args, taskContext);
|
|
566
638
|
if (!rawResult || typeof rawResult !== "object") {
|
|
567
639
|
throw new Error(`工具 ${name} 返回了无效响应`);
|
|
568
640
|
}
|
|
569
|
-
const result = decorateResult(name, args, rawResult, traceMeta);
|
|
641
|
+
const result = decorateResult(name, args, rawResult, traceMeta, bootstrap);
|
|
570
642
|
const latestTask = await extra.taskStore?.getTask(task.taskId);
|
|
571
643
|
if (!latestTask || isTerminalTaskStatus(latestTask.status)) {
|
|
572
644
|
return;
|
|
@@ -617,12 +689,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
617
689
|
try {
|
|
618
690
|
ensureNotAborted();
|
|
619
691
|
await emitProgress(5, `开始执行工具: ${name}`);
|
|
620
|
-
const rawResult = await executeTool(name, args, toolContext);
|
|
692
|
+
const { bootstrap, result: rawResult } = await executeTool(name, args, toolContext);
|
|
621
693
|
if (!rawResult || typeof rawResult !== "object") {
|
|
622
694
|
throw new Error(`工具 ${name} 返回了无效响应`);
|
|
623
695
|
}
|
|
624
696
|
ensureNotAborted();
|
|
625
|
-
const result = decorateResult(name, args, rawResult, traceMeta);
|
|
697
|
+
const result = decorateResult(name, args, rawResult, traceMeta, bootstrap);
|
|
626
698
|
await emitProgress(100, `工具执行完成: ${name}`);
|
|
627
699
|
return result;
|
|
628
700
|
}
|
|
@@ -637,7 +709,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
637
709
|
return withTraceMeta(makeToolError(errorMessage), traceMeta);
|
|
638
710
|
}
|
|
639
711
|
});
|
|
640
|
-
//
|
|
712
|
+
// 定义资源列表(Cursor 设置页会展示为条目;仅保留 status + graph/latest,其余 URI 仍可 ReadResource)
|
|
641
713
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
642
714
|
const resources = [
|
|
643
715
|
{
|
|
@@ -648,41 +720,11 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
648
720
|
},
|
|
649
721
|
{
|
|
650
722
|
uri: "probe://graph/latest",
|
|
651
|
-
name: "
|
|
652
|
-
description: "
|
|
653
|
-
mimeType: "application/json",
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
uri: "probe://graph/history",
|
|
657
|
-
name: "图谱快照(历史)",
|
|
658
|
-
description: `最近 ${graphSnapshotOrder.length} 条图谱快照摘要`,
|
|
659
|
-
mimeType: "application/json",
|
|
660
|
-
},
|
|
661
|
-
{
|
|
662
|
-
uri: "probe://graph/latest.md",
|
|
663
|
-
name: "图谱快照(最新 Markdown)",
|
|
664
|
-
description: "最近一次图谱快照的 Markdown 视图",
|
|
665
|
-
mimeType: "text/markdown",
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
uri: "probe://graph/files",
|
|
669
|
-
name: "图谱快照(文件索引)",
|
|
670
|
-
description: `图谱快照落盘目录: ${toPosixPath(graphSnapshotDir)}`,
|
|
723
|
+
name: "图谱快照",
|
|
724
|
+
description: "最新图谱快照(含历史摘要与落盘索引;Markdown 可读 probe://graph/latest.md)",
|
|
671
725
|
mimeType: "application/json",
|
|
672
726
|
},
|
|
673
727
|
];
|
|
674
|
-
for (const id of graphSnapshotOrder.slice().reverse().slice(0, 10)) {
|
|
675
|
-
const snapshot = graphSnapshots.get(id);
|
|
676
|
-
if (!snapshot) {
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
|
-
resources.push({
|
|
680
|
-
uri: snapshot.uri,
|
|
681
|
-
name: `图谱快照 · ${snapshot.toolName}`,
|
|
682
|
-
description: `${snapshot.status} · ${trimText(snapshot.summary, 120)} (${snapshot.createdAt})`,
|
|
683
|
-
mimeType: "application/json",
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
728
|
if (uiAppsEnabled) {
|
|
687
729
|
for (const uri of uiAppResourceOrder.slice().reverse()) {
|
|
688
730
|
const entry = uiAppResources.get(uri);
|
|
@@ -810,6 +852,13 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
810
852
|
json: snapshot.jsonFilePath ?? null,
|
|
811
853
|
markdown: snapshot.markdownFilePath ?? null,
|
|
812
854
|
},
|
|
855
|
+
history: buildGraphHistorySummary(),
|
|
856
|
+
fileIndex: buildGraphFilesIndex(),
|
|
857
|
+
relatedUris: {
|
|
858
|
+
markdown: "probe://graph/latest.md",
|
|
859
|
+
history: "probe://graph/history",
|
|
860
|
+
files: "probe://graph/files",
|
|
861
|
+
},
|
|
813
862
|
}, null, 2),
|
|
814
863
|
},
|
|
815
864
|
],
|
|
@@ -846,67 +895,24 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
846
895
|
};
|
|
847
896
|
}
|
|
848
897
|
if (uri === "probe://graph/history") {
|
|
849
|
-
const history =
|
|
850
|
-
.slice()
|
|
851
|
-
.reverse()
|
|
852
|
-
.map((id) => graphSnapshots.get(id))
|
|
853
|
-
.filter((item) => Boolean(item))
|
|
854
|
-
.map((item) => ({
|
|
855
|
-
id: item.id,
|
|
856
|
-
uri: item.uri,
|
|
857
|
-
toolName: item.toolName,
|
|
858
|
-
createdAt: item.createdAt,
|
|
859
|
-
status: item.status,
|
|
860
|
-
summary: trimText(item.summary, 200),
|
|
861
|
-
files: {
|
|
862
|
-
json: item.jsonFilePath ?? null,
|
|
863
|
-
markdown: item.markdownFilePath ?? null,
|
|
864
|
-
},
|
|
865
|
-
}));
|
|
898
|
+
const history = buildGraphHistorySummary();
|
|
866
899
|
return {
|
|
867
900
|
contents: [
|
|
868
901
|
{
|
|
869
902
|
uri,
|
|
870
903
|
mimeType: "application/json",
|
|
871
|
-
text: JSON.stringify(
|
|
872
|
-
count: history.length,
|
|
873
|
-
items: history,
|
|
874
|
-
}, null, 2),
|
|
904
|
+
text: JSON.stringify(history, null, 2),
|
|
875
905
|
},
|
|
876
906
|
],
|
|
877
907
|
};
|
|
878
908
|
}
|
|
879
909
|
if (uri === "probe://graph/files") {
|
|
880
|
-
const latestId = graphSnapshotOrder[graphSnapshotOrder.length - 1];
|
|
881
|
-
const latest = latestId ? graphSnapshots.get(latestId) ?? null : null;
|
|
882
|
-
const hasDir = fs.existsSync(graphSnapshotDir);
|
|
883
|
-
const files = hasDir
|
|
884
|
-
? fs
|
|
885
|
-
.readdirSync(graphSnapshotDir, { withFileTypes: true })
|
|
886
|
-
.filter((entry) => entry.isFile() && /\.(json|md)$/i.test(entry.name))
|
|
887
|
-
.map((entry) => toPosixPath(path.join(graphSnapshotDir, entry.name)))
|
|
888
|
-
.sort((a, b) => b.localeCompare(a))
|
|
889
|
-
.slice(0, 40)
|
|
890
|
-
: [];
|
|
891
910
|
return {
|
|
892
911
|
contents: [
|
|
893
912
|
{
|
|
894
913
|
uri,
|
|
895
914
|
mimeType: "application/json",
|
|
896
|
-
text: JSON.stringify(
|
|
897
|
-
snapshotDir: toPosixPath(graphSnapshotDir),
|
|
898
|
-
exists: hasDir,
|
|
899
|
-
latest: latest
|
|
900
|
-
? {
|
|
901
|
-
id: latest.id,
|
|
902
|
-
uri: latest.uri,
|
|
903
|
-
toolName: latest.toolName,
|
|
904
|
-
jsonFilePath: latest.jsonFilePath ?? null,
|
|
905
|
-
markdownFilePath: latest.markdownFilePath ?? null,
|
|
906
|
-
}
|
|
907
|
-
: null,
|
|
908
|
-
files,
|
|
909
|
-
}, null, 2),
|
|
915
|
+
text: JSON.stringify(buildGraphFilesIndex(), null, 2),
|
|
910
916
|
},
|
|
911
917
|
],
|
|
912
918
|
};
|
|
@@ -948,7 +954,8 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
948
954
|
async function main() {
|
|
949
955
|
const transport = new StdioServerTransport();
|
|
950
956
|
await server.connect(transport);
|
|
951
|
-
|
|
957
|
+
const workspace = resolveWorkspaceRoot("");
|
|
958
|
+
console.error(`MCP Probe Kit v${VERSION} 已启动 | workspace=${workspace}`);
|
|
952
959
|
}
|
|
953
960
|
// 启动服务器
|
|
954
961
|
main().catch((error) => {
|
|
@@ -20,6 +20,7 @@ describe('generateAgentsMdInner', () => {
|
|
|
20
20
|
expect(md).toContain('记忆');
|
|
21
21
|
expect(md).toContain('start_bugfix');
|
|
22
22
|
expect(md).toContain('memorize_asset');
|
|
23
|
+
expect(md).toContain('mcp-probe-kit/SKILL.md');
|
|
23
24
|
expect(md).toContain('bugfix');
|
|
24
25
|
expect(md).toContain('search_memory');
|
|
25
26
|
expect(md).toContain('自动注入');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { buildDevWorkflow, detectWorkflowScenario } from '../dev-workflow.js';
|
|
3
|
+
describe('dev-workflow', () => {
|
|
4
|
+
test('识别 bugfix 场景', () => {
|
|
5
|
+
const result = detectWorkflowScenario('登录报错 NullReference 需要排查修复');
|
|
6
|
+
expect(result.scenario).toBe('bugfix');
|
|
7
|
+
});
|
|
8
|
+
test('识别 feature 场景', () => {
|
|
9
|
+
const result = detectWorkflowScenario('开发用户认证新功能');
|
|
10
|
+
expect(result.scenario).toBe('feature');
|
|
11
|
+
});
|
|
12
|
+
test('bugfix 计划首工具为 start_bugfix', () => {
|
|
13
|
+
const plan = buildDevWorkflow('TypeError in checkout');
|
|
14
|
+
expect(plan.firstTool).toBe('start_bugfix');
|
|
15
|
+
expect(plan.phases.some((p) => p.steps.some((s) => s.tool === 'start_bugfix'))).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
test('显式 scenario 覆盖推断', () => {
|
|
18
|
+
const plan = buildDevWorkflow('随便', { scenario: 'ui' });
|
|
19
|
+
expect(plan.scenario).toBe('ui');
|
|
20
|
+
expect(plan.firstTool).toBe('start_ui');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { attachHandles, buildMemoryAssetHandles, mergeHandles, } from '../../lib/handles.js';
|
|
3
|
+
describe('handles', () => {
|
|
4
|
+
test('buildMemoryAssetHandles 生成 read 默认工具名', () => {
|
|
5
|
+
const handles = buildMemoryAssetHandles([{ id: 'a-1', name: 'demo' }]);
|
|
6
|
+
expect(handles).toEqual([{ id: 'a-1', name: 'demo', tool: 'read_memory_asset' }]);
|
|
7
|
+
});
|
|
8
|
+
test('attachHandles 合并已有 handles', () => {
|
|
9
|
+
const result = attachHandles({ handles: { graph_resource: 'probe://graph/latest' } }, { memory_assets: buildMemoryAssetHandles([{ id: 'x' }]) });
|
|
10
|
+
expect(result.handles.graph_resource).toBe('probe://graph/latest');
|
|
11
|
+
expect(result.handles.memory_assets).toHaveLength(1);
|
|
12
|
+
});
|
|
13
|
+
test('mergeHandles 后者覆盖 graph 字段', () => {
|
|
14
|
+
expect(mergeHandles({ graph_resource: 'a' }, { graph_resource: 'b', graph_snapshot: 'probe://graph/s1' })).toEqual({
|
|
15
|
+
graph_resource: 'b',
|
|
16
|
+
graph_snapshot: 'probe://graph/s1',
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { buildCodeInsightAppHtml, buildSearchMemoryAppHtml, isMcpUiAppTool, } from '../mcp-apps.js';
|
|
3
|
+
describe('mcp-apps', () => {
|
|
4
|
+
test('search_memory 与 code_insight 属于 UI App 工具', () => {
|
|
5
|
+
expect(isMcpUiAppTool('search_memory')).toBe(true);
|
|
6
|
+
expect(isMcpUiAppTool('code_insight')).toBe(true);
|
|
7
|
+
expect(isMcpUiAppTool('gencommit')).toBe(false);
|
|
8
|
+
});
|
|
9
|
+
test('search_memory App 渲染命中卡片与 handles', () => {
|
|
10
|
+
const html = buildSearchMemoryAppHtml({ query: 'proxy bug' }, {
|
|
11
|
+
structuredContent: {
|
|
12
|
+
query: 'proxy bug',
|
|
13
|
+
count: 1,
|
|
14
|
+
results: [
|
|
15
|
+
{
|
|
16
|
+
id: 'asset-1',
|
|
17
|
+
name: 'feishu-proxy',
|
|
18
|
+
type: 'bugfix',
|
|
19
|
+
score: 0.88,
|
|
20
|
+
summary: 'proxy mismatch',
|
|
21
|
+
content: '【修复】done',
|
|
22
|
+
tags: ['bugfix'],
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
handles: {
|
|
26
|
+
memory_assets: [{ id: 'asset-1', tool: 'read_memory_asset' }],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
expect(html).toContain('search_memory');
|
|
31
|
+
expect(html).toContain('feishu-proxy');
|
|
32
|
+
expect(html).toContain('asset-1');
|
|
33
|
+
expect(html).toContain('read_memory_asset');
|
|
34
|
+
expect(html).toContain('proxy mismatch');
|
|
35
|
+
});
|
|
36
|
+
test('code_insight App 渲染状态与执行表', () => {
|
|
37
|
+
const html = buildCodeInsightAppHtml({ mode: 'auto' }, {
|
|
38
|
+
structuredContent: {
|
|
39
|
+
status: 'ok',
|
|
40
|
+
provider: 'gitnexus',
|
|
41
|
+
summary: 'login flow analyzed',
|
|
42
|
+
mode: { requested: 'auto', resolved: 'query' },
|
|
43
|
+
executions: [{ tool: 'query', ok: true, text: 'found login handler' }],
|
|
44
|
+
handles: { graph_resource: 'probe://graph/latest' },
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
expect(html).toContain('code_insight');
|
|
48
|
+
expect(html).toContain('login flow analyzed');
|
|
49
|
+
expect(html).toContain('probe://graph/latest');
|
|
50
|
+
expect(html).toContain('found login handler');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|