mcp-probe-kit 3.0.3 → 3.0.6
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 +457 -423
- package/build/index.js +434 -140
- package/build/lib/template-loader.js +317 -317
- package/build/lib/tool-execution-context.d.ts +8 -0
- package/build/lib/tool-execution-context.js +20 -0
- package/build/schemas/git-tools.js +16 -16
- package/build/tools/__tests__/start_bugfix.unit.test.js +14 -14
- package/build/tools/__tests__/start_ui.unit.test.js +11 -11
- package/build/tools/add_feature.js +79 -79
- package/build/tools/ask_user.js +5 -5
- package/build/tools/interview.js +9 -9
- package/build/tools/start_bugfix.d.ts +2 -1
- package/build/tools/start_bugfix.js +131 -122
- package/build/tools/start_feature.d.ts +2 -1
- package/build/tools/start_feature.js +113 -104
- package/build/tools/start_onboard.d.ts +2 -1
- package/build/tools/start_onboard.js +57 -51
- package/build/tools/start_product.d.ts +2 -1
- package/build/tools/start_product.js +9 -1
- package/build/tools/start_ralph.d.ts +2 -1
- package/build/tools/start_ralph.js +9 -3
- package/build/tools/start_ui.d.ts +2 -1
- package/build/tools/start_ui.js +102 -88
- package/build/tools/ui-ux-tools.d.ts +2 -1
- package/build/tools/ui-ux-tools.js +19 -3
- package/build/utils/ui-sync.d.ts +6 -2
- package/build/utils/ui-sync.js +125 -29
- package/docs/assets/font/MaterialSymbolsOutlined.codepoints +4102 -0
- package/docs/assets/font/MaterialSymbolsOutlined.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-400.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-700.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-900.ttf +0 -0
- package/docs/assets/js/i18n.js +375 -0
- package/docs/assets/js/tailwind.js +83 -83
- package/docs/assets/logo-zh.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/data/tools.js +21 -21
- package/docs/debug-i18n.html +163 -0
- package/docs/i18n/all-tools/en.json +157 -0
- package/docs/i18n/all-tools/ja.json +157 -0
- package/docs/i18n/all-tools/ko.json +157 -0
- package/docs/i18n/all-tools/zh-CN.json +157 -0
- package/docs/i18n/en.json +518 -0
- package/docs/i18n/ja.json +518 -0
- package/docs/i18n/ko.json +518 -0
- package/docs/i18n/zh-CN.json +518 -0
- package/docs/index.html +43 -32
- package/docs/pages/all-tools.html +514 -330
- package/docs/pages/examples.html +689 -673
- package/docs/pages/getting-started.html +589 -577
- package/docs/pages/migration.html +298 -283
- package/package.json +6 -6
- package/docs/project-context/architecture.md +0 -0
- package/docs/project-context/how-to-develop.md +0 -313
- package/docs/project-context/how-to-test.md +0 -457
- package/docs/project-context/tech-stack.md +0 -96
- package/docs/project-context.md +0 -53
- package/docs/specs/git-work-report/design.md +0 -568
- package/docs/specs/git-work-report/requirements.md +0 -131
- package/docs/specs/git-work-report/tasks.md +0 -197
package/build/index.js
CHANGED
|
@@ -1,20 +1,199 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import {
|
|
4
|
+
import { InMemoryTaskMessageQueue, InMemoryTaskStore, } from "@modelcontextprotocol/sdk/experimental/index.js";
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ProgressNotificationSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
6
|
import { initProject, gencommit, codeReview, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport } from "./tools/index.js";
|
|
6
7
|
import { VERSION, NAME } from "./version.js";
|
|
7
8
|
import { allToolSchemas } from "./schemas/index.js";
|
|
8
9
|
import { filterTools, getToolsetFromEnv } from "./lib/toolset-manager.js";
|
|
10
|
+
import { isAbortError, } from "./lib/tool-execution-context.js";
|
|
11
|
+
const EXTENSIONS_CAPABILITY_KEY = "io.github.mybolide/extensions";
|
|
12
|
+
const MAX_UI_APP_RESOURCES = 30;
|
|
13
|
+
const uiAppResources = new Map();
|
|
14
|
+
const uiAppResourceOrder = [];
|
|
15
|
+
function isEnvEnabled(name, fallback = false) {
|
|
16
|
+
const raw = process.env[name];
|
|
17
|
+
if (raw === undefined) {
|
|
18
|
+
return fallback;
|
|
19
|
+
}
|
|
20
|
+
return /^(1|true|yes|on)$/i.test(raw.trim());
|
|
21
|
+
}
|
|
22
|
+
const extensionsCapabilityEnabled = isEnvEnabled("MCP_ENABLE_EXTENSIONS_CAPABILITY", false);
|
|
23
|
+
const uiAppsEnabled = isEnvEnabled("MCP_ENABLE_UI_APPS", false);
|
|
24
|
+
const traceMetaKey = process.env.MCP_TRACE_META_KEY || "trace";
|
|
25
|
+
const serverCapabilities = {
|
|
26
|
+
tools: {},
|
|
27
|
+
resources: {},
|
|
28
|
+
tasks: {
|
|
29
|
+
list: {},
|
|
30
|
+
cancel: {},
|
|
31
|
+
requests: {
|
|
32
|
+
tools: {
|
|
33
|
+
call: {},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
if (extensionsCapabilityEnabled) {
|
|
39
|
+
serverCapabilities.experimental = {
|
|
40
|
+
[EXTENSIONS_CAPABILITY_KEY]: {
|
|
41
|
+
traceMetaPassthrough: true,
|
|
42
|
+
traceMetaKey,
|
|
43
|
+
uiApps: uiAppsEnabled,
|
|
44
|
+
uiAppsMetaKey: "ui.resourceUri",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function getTraceMeta(meta) {
|
|
49
|
+
if (!meta || typeof meta !== "object") {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const metaRecord = meta;
|
|
53
|
+
if (traceMetaKey in metaRecord) {
|
|
54
|
+
return metaRecord[traceMetaKey];
|
|
55
|
+
}
|
|
56
|
+
return metaRecord.trace;
|
|
57
|
+
}
|
|
58
|
+
function withTraceMeta(result, traceMeta) {
|
|
59
|
+
if (traceMeta === undefined) {
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...result,
|
|
64
|
+
_meta: {
|
|
65
|
+
...(result._meta ?? {}),
|
|
66
|
+
[traceMetaKey]: traceMeta,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function escapeHtml(value) {
|
|
71
|
+
return value
|
|
72
|
+
.replace(/&/g, "&")
|
|
73
|
+
.replace(/</g, "<")
|
|
74
|
+
.replace(/>/g, ">")
|
|
75
|
+
.replace(/"/g, """)
|
|
76
|
+
.replace(/'/g, "'");
|
|
77
|
+
}
|
|
78
|
+
function isUiTool(name) {
|
|
79
|
+
return [
|
|
80
|
+
"ui_design_system",
|
|
81
|
+
"ui_search",
|
|
82
|
+
"sync_ui_data",
|
|
83
|
+
"start_ui",
|
|
84
|
+
"start_product",
|
|
85
|
+
].includes(name);
|
|
86
|
+
}
|
|
87
|
+
function buildUiResourceHtml(name, args, result) {
|
|
88
|
+
const structured = result.structuredContent
|
|
89
|
+
? JSON.stringify(result.structuredContent, null, 2)
|
|
90
|
+
: "{}";
|
|
91
|
+
const argJson = JSON.stringify(args ?? {}, null, 2);
|
|
92
|
+
const textBlocks = Array.isArray(result.content)
|
|
93
|
+
? result.content
|
|
94
|
+
.map((item) => {
|
|
95
|
+
if (!item || typeof item !== "object") {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
const text = item.text;
|
|
99
|
+
return typeof text === "string" ? text : "";
|
|
100
|
+
})
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.join("\n\n")
|
|
103
|
+
: "";
|
|
104
|
+
const now = new Date().toISOString();
|
|
105
|
+
return `<!doctype html>
|
|
106
|
+
<html lang="zh-CN">
|
|
107
|
+
<head>
|
|
108
|
+
<meta charset="utf-8">
|
|
109
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
110
|
+
<title>${escapeHtml(name)} · MCP Apps</title>
|
|
111
|
+
<style>
|
|
112
|
+
:root { color-scheme: light; }
|
|
113
|
+
body { font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; margin: 0; background: #f4f7fb; color: #1e2a35; }
|
|
114
|
+
.wrap { max-width: 960px; margin: 0 auto; padding: 24px; }
|
|
115
|
+
.card { background: #fff; border-radius: 14px; padding: 18px; box-shadow: 0 4px 18px rgba(30,42,53,.08); margin-bottom: 16px; }
|
|
116
|
+
h1 { margin: 0 0 8px; font-size: 24px; }
|
|
117
|
+
h2 { margin: 0 0 10px; font-size: 16px; color: #2f4a65; }
|
|
118
|
+
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; }
|
|
119
|
+
.meta { color: #4f6880; font-size: 12px; }
|
|
120
|
+
</style>
|
|
121
|
+
</head>
|
|
122
|
+
<body>
|
|
123
|
+
<div class="wrap">
|
|
124
|
+
<div class="card">
|
|
125
|
+
<h1>${escapeHtml(name)}</h1>
|
|
126
|
+
<div class="meta">Generated at ${escapeHtml(now)} · MCP Apps preview</div>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="card">
|
|
129
|
+
<h2>Text Output</h2>
|
|
130
|
+
<pre>${escapeHtml(textBlocks || "(no text output)")}</pre>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="card">
|
|
133
|
+
<h2>Structured Content</h2>
|
|
134
|
+
<pre>${escapeHtml(structured)}</pre>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="card">
|
|
137
|
+
<h2>Arguments</h2>
|
|
138
|
+
<pre>${escapeHtml(argJson)}</pre>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</body>
|
|
142
|
+
</html>`;
|
|
143
|
+
}
|
|
144
|
+
function putUiAppResource(toolName, args, result) {
|
|
145
|
+
const uid = `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
146
|
+
const uri = `ui://mcp-probe-kit/${toolName}/${uid}`;
|
|
147
|
+
const entry = {
|
|
148
|
+
uri,
|
|
149
|
+
name: `UI Preview · ${toolName}`,
|
|
150
|
+
description: `MCP Apps preview generated by ${toolName}`,
|
|
151
|
+
mimeType: "text/html",
|
|
152
|
+
text: buildUiResourceHtml(toolName, args, result),
|
|
153
|
+
createdAt: new Date().toISOString(),
|
|
154
|
+
};
|
|
155
|
+
uiAppResources.set(uri, entry);
|
|
156
|
+
uiAppResourceOrder.push(uri);
|
|
157
|
+
while (uiAppResourceOrder.length > MAX_UI_APP_RESOURCES) {
|
|
158
|
+
const oldest = uiAppResourceOrder.shift();
|
|
159
|
+
if (oldest) {
|
|
160
|
+
uiAppResources.delete(oldest);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return uri;
|
|
164
|
+
}
|
|
165
|
+
function withUiResourceMeta(result, resourceUri) {
|
|
166
|
+
const currentUi = result._meta?.ui;
|
|
167
|
+
const currentUiRecord = currentUi && typeof currentUi === "object"
|
|
168
|
+
? currentUi
|
|
169
|
+
: {};
|
|
170
|
+
return {
|
|
171
|
+
...result,
|
|
172
|
+
_meta: {
|
|
173
|
+
...(result._meta ?? {}),
|
|
174
|
+
ui: {
|
|
175
|
+
...currentUiRecord,
|
|
176
|
+
resourceUri,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function decorateResult(toolName, args, raw, traceMeta) {
|
|
182
|
+
let result = withTraceMeta(raw, traceMeta);
|
|
183
|
+
if (uiAppsEnabled && isUiTool(toolName) && !result.isError) {
|
|
184
|
+
const resourceUri = putUiAppResource(toolName, args, result);
|
|
185
|
+
result = withUiResourceMeta(result, resourceUri);
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
9
189
|
// 创建MCP服务器实例
|
|
10
190
|
const server = new Server({
|
|
11
191
|
name: NAME,
|
|
12
192
|
version: VERSION,
|
|
13
193
|
}, {
|
|
14
|
-
capabilities:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
},
|
|
194
|
+
capabilities: serverCapabilities,
|
|
195
|
+
taskStore: new InMemoryTaskStore(),
|
|
196
|
+
taskMessageQueue: new InMemoryTaskMessageQueue(),
|
|
18
197
|
});
|
|
19
198
|
// 定义工具列表 - 从 schemas 导入,并根据工具集过滤
|
|
20
199
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -25,86 +204,243 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
25
204
|
tools: filteredTools,
|
|
26
205
|
};
|
|
27
206
|
});
|
|
207
|
+
async function executeTool(name, args, context) {
|
|
208
|
+
switch (name) {
|
|
209
|
+
case "init_project":
|
|
210
|
+
return await initProject(args);
|
|
211
|
+
case "gencommit":
|
|
212
|
+
return await gencommit(args);
|
|
213
|
+
case "code_review":
|
|
214
|
+
return await codeReview(args);
|
|
215
|
+
case "gentest":
|
|
216
|
+
return await gentest(args);
|
|
217
|
+
case "refactor":
|
|
218
|
+
return await refactor(args);
|
|
219
|
+
case "init_project_context":
|
|
220
|
+
return await initProjectContext(args);
|
|
221
|
+
case "add_feature":
|
|
222
|
+
return await addFeature(args);
|
|
223
|
+
case "fix_bug":
|
|
224
|
+
return await fixBug(args);
|
|
225
|
+
case "estimate":
|
|
226
|
+
return await estimate(args);
|
|
227
|
+
case "start_feature":
|
|
228
|
+
return await startFeature(args, context);
|
|
229
|
+
case "start_bugfix":
|
|
230
|
+
return await startBugfix(args, context);
|
|
231
|
+
case "start_onboard":
|
|
232
|
+
return await startOnboard(args, context);
|
|
233
|
+
case "start_ralph":
|
|
234
|
+
return await startRalph(args, context);
|
|
235
|
+
case "interview":
|
|
236
|
+
return await interview(args);
|
|
237
|
+
case "ask_user":
|
|
238
|
+
return await askUser(args);
|
|
239
|
+
case "ui_design_system":
|
|
240
|
+
return await uiDesignSystem(args);
|
|
241
|
+
case "ui_search":
|
|
242
|
+
return await uiSearch(args);
|
|
243
|
+
case "sync_ui_data":
|
|
244
|
+
return await syncUiData(args, context);
|
|
245
|
+
case "start_ui":
|
|
246
|
+
return await startUi(args, context);
|
|
247
|
+
case "start_product":
|
|
248
|
+
return await startProduct((args ?? {}), context);
|
|
249
|
+
case "git_work_report":
|
|
250
|
+
return await gitWorkReport(args);
|
|
251
|
+
default:
|
|
252
|
+
throw new Error(`未知工具: ${name}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function makeToolError(errorMessage) {
|
|
256
|
+
return {
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: "text",
|
|
260
|
+
text: `错误: ${errorMessage}`,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
isError: true,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function isTerminalTaskStatus(status) {
|
|
267
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
268
|
+
}
|
|
28
269
|
// 处理工具调用
|
|
29
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
270
|
+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
30
271
|
const { name, arguments: args } = request.params;
|
|
272
|
+
const taskRequest = request.params.task;
|
|
273
|
+
const traceMeta = getTraceMeta(extra._meta);
|
|
274
|
+
const emitProgress = async (progress, message) => {
|
|
275
|
+
const progressToken = extra._meta?.progressToken;
|
|
276
|
+
if (progressToken === undefined) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
await extra.sendNotification(ProgressNotificationSchema.parse({
|
|
281
|
+
method: "notifications/progress",
|
|
282
|
+
params: {
|
|
283
|
+
progressToken,
|
|
284
|
+
progress,
|
|
285
|
+
total: 100,
|
|
286
|
+
message,
|
|
287
|
+
...(traceMeta === undefined
|
|
288
|
+
? {}
|
|
289
|
+
: {
|
|
290
|
+
_meta: {
|
|
291
|
+
[traceMetaKey]: traceMeta,
|
|
292
|
+
},
|
|
293
|
+
}),
|
|
294
|
+
},
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
const err = error instanceof Error ? error.message : String(error);
|
|
299
|
+
console.error(`[MCP Probe Kit] progress notification failed: ${err}`);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
if (taskRequest) {
|
|
303
|
+
if (!extra.taskStore) {
|
|
304
|
+
return withTraceMeta(makeToolError("服务器未启用任务存储,无法创建任务"), traceMeta);
|
|
305
|
+
}
|
|
306
|
+
const task = await extra.taskStore.createTask({
|
|
307
|
+
ttl: extra.taskRequestedTtl ?? taskRequest.ttl,
|
|
308
|
+
});
|
|
309
|
+
const taskAbortController = new AbortController();
|
|
310
|
+
const cancelWatcher = setInterval(() => {
|
|
311
|
+
void (async () => {
|
|
312
|
+
try {
|
|
313
|
+
const latestTask = await extra.taskStore?.getTask(task.taskId);
|
|
314
|
+
if (latestTask?.status === "cancelled" && !taskAbortController.signal.aborted) {
|
|
315
|
+
taskAbortController.abort();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// ignore watcher errors
|
|
320
|
+
}
|
|
321
|
+
})();
|
|
322
|
+
}, 400);
|
|
323
|
+
const onRequestAbort = () => taskAbortController.abort();
|
|
324
|
+
extra.signal.addEventListener("abort", onRequestAbort, { once: true });
|
|
325
|
+
const taskContext = {
|
|
326
|
+
signal: taskAbortController.signal,
|
|
327
|
+
traceMeta,
|
|
328
|
+
reportProgress: async (progress, message) => {
|
|
329
|
+
const normalized = Math.max(0, Math.min(100, Math.round(progress)));
|
|
330
|
+
await emitProgress(normalized, message);
|
|
331
|
+
try {
|
|
332
|
+
await extra.taskStore?.updateTaskStatus(task.taskId, "working", `[${normalized}%] ${message}`);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// task may have already reached terminal status
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
// 后台执行任务,不阻塞当前请求,立即返回 taskId 给客户端轮询。
|
|
340
|
+
void (async () => {
|
|
341
|
+
try {
|
|
342
|
+
await taskContext.reportProgress?.(5, `开始执行工具: ${name}`);
|
|
343
|
+
const rawResult = await executeTool(name, args, taskContext);
|
|
344
|
+
if (!rawResult || typeof rawResult !== "object") {
|
|
345
|
+
throw new Error(`工具 ${name} 返回了无效响应`);
|
|
346
|
+
}
|
|
347
|
+
const result = decorateResult(name, args, rawResult, traceMeta);
|
|
348
|
+
const latestTask = await extra.taskStore?.getTask(task.taskId);
|
|
349
|
+
if (!latestTask || isTerminalTaskStatus(latestTask.status)) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const status = result && typeof result === "object" && "isError" in result && result.isError
|
|
353
|
+
? "failed"
|
|
354
|
+
: "completed";
|
|
355
|
+
await extra.taskStore?.storeTaskResult(task.taskId, status, result);
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
if (isAbortError(error)) {
|
|
359
|
+
const latestTask = await extra.taskStore?.getTask(task.taskId);
|
|
360
|
+
if (!latestTask || isTerminalTaskStatus(latestTask.status)) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
await extra.taskStore?.storeTaskResult(task.taskId, "failed", withTraceMeta(makeToolError(`工具执行已取消: ${name}`), traceMeta));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
367
|
+
const latestTask = await extra.taskStore?.getTask(task.taskId);
|
|
368
|
+
if (!latestTask || isTerminalTaskStatus(latestTask.status)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
await extra.taskStore?.storeTaskResult(task.taskId, "failed", withTraceMeta(makeToolError(errorMessage), traceMeta));
|
|
372
|
+
}
|
|
373
|
+
})().catch((error) => {
|
|
374
|
+
const err = error instanceof Error ? error.message : String(error);
|
|
375
|
+
console.error(`[MCP Probe Kit] task execution failed: ${err}`);
|
|
376
|
+
}).finally(() => {
|
|
377
|
+
clearInterval(cancelWatcher);
|
|
378
|
+
extra.signal.removeEventListener("abort", onRequestAbort);
|
|
379
|
+
});
|
|
380
|
+
return withTraceMeta({ task }, traceMeta);
|
|
381
|
+
}
|
|
382
|
+
const ensureNotAborted = () => {
|
|
383
|
+
if (extra.signal.aborted) {
|
|
384
|
+
throw new Error(`工具执行已取消: ${name}`);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
const toolContext = {
|
|
388
|
+
signal: extra.signal,
|
|
389
|
+
traceMeta,
|
|
390
|
+
reportProgress: async (progress, message) => {
|
|
391
|
+
const normalized = Math.max(0, Math.min(100, Math.round(progress)));
|
|
392
|
+
await emitProgress(normalized, message);
|
|
393
|
+
},
|
|
394
|
+
};
|
|
31
395
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
case "code_review":
|
|
38
|
-
return await codeReview(args);
|
|
39
|
-
case "gentest":
|
|
40
|
-
return await gentest(args);
|
|
41
|
-
case "refactor":
|
|
42
|
-
return await refactor(args);
|
|
43
|
-
case "init_project_context":
|
|
44
|
-
return await initProjectContext(args);
|
|
45
|
-
case "add_feature":
|
|
46
|
-
return await addFeature(args);
|
|
47
|
-
case "fix_bug":
|
|
48
|
-
return await fixBug(args);
|
|
49
|
-
case "estimate":
|
|
50
|
-
return await estimate(args);
|
|
51
|
-
// 智能编排工具
|
|
52
|
-
case "start_feature":
|
|
53
|
-
return await startFeature(args);
|
|
54
|
-
case "start_bugfix":
|
|
55
|
-
return await startBugfix(args);
|
|
56
|
-
case "start_onboard":
|
|
57
|
-
return await startOnboard(args);
|
|
58
|
-
case "start_ralph":
|
|
59
|
-
return await startRalph(args);
|
|
60
|
-
// 访谈工具
|
|
61
|
-
case "interview":
|
|
62
|
-
return await interview(args);
|
|
63
|
-
case "ask_user":
|
|
64
|
-
return await askUser(args);
|
|
65
|
-
// UI/UX Pro Max 工具
|
|
66
|
-
case "ui_design_system":
|
|
67
|
-
return await uiDesignSystem(args);
|
|
68
|
-
case "ui_search":
|
|
69
|
-
return await uiSearch(args);
|
|
70
|
-
case "sync_ui_data":
|
|
71
|
-
return await syncUiData(args);
|
|
72
|
-
case "start_ui":
|
|
73
|
-
return await startUi(args);
|
|
74
|
-
// 产品设计工作流
|
|
75
|
-
case "start_product":
|
|
76
|
-
return await startProduct(args || {});
|
|
77
|
-
// Git 工具
|
|
78
|
-
case "git_work_report":
|
|
79
|
-
return await gitWorkReport(args);
|
|
80
|
-
default:
|
|
81
|
-
throw new Error(`未知工具: ${name}`);
|
|
396
|
+
ensureNotAborted();
|
|
397
|
+
await emitProgress(5, `开始执行工具: ${name}`);
|
|
398
|
+
const rawResult = await executeTool(name, args, toolContext);
|
|
399
|
+
if (!rawResult || typeof rawResult !== "object") {
|
|
400
|
+
throw new Error(`工具 ${name} 返回了无效响应`);
|
|
82
401
|
}
|
|
402
|
+
ensureNotAborted();
|
|
403
|
+
const result = decorateResult(name, args, rawResult, traceMeta);
|
|
404
|
+
await emitProgress(100, `工具执行完成: ${name}`);
|
|
405
|
+
return result;
|
|
83
406
|
}
|
|
84
407
|
catch (error) {
|
|
408
|
+
if (extra.signal.aborted) {
|
|
409
|
+
await emitProgress(100, `工具执行已取消: ${name}`);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
await emitProgress(100, `工具执行失败: ${name}`);
|
|
413
|
+
}
|
|
85
414
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
86
|
-
return
|
|
87
|
-
content: [
|
|
88
|
-
{
|
|
89
|
-
type: "text",
|
|
90
|
-
text: `错误: ${errorMessage}`,
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
isError: true,
|
|
94
|
-
};
|
|
415
|
+
return withTraceMeta(makeToolError(errorMessage), traceMeta);
|
|
95
416
|
}
|
|
96
417
|
});
|
|
97
418
|
// 定义资源列表
|
|
98
419
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
420
|
+
const resources = [
|
|
421
|
+
{
|
|
422
|
+
uri: "probe://status",
|
|
423
|
+
name: "服务器状态",
|
|
424
|
+
description: "MCP Probe Kit 服务器当前状态",
|
|
425
|
+
mimeType: "application/json",
|
|
426
|
+
},
|
|
427
|
+
];
|
|
428
|
+
if (uiAppsEnabled) {
|
|
429
|
+
for (const uri of uiAppResourceOrder.slice().reverse()) {
|
|
430
|
+
const entry = uiAppResources.get(uri);
|
|
431
|
+
if (!entry) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
resources.push({
|
|
435
|
+
uri: entry.uri,
|
|
436
|
+
name: entry.name,
|
|
437
|
+
description: `${entry.description} (${entry.createdAt})`,
|
|
438
|
+
mimeType: entry.mimeType,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
99
442
|
return {
|
|
100
|
-
resources
|
|
101
|
-
{
|
|
102
|
-
uri: "probe://status",
|
|
103
|
-
name: "服务器状态",
|
|
104
|
-
description: "MCP Probe Kit 服务器当前状态",
|
|
105
|
-
mimeType: "application/json",
|
|
106
|
-
},
|
|
107
|
-
],
|
|
443
|
+
resources,
|
|
108
444
|
};
|
|
109
445
|
});
|
|
110
446
|
// 读取资源
|
|
@@ -124,81 +460,39 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
124
460
|
version: VERSION,
|
|
125
461
|
description: "AI 驱动的完整研发工具集",
|
|
126
462
|
},
|
|
463
|
+
extensions: {
|
|
464
|
+
enabled: extensionsCapabilityEnabled,
|
|
465
|
+
traceMetaKey,
|
|
466
|
+
uiAppsEnabled,
|
|
467
|
+
},
|
|
468
|
+
experimentalTasksStreaming: {
|
|
469
|
+
requestStream: typeof server.experimental.tasks.requestStream === "function",
|
|
470
|
+
createMessageStream: typeof server.experimental.tasks.createMessageStream === "function",
|
|
471
|
+
elicitInputStream: typeof server.experimental.tasks.elicitInputStream === "function",
|
|
472
|
+
},
|
|
127
473
|
toolCount: allToolSchemas.length,
|
|
128
474
|
}, null, 2),
|
|
129
475
|
},
|
|
130
476
|
],
|
|
131
477
|
};
|
|
132
478
|
}
|
|
479
|
+
if (uri.startsWith("ui://")) {
|
|
480
|
+
const entry = uiAppResources.get(uri);
|
|
481
|
+
if (!entry) {
|
|
482
|
+
throw new Error(`未知 UI 资源: ${uri}`);
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
contents: [
|
|
486
|
+
{
|
|
487
|
+
uri: entry.uri,
|
|
488
|
+
mimeType: entry.mimeType,
|
|
489
|
+
text: entry.text,
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
};
|
|
493
|
+
}
|
|
133
494
|
throw new Error(`未知资源: ${uri}`);
|
|
134
495
|
});
|
|
135
|
-
// ============================================
|
|
136
|
-
// Tasks API 端点 - 暂时禁用,等待 MCP SDK 正式支持
|
|
137
|
-
// ============================================
|
|
138
|
-
// 注意:当前 MCP SDK 版本不支持自定义 method,Tasks API 功能暂时禁用
|
|
139
|
-
// 相关 issue: https://github.com/modelcontextprotocol/sdk/issues/xxx
|
|
140
|
-
/*
|
|
141
|
-
// 获取任务状态
|
|
142
|
-
server.setRequestHandler({ method: "tasks/get" } as any, async (request: any) => {
|
|
143
|
-
try {
|
|
144
|
-
const { taskId } = request.params;
|
|
145
|
-
const tasksManager = getTasksManager();
|
|
146
|
-
const task = tasksManager.getTask(taskId);
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
task,
|
|
150
|
-
};
|
|
151
|
-
} catch (error) {
|
|
152
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
153
|
-
throw new Error(`Failed to get task: ${errorMessage}`);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// 获取任务结果
|
|
158
|
-
server.setRequestHandler({ method: "tasks/result" } as any, async (request: any) => {
|
|
159
|
-
try {
|
|
160
|
-
const { taskId } = request.params;
|
|
161
|
-
const tasksManager = getTasksManager();
|
|
162
|
-
const result = tasksManager.getTaskResult(taskId);
|
|
163
|
-
|
|
164
|
-
return result;
|
|
165
|
-
} catch (error) {
|
|
166
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
167
|
-
throw new Error(`Failed to get task result: ${errorMessage}`);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// 取消任务
|
|
172
|
-
server.setRequestHandler({ method: "tasks/cancel" } as any, async (request: any) => {
|
|
173
|
-
try {
|
|
174
|
-
const { taskId } = request.params;
|
|
175
|
-
const tasksManager = getTasksManager();
|
|
176
|
-
tasksManager.cancelTask(taskId);
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
_meta: {},
|
|
180
|
-
};
|
|
181
|
-
} catch (error) {
|
|
182
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
183
|
-
throw new Error(`Failed to cancel task: ${errorMessage}`);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// 列出所有任务
|
|
188
|
-
server.setRequestHandler({ method: "tasks/list" } as any, async () => {
|
|
189
|
-
try {
|
|
190
|
-
const tasksManager = getTasksManager();
|
|
191
|
-
const tasks = tasksManager.listTasks();
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
tasks,
|
|
195
|
-
};
|
|
196
|
-
} catch (error) {
|
|
197
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
198
|
-
throw new Error(`Failed to list tasks: ${errorMessage}`);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
*/
|
|
202
496
|
// 启动服务器
|
|
203
497
|
async function main() {
|
|
204
498
|
const transport = new StdioServerTransport();
|