mcp-probe-kit 3.0.6 → 3.0.7
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 +19 -10
- package/build/index.js +441 -1
- package/build/lib/gitnexus-bridge.d.ts +58 -0
- package/build/lib/gitnexus-bridge.js +379 -0
- package/build/lib/toolset-manager.d.ts +1 -1
- package/build/lib/toolset-manager.js +7 -5
- package/build/schemas/code-analysis-tools.d.ts +46 -0
- package/build/schemas/code-analysis-tools.js +47 -0
- package/build/schemas/index.d.ts +46 -0
- package/build/tools/__tests__/code_insight.unit.test.d.ts +1 -0
- package/build/tools/__tests__/code_insight.unit.test.js +35 -0
- package/build/tools/code_insight.d.ts +8 -0
- package/build/tools/code_insight.js +129 -0
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/start_bugfix.js +39 -4
- package/build/tools/start_feature.js +43 -8
- package/docs/data/tools.js +34 -14
- package/docs/specs/user-auth/design.md +82 -0
- package/docs/specs/user-auth/requirements.md +52 -0
- package/docs/specs/user-auth/tasks.md +55 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
> **Talk is cheap, show me the Context.**
|
|
15
15
|
>
|
|
16
|
-
> mcp-probe-kit is a protocol-level toolkit designed for developers who want AI to truly understand their project's intent. It's not just a collection of
|
|
16
|
+
> mcp-probe-kit is a protocol-level toolkit designed for developers who want AI to truly understand their project's intent. It's not just a collection of 22 tools—it's a context-aware system that helps AI agents grasp what you're building.
|
|
17
17
|
|
|
18
18
|
**Languages**: [English](README.md) | [简体中文](i18n/README.zh-CN.md) | [日本語](i18n/README.ja-JP.md) | [한국어](i18n/README.ko-KR.md) | [Español](i18n/README.es-ES.md) | [Français](i18n/README.fr-FR.md) | [Deutsch](i18n/README.de-DE.md) | [Português (BR)](i18n/README.pt-BR.md)
|
|
19
19
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
> 🚀 AI-Powered Complete Development Toolkit - Covering the Entire Development Lifecycle
|
|
26
26
|
|
|
27
|
-
A powerful MCP (Model Context Protocol) server providing **
|
|
27
|
+
A powerful MCP (Model Context Protocol) server providing **22 tools** covering the complete workflow from product analysis to final release (Requirements → Design → Development → Quality → Release), all tools support **structured output**.
|
|
28
28
|
|
|
29
29
|
**🎉 v3.0 Major Update**: Streamlined tool count, focus on core competencies, eliminate choice paralysis, let AI do more native work
|
|
30
30
|
|
|
@@ -39,7 +39,7 @@ A powerful MCP (Model Context Protocol) server providing **21 tools** covering t
|
|
|
39
39
|
**👉 [https://mcp-probe-kit.bytezonex.com](https://mcp-probe-kit.bytezonex.com/)**
|
|
40
40
|
|
|
41
41
|
- [Quick Start](https://mcp-probe-kit.bytezonex.com/pages/getting-started.html) - Setup in 5 minutes
|
|
42
|
-
- [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of
|
|
42
|
+
- [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of 22 tools
|
|
43
43
|
- [Best Practices](https://mcp-probe-kit.bytezonex.com/pages/examples.html) - Full development workflow guide
|
|
44
44
|
- [v3.0 Migration Guide](https://mcp-probe-kit.bytezonex.com/pages/migration.html) - Upgrade from v2.x to v3.0
|
|
45
45
|
|
|
@@ -47,20 +47,29 @@ A powerful MCP (Model Context Protocol) server providing **21 tools** covering t
|
|
|
47
47
|
|
|
48
48
|
## ✨ Core Features
|
|
49
49
|
|
|
50
|
-
### 📦
|
|
50
|
+
### 📦 22 Tools
|
|
51
51
|
|
|
52
52
|
- **🔄 Workflow Orchestration** (6 tools) - One-click complex development workflows
|
|
53
53
|
- `start_feature`, `start_bugfix`, `start_onboard`, `start_ui`, `start_product`, `start_ralph`
|
|
54
|
-
- **🔍 Code Analysis** (
|
|
55
|
-
- `code_review`, `fix_bug`, `refactor`
|
|
54
|
+
- **🔍 Code Analysis** (4 tools) - Code quality, refactoring, and graph insight
|
|
55
|
+
- `code_review`, `code_insight`, `fix_bug`, `refactor`
|
|
56
56
|
- **📝 Git Tools** (2 tools) - Git commits and work reports
|
|
57
57
|
- `gencommit`, `git_work_report`
|
|
58
58
|
- **⚡ Code Generation** (1 tool) - Test generation
|
|
59
59
|
- `gentest`
|
|
60
|
-
- **📦 Project Management** (
|
|
61
|
-
- `init_project`, `init_project_context`, `add_feature`, `estimate`, `interview`, `ask_user`
|
|
62
|
-
- **🎨 UI/UX Tools** (3 tools) - Design systems and data synchronization
|
|
63
|
-
- `ui_design_system`, `ui_search`, `sync_ui_data`
|
|
60
|
+
- **📦 Project Management** (6 tools) - Project initialization and requirements management
|
|
61
|
+
- `init_project`, `init_project_context`, `add_feature`, `estimate`, `interview`, `ask_user`
|
|
62
|
+
- **🎨 UI/UX Tools** (3 tools) - Design systems and data synchronization
|
|
63
|
+
- `ui_design_system`, `ui_search`, `sync_ui_data`
|
|
64
|
+
|
|
65
|
+
### 🧠 Code Graph Bridge (GitNexus)
|
|
66
|
+
|
|
67
|
+
- `code_insight` bridges GitNexus by default for query/context/impact analysis
|
|
68
|
+
- `start_feature` and `start_bugfix` automatically enrich plans with graph context when available
|
|
69
|
+
- If GitNexus is unavailable, the server falls back automatically without breaking orchestration
|
|
70
|
+
- Graph snapshots are exposed as resources (`probe://graph/latest`, `probe://graph/history`, `probe://graph/latest.md`)
|
|
71
|
+
- Graph snapshots are also persisted to readable files in `.mcp-probe-kit/graph-snapshots` (customizable via `MCP_GRAPH_SNAPSHOT_DIR`)
|
|
72
|
+
- Tool responses include `_meta.graph` with snapshot URI and local JSON/Markdown file paths
|
|
64
73
|
|
|
65
74
|
### 🎯 Structured Output
|
|
66
75
|
|
package/build/index.js
CHANGED
|
@@ -3,15 +3,21 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { InMemoryTaskMessageQueue, InMemoryTaskStore, } from "@modelcontextprotocol/sdk/experimental/index.js";
|
|
5
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ProgressNotificationSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
-
import
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport } from "./tools/index.js";
|
|
7
9
|
import { VERSION, NAME } from "./version.js";
|
|
8
10
|
import { allToolSchemas } from "./schemas/index.js";
|
|
9
11
|
import { filterTools, getToolsetFromEnv } from "./lib/toolset-manager.js";
|
|
10
12
|
import { isAbortError, } from "./lib/tool-execution-context.js";
|
|
11
13
|
const EXTENSIONS_CAPABILITY_KEY = "io.github.mybolide/extensions";
|
|
12
14
|
const MAX_UI_APP_RESOURCES = 30;
|
|
15
|
+
const MAX_GRAPH_SNAPSHOTS = 20;
|
|
16
|
+
const DEFAULT_GRAPH_SNAPSHOT_DIR = path.resolve(process.cwd(), ".mcp-probe-kit", "graph-snapshots");
|
|
13
17
|
const uiAppResources = new Map();
|
|
14
18
|
const uiAppResourceOrder = [];
|
|
19
|
+
const graphSnapshots = new Map();
|
|
20
|
+
const graphSnapshotOrder = [];
|
|
15
21
|
function isEnvEnabled(name, fallback = false) {
|
|
16
22
|
const raw = process.env[name];
|
|
17
23
|
if (raw === undefined) {
|
|
@@ -19,9 +25,17 @@ function isEnvEnabled(name, fallback = false) {
|
|
|
19
25
|
}
|
|
20
26
|
return /^(1|true|yes|on)$/i.test(raw.trim());
|
|
21
27
|
}
|
|
28
|
+
function resolveGraphSnapshotDir() {
|
|
29
|
+
const raw = process.env.MCP_GRAPH_SNAPSHOT_DIR?.trim();
|
|
30
|
+
if (!raw) {
|
|
31
|
+
return DEFAULT_GRAPH_SNAPSHOT_DIR;
|
|
32
|
+
}
|
|
33
|
+
return path.isAbsolute(raw) ? raw : path.resolve(process.cwd(), raw);
|
|
34
|
+
}
|
|
22
35
|
const extensionsCapabilityEnabled = isEnvEnabled("MCP_ENABLE_EXTENSIONS_CAPABILITY", false);
|
|
23
36
|
const uiAppsEnabled = isEnvEnabled("MCP_ENABLE_UI_APPS", false);
|
|
24
37
|
const traceMetaKey = process.env.MCP_TRACE_META_KEY || "trace";
|
|
38
|
+
const graphSnapshotDir = resolveGraphSnapshotDir();
|
|
25
39
|
const serverCapabilities = {
|
|
26
40
|
tools: {},
|
|
27
41
|
resources: {},
|
|
@@ -178,8 +192,203 @@ function withUiResourceMeta(result, resourceUri) {
|
|
|
178
192
|
},
|
|
179
193
|
};
|
|
180
194
|
}
|
|
195
|
+
function withGraphSnapshotMeta(result, snapshot) {
|
|
196
|
+
const currentGraphMeta = result._meta?.graph;
|
|
197
|
+
const currentGraphMetaRecord = currentGraphMeta && typeof currentGraphMeta === "object"
|
|
198
|
+
? currentGraphMeta
|
|
199
|
+
: {};
|
|
200
|
+
return {
|
|
201
|
+
...result,
|
|
202
|
+
_meta: {
|
|
203
|
+
...(result._meta ?? {}),
|
|
204
|
+
graph: {
|
|
205
|
+
...currentGraphMetaRecord,
|
|
206
|
+
snapshotUri: snapshot.uri,
|
|
207
|
+
snapshotId: snapshot.id,
|
|
208
|
+
status: snapshot.status,
|
|
209
|
+
createdAt: snapshot.createdAt,
|
|
210
|
+
jsonFilePath: snapshot.jsonFilePath ?? null,
|
|
211
|
+
markdownFilePath: snapshot.markdownFilePath ?? null,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function trimText(value, maxLen) {
|
|
217
|
+
if (value.length <= maxLen) {
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
return `${value.slice(0, maxLen - 3)}...`;
|
|
221
|
+
}
|
|
222
|
+
function toPosixPath(value) {
|
|
223
|
+
return value.replace(/\\/g, "/");
|
|
224
|
+
}
|
|
225
|
+
function makeSafeFileSegment(value) {
|
|
226
|
+
return value
|
|
227
|
+
.toLowerCase()
|
|
228
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
229
|
+
.replace(/-+/g, "-")
|
|
230
|
+
.replace(/^-|-$/g, "")
|
|
231
|
+
.slice(0, 48) || "snapshot";
|
|
232
|
+
}
|
|
233
|
+
function ensureGraphSnapshotDir() {
|
|
234
|
+
if (!fs.existsSync(graphSnapshotDir)) {
|
|
235
|
+
fs.mkdirSync(graphSnapshotDir, { recursive: true });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function renderGraphSnapshotMarkdown(snapshot) {
|
|
239
|
+
return [
|
|
240
|
+
"# Graph Snapshot",
|
|
241
|
+
"",
|
|
242
|
+
`- id: ${snapshot.id}`,
|
|
243
|
+
`- tool: ${snapshot.toolName}`,
|
|
244
|
+
`- status: ${snapshot.status}`,
|
|
245
|
+
`- createdAt: ${snapshot.createdAt}`,
|
|
246
|
+
`- summary: ${snapshot.summary}`,
|
|
247
|
+
"",
|
|
248
|
+
"## Payload",
|
|
249
|
+
"```json",
|
|
250
|
+
JSON.stringify(snapshot.payload, null, 2),
|
|
251
|
+
"```",
|
|
252
|
+
"",
|
|
253
|
+
].join("\n");
|
|
254
|
+
}
|
|
255
|
+
function persistGraphSnapshot(snapshot) {
|
|
256
|
+
try {
|
|
257
|
+
ensureGraphSnapshotDir();
|
|
258
|
+
const safeTool = makeSafeFileSegment(snapshot.toolName);
|
|
259
|
+
const baseName = `${snapshot.id}-${safeTool}`;
|
|
260
|
+
const jsonPath = path.join(graphSnapshotDir, `${baseName}.json`);
|
|
261
|
+
const markdownPath = path.join(graphSnapshotDir, `${baseName}.md`);
|
|
262
|
+
const jsonText = JSON.stringify({
|
|
263
|
+
id: snapshot.id,
|
|
264
|
+
uri: snapshot.uri,
|
|
265
|
+
toolName: snapshot.toolName,
|
|
266
|
+
createdAt: snapshot.createdAt,
|
|
267
|
+
status: snapshot.status,
|
|
268
|
+
summary: snapshot.summary,
|
|
269
|
+
payload: snapshot.payload,
|
|
270
|
+
}, null, 2);
|
|
271
|
+
fs.writeFileSync(jsonPath, jsonText, "utf-8");
|
|
272
|
+
fs.writeFileSync(markdownPath, renderGraphSnapshotMarkdown(snapshot), "utf-8");
|
|
273
|
+
return {
|
|
274
|
+
...snapshot,
|
|
275
|
+
jsonFilePath: toPosixPath(jsonPath),
|
|
276
|
+
markdownFilePath: toPosixPath(markdownPath),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
281
|
+
console.error(`[MCP Probe Kit] graph snapshot persist failed: ${message}`);
|
|
282
|
+
return snapshot;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function sanitizeGraphPayload(payload) {
|
|
286
|
+
if (!payload || typeof payload !== "object") {
|
|
287
|
+
return payload;
|
|
288
|
+
}
|
|
289
|
+
if (Array.isArray(payload)) {
|
|
290
|
+
return payload.slice(0, 20).map((item) => sanitizeGraphPayload(item));
|
|
291
|
+
}
|
|
292
|
+
const record = payload;
|
|
293
|
+
const next = {};
|
|
294
|
+
for (const [key, value] of Object.entries(record)) {
|
|
295
|
+
if (typeof value === "string") {
|
|
296
|
+
next[key] = trimText(value, 6000);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (key === "executions" && Array.isArray(value)) {
|
|
300
|
+
next[key] = value.slice(0, 8).map((item) => {
|
|
301
|
+
if (!item || typeof item !== "object") {
|
|
302
|
+
return item;
|
|
303
|
+
}
|
|
304
|
+
const exec = item;
|
|
305
|
+
return {
|
|
306
|
+
...exec,
|
|
307
|
+
text: typeof exec.text === "string" ? trimText(exec.text, 6000) : exec.text,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
next[key] = sanitizeGraphPayload(value);
|
|
313
|
+
}
|
|
314
|
+
return next;
|
|
315
|
+
}
|
|
316
|
+
function readGraphPayload(toolName, result) {
|
|
317
|
+
if (result.isError) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
if (toolName === "code_insight" && result.structuredContent && typeof result.structuredContent === "object") {
|
|
321
|
+
const structured = result.structuredContent;
|
|
322
|
+
const status = typeof structured.status === "string" ? structured.status : "ok";
|
|
323
|
+
const summary = typeof structured.summary === "string"
|
|
324
|
+
? structured.summary
|
|
325
|
+
: "code_insight 图谱结果";
|
|
326
|
+
return {
|
|
327
|
+
status,
|
|
328
|
+
summary,
|
|
329
|
+
payload: sanitizeGraphPayload(structured),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
if ((toolName === "start_feature" || toolName === "start_bugfix")
|
|
333
|
+
&& result.structuredContent
|
|
334
|
+
&& typeof result.structuredContent === "object") {
|
|
335
|
+
const structured = result.structuredContent;
|
|
336
|
+
const metadata = structured.metadata;
|
|
337
|
+
if (!metadata || typeof metadata !== "object") {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
const graphContext = metadata.graphContext;
|
|
341
|
+
if (!graphContext || typeof graphContext !== "object") {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const graphRecord = graphContext;
|
|
345
|
+
const status = graphRecord.available === false ? "degraded" : "ok";
|
|
346
|
+
const summary = typeof graphRecord.summary === "string"
|
|
347
|
+
? graphRecord.summary
|
|
348
|
+
: `${toolName} 图谱上下文`;
|
|
349
|
+
return {
|
|
350
|
+
status,
|
|
351
|
+
summary,
|
|
352
|
+
payload: sanitizeGraphPayload({
|
|
353
|
+
graphContext,
|
|
354
|
+
plan: metadata.plan ?? null,
|
|
355
|
+
}),
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
function rememberGraphSnapshot(toolName, result) {
|
|
361
|
+
const graph = readGraphPayload(toolName, result);
|
|
362
|
+
if (!graph) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
const id = `${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
|
366
|
+
const uri = `probe://graph/${id}`;
|
|
367
|
+
const snapshot = persistGraphSnapshot({
|
|
368
|
+
id,
|
|
369
|
+
uri,
|
|
370
|
+
toolName,
|
|
371
|
+
createdAt: new Date().toISOString(),
|
|
372
|
+
status: graph.status,
|
|
373
|
+
summary: graph.summary,
|
|
374
|
+
payload: graph.payload,
|
|
375
|
+
});
|
|
376
|
+
graphSnapshots.set(id, snapshot);
|
|
377
|
+
graphSnapshotOrder.push(id);
|
|
378
|
+
while (graphSnapshotOrder.length > MAX_GRAPH_SNAPSHOTS) {
|
|
379
|
+
const oldest = graphSnapshotOrder.shift();
|
|
380
|
+
if (oldest) {
|
|
381
|
+
graphSnapshots.delete(oldest);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return snapshot;
|
|
385
|
+
}
|
|
181
386
|
function decorateResult(toolName, args, raw, traceMeta) {
|
|
182
387
|
let result = withTraceMeta(raw, traceMeta);
|
|
388
|
+
const snapshot = rememberGraphSnapshot(toolName, result);
|
|
389
|
+
if (snapshot) {
|
|
390
|
+
result = withGraphSnapshotMeta(result, snapshot);
|
|
391
|
+
}
|
|
183
392
|
if (uiAppsEnabled && isUiTool(toolName) && !result.isError) {
|
|
184
393
|
const resourceUri = putUiAppResource(toolName, args, result);
|
|
185
394
|
result = withUiResourceMeta(result, resourceUri);
|
|
@@ -212,6 +421,8 @@ async function executeTool(name, args, context) {
|
|
|
212
421
|
return await gencommit(args);
|
|
213
422
|
case "code_review":
|
|
214
423
|
return await codeReview(args);
|
|
424
|
+
case "code_insight":
|
|
425
|
+
return await codeInsight(args, context);
|
|
215
426
|
case "gentest":
|
|
216
427
|
return await gentest(args);
|
|
217
428
|
case "refactor":
|
|
@@ -424,7 +635,43 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
424
635
|
description: "MCP Probe Kit 服务器当前状态",
|
|
425
636
|
mimeType: "application/json",
|
|
426
637
|
},
|
|
638
|
+
{
|
|
639
|
+
uri: "probe://graph/latest",
|
|
640
|
+
name: "图谱快照(最新)",
|
|
641
|
+
description: "最近一次 code_insight 或 start_* 生成的图谱快照",
|
|
642
|
+
mimeType: "application/json",
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
uri: "probe://graph/history",
|
|
646
|
+
name: "图谱快照(历史)",
|
|
647
|
+
description: `最近 ${graphSnapshotOrder.length} 条图谱快照摘要`,
|
|
648
|
+
mimeType: "application/json",
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
uri: "probe://graph/latest.md",
|
|
652
|
+
name: "图谱快照(最新 Markdown)",
|
|
653
|
+
description: "最近一次图谱快照的 Markdown 视图",
|
|
654
|
+
mimeType: "text/markdown",
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
uri: "probe://graph/files",
|
|
658
|
+
name: "图谱快照(文件索引)",
|
|
659
|
+
description: `图谱快照落盘目录: ${toPosixPath(graphSnapshotDir)}`,
|
|
660
|
+
mimeType: "application/json",
|
|
661
|
+
},
|
|
427
662
|
];
|
|
663
|
+
for (const id of graphSnapshotOrder.slice().reverse().slice(0, 10)) {
|
|
664
|
+
const snapshot = graphSnapshots.get(id);
|
|
665
|
+
if (!snapshot) {
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
resources.push({
|
|
669
|
+
uri: snapshot.uri,
|
|
670
|
+
name: `图谱快照 · ${snapshot.toolName}`,
|
|
671
|
+
description: `${snapshot.status} · ${trimText(snapshot.summary, 120)} (${snapshot.createdAt})`,
|
|
672
|
+
mimeType: "application/json",
|
|
673
|
+
});
|
|
674
|
+
}
|
|
428
675
|
if (uiAppsEnabled) {
|
|
429
676
|
for (const uri of uiAppResourceOrder.slice().reverse()) {
|
|
430
677
|
const entry = uiAppResources.get(uri);
|
|
@@ -470,6 +717,30 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
470
717
|
createMessageStream: typeof server.experimental.tasks.createMessageStream === "function",
|
|
471
718
|
elicitInputStream: typeof server.experimental.tasks.elicitInputStream === "function",
|
|
472
719
|
},
|
|
720
|
+
graphSnapshots: {
|
|
721
|
+
count: graphSnapshotOrder.length,
|
|
722
|
+
snapshotDir: toPosixPath(graphSnapshotDir),
|
|
723
|
+
latest: (() => {
|
|
724
|
+
const latestId = graphSnapshotOrder[graphSnapshotOrder.length - 1];
|
|
725
|
+
if (!latestId) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const latest = graphSnapshots.get(latestId);
|
|
729
|
+
if (!latest) {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
id: latest.id,
|
|
734
|
+
uri: latest.uri,
|
|
735
|
+
toolName: latest.toolName,
|
|
736
|
+
status: latest.status,
|
|
737
|
+
summary: trimText(latest.summary, 140),
|
|
738
|
+
createdAt: latest.createdAt,
|
|
739
|
+
jsonFilePath: latest.jsonFilePath ?? null,
|
|
740
|
+
markdownFilePath: latest.markdownFilePath ?? null,
|
|
741
|
+
};
|
|
742
|
+
})(),
|
|
743
|
+
},
|
|
473
744
|
toolCount: allToolSchemas.length,
|
|
474
745
|
}, null, 2),
|
|
475
746
|
},
|
|
@@ -491,6 +762,175 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
491
762
|
],
|
|
492
763
|
};
|
|
493
764
|
}
|
|
765
|
+
if (uri === "probe://graph/latest") {
|
|
766
|
+
const latestId = graphSnapshotOrder[graphSnapshotOrder.length - 1];
|
|
767
|
+
if (!latestId) {
|
|
768
|
+
return {
|
|
769
|
+
contents: [
|
|
770
|
+
{
|
|
771
|
+
uri,
|
|
772
|
+
mimeType: "application/json",
|
|
773
|
+
text: JSON.stringify({
|
|
774
|
+
status: "empty",
|
|
775
|
+
message: "暂无图谱快照,请先调用 code_insight 或 start_feature/start_bugfix。",
|
|
776
|
+
}, null, 2),
|
|
777
|
+
},
|
|
778
|
+
],
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
const snapshot = graphSnapshots.get(latestId);
|
|
782
|
+
if (!snapshot) {
|
|
783
|
+
throw new Error(`图谱快照不存在: ${latestId}`);
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
contents: [
|
|
787
|
+
{
|
|
788
|
+
uri,
|
|
789
|
+
mimeType: "application/json",
|
|
790
|
+
text: JSON.stringify({
|
|
791
|
+
id: snapshot.id,
|
|
792
|
+
uri: snapshot.uri,
|
|
793
|
+
toolName: snapshot.toolName,
|
|
794
|
+
createdAt: snapshot.createdAt,
|
|
795
|
+
status: snapshot.status,
|
|
796
|
+
summary: snapshot.summary,
|
|
797
|
+
payload: snapshot.payload,
|
|
798
|
+
files: {
|
|
799
|
+
json: snapshot.jsonFilePath ?? null,
|
|
800
|
+
markdown: snapshot.markdownFilePath ?? null,
|
|
801
|
+
},
|
|
802
|
+
}, null, 2),
|
|
803
|
+
},
|
|
804
|
+
],
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (uri === "probe://graph/latest.md") {
|
|
808
|
+
const latestId = graphSnapshotOrder[graphSnapshotOrder.length - 1];
|
|
809
|
+
if (!latestId) {
|
|
810
|
+
return {
|
|
811
|
+
contents: [
|
|
812
|
+
{
|
|
813
|
+
uri,
|
|
814
|
+
mimeType: "text/markdown",
|
|
815
|
+
text: "# Graph Snapshot\n\n暂无图谱快照,请先调用 code_insight 或 start_feature/start_bugfix。",
|
|
816
|
+
},
|
|
817
|
+
],
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
const snapshot = graphSnapshots.get(latestId);
|
|
821
|
+
if (!snapshot) {
|
|
822
|
+
throw new Error(`图谱快照不存在: ${latestId}`);
|
|
823
|
+
}
|
|
824
|
+
const markdown = snapshot.markdownFilePath && fs.existsSync(snapshot.markdownFilePath)
|
|
825
|
+
? fs.readFileSync(snapshot.markdownFilePath, "utf-8")
|
|
826
|
+
: renderGraphSnapshotMarkdown(snapshot);
|
|
827
|
+
return {
|
|
828
|
+
contents: [
|
|
829
|
+
{
|
|
830
|
+
uri,
|
|
831
|
+
mimeType: "text/markdown",
|
|
832
|
+
text: markdown,
|
|
833
|
+
},
|
|
834
|
+
],
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
if (uri === "probe://graph/history") {
|
|
838
|
+
const history = graphSnapshotOrder
|
|
839
|
+
.slice()
|
|
840
|
+
.reverse()
|
|
841
|
+
.map((id) => graphSnapshots.get(id))
|
|
842
|
+
.filter((item) => Boolean(item))
|
|
843
|
+
.map((item) => ({
|
|
844
|
+
id: item.id,
|
|
845
|
+
uri: item.uri,
|
|
846
|
+
toolName: item.toolName,
|
|
847
|
+
createdAt: item.createdAt,
|
|
848
|
+
status: item.status,
|
|
849
|
+
summary: trimText(item.summary, 200),
|
|
850
|
+
files: {
|
|
851
|
+
json: item.jsonFilePath ?? null,
|
|
852
|
+
markdown: item.markdownFilePath ?? null,
|
|
853
|
+
},
|
|
854
|
+
}));
|
|
855
|
+
return {
|
|
856
|
+
contents: [
|
|
857
|
+
{
|
|
858
|
+
uri,
|
|
859
|
+
mimeType: "application/json",
|
|
860
|
+
text: JSON.stringify({
|
|
861
|
+
count: history.length,
|
|
862
|
+
items: history,
|
|
863
|
+
}, null, 2),
|
|
864
|
+
},
|
|
865
|
+
],
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
if (uri === "probe://graph/files") {
|
|
869
|
+
const latestId = graphSnapshotOrder[graphSnapshotOrder.length - 1];
|
|
870
|
+
const latest = latestId ? graphSnapshots.get(latestId) ?? null : null;
|
|
871
|
+
const hasDir = fs.existsSync(graphSnapshotDir);
|
|
872
|
+
const files = hasDir
|
|
873
|
+
? fs
|
|
874
|
+
.readdirSync(graphSnapshotDir, { withFileTypes: true })
|
|
875
|
+
.filter((entry) => entry.isFile() && /\.(json|md)$/i.test(entry.name))
|
|
876
|
+
.map((entry) => toPosixPath(path.join(graphSnapshotDir, entry.name)))
|
|
877
|
+
.sort((a, b) => b.localeCompare(a))
|
|
878
|
+
.slice(0, 40)
|
|
879
|
+
: [];
|
|
880
|
+
return {
|
|
881
|
+
contents: [
|
|
882
|
+
{
|
|
883
|
+
uri,
|
|
884
|
+
mimeType: "application/json",
|
|
885
|
+
text: JSON.stringify({
|
|
886
|
+
snapshotDir: toPosixPath(graphSnapshotDir),
|
|
887
|
+
exists: hasDir,
|
|
888
|
+
latest: latest
|
|
889
|
+
? {
|
|
890
|
+
id: latest.id,
|
|
891
|
+
uri: latest.uri,
|
|
892
|
+
toolName: latest.toolName,
|
|
893
|
+
jsonFilePath: latest.jsonFilePath ?? null,
|
|
894
|
+
markdownFilePath: latest.markdownFilePath ?? null,
|
|
895
|
+
}
|
|
896
|
+
: null,
|
|
897
|
+
files,
|
|
898
|
+
}, null, 2),
|
|
899
|
+
},
|
|
900
|
+
],
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
if (uri.startsWith("probe://graph/")) {
|
|
904
|
+
const id = uri.slice("probe://graph/".length);
|
|
905
|
+
if (!id || id === "latest" || id === "history" || id === "files" || id === "latest.md") {
|
|
906
|
+
throw new Error(`未知图谱资源: ${uri}`);
|
|
907
|
+
}
|
|
908
|
+
const snapshot = graphSnapshots.get(id);
|
|
909
|
+
if (!snapshot) {
|
|
910
|
+
throw new Error(`图谱快照不存在: ${id}`);
|
|
911
|
+
}
|
|
912
|
+
return {
|
|
913
|
+
contents: [
|
|
914
|
+
{
|
|
915
|
+
uri,
|
|
916
|
+
mimeType: "application/json",
|
|
917
|
+
text: JSON.stringify({
|
|
918
|
+
id: snapshot.id,
|
|
919
|
+
uri: snapshot.uri,
|
|
920
|
+
toolName: snapshot.toolName,
|
|
921
|
+
createdAt: snapshot.createdAt,
|
|
922
|
+
status: snapshot.status,
|
|
923
|
+
summary: snapshot.summary,
|
|
924
|
+
payload: snapshot.payload,
|
|
925
|
+
files: {
|
|
926
|
+
json: snapshot.jsonFilePath ?? null,
|
|
927
|
+
markdown: snapshot.markdownFilePath ?? null,
|
|
928
|
+
},
|
|
929
|
+
}, null, 2),
|
|
930
|
+
},
|
|
931
|
+
],
|
|
932
|
+
};
|
|
933
|
+
}
|
|
494
934
|
throw new Error(`未知资源: ${uri}`);
|
|
495
935
|
});
|
|
496
936
|
// 启动服务器
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export type CodeInsightMode = "auto" | "query" | "context" | "impact";
|
|
2
|
+
export type CodeInsightDirection = "upstream" | "downstream";
|
|
3
|
+
export interface CodeInsightRequest {
|
|
4
|
+
mode?: CodeInsightMode;
|
|
5
|
+
query?: string;
|
|
6
|
+
target?: string;
|
|
7
|
+
repo?: string;
|
|
8
|
+
goal?: string;
|
|
9
|
+
taskContext?: string;
|
|
10
|
+
direction?: CodeInsightDirection;
|
|
11
|
+
maxDepth?: number;
|
|
12
|
+
includeTests?: boolean;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export interface CodeInsightExecution {
|
|
16
|
+
tool: string;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
durationMs: number;
|
|
19
|
+
args: Record<string, unknown>;
|
|
20
|
+
text?: string;
|
|
21
|
+
structuredContent?: unknown;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CodeInsightBridgeResult {
|
|
25
|
+
provider: "gitnexus";
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
available: boolean;
|
|
28
|
+
degraded: boolean;
|
|
29
|
+
modeRequested: CodeInsightMode;
|
|
30
|
+
modeResolved: "query" | "context" | "impact";
|
|
31
|
+
summary: string;
|
|
32
|
+
executions: CodeInsightExecution[];
|
|
33
|
+
warnings: string[];
|
|
34
|
+
repo?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface EmbeddedGraphContext {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
available: boolean;
|
|
39
|
+
degraded: boolean;
|
|
40
|
+
summary: string;
|
|
41
|
+
warnings: string[];
|
|
42
|
+
provider: "gitnexus";
|
|
43
|
+
mode: "query" | "context" | "impact";
|
|
44
|
+
highlights: string[];
|
|
45
|
+
}
|
|
46
|
+
export declare function runCodeInsightBridge(request: CodeInsightRequest): Promise<CodeInsightBridgeResult>;
|
|
47
|
+
export declare function buildFeatureGraphContext(input: {
|
|
48
|
+
featureName: string;
|
|
49
|
+
description: string;
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
repo?: string;
|
|
52
|
+
}): Promise<EmbeddedGraphContext>;
|
|
53
|
+
export declare function buildBugfixGraphContext(input: {
|
|
54
|
+
errorMessage: string;
|
|
55
|
+
stackTrace?: string;
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
repo?: string;
|
|
58
|
+
}): Promise<EmbeddedGraphContext>;
|