@ppdocs/mcp 3.2.21 → 3.2.23

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.
@@ -0,0 +1,483 @@
1
+ /**
2
+ * 🔀 kg_flowchart — 逻辑流程图批量操作
3
+ * AI 一次提交所有节点+边, 后端原子处理, 返回孤立检测结果
4
+ */
5
+ import { z } from 'zod';
6
+ import { getClient } from '../storage/httpClient.js';
7
+ import { decodeObjectStrings } from '../utils.js';
8
+ import { wrap, safeTool } from './shared.js';
9
+ const NodeSchema = z.object({
10
+ id: z.string().describe('节点唯一ID'),
11
+ label: z.string().describe('节点标签'),
12
+ description: z.string().optional().describe('节点描述'),
13
+ nodeType: z.string().optional().describe('节点类型: super|process|data|entry'),
14
+ domain: z.string().optional().describe('领域: frontend|backend|mcp|system|infra'),
15
+ input: z.array(z.string()).optional(),
16
+ output: z.array(z.string()).optional(),
17
+ affiliation: z.string().optional().describe('父节点ID, 默认root'),
18
+ });
19
+ const EdgeSchema = z.object({
20
+ from: z.string().describe('起点节点ID'),
21
+ to: z.string().describe('终点节点ID'),
22
+ label: z.string().optional().describe('连线标签'),
23
+ edgeType: z.string().optional().describe('类型: call|event|data|reference'),
24
+ });
25
+ export function registerFlowchartTools(server, ctx) {
26
+ const client = () => getClient();
27
+ server.tool('kg_flowchart', '🔀 逻辑流程图(关系型知识锚点) — action: list|get|get_node(★爆炸式上下文)|update_node|delete_node|batch_add|bind|unbind|orphans|health', {
28
+ action: z.enum(['list', 'get', 'get_node', 'update_node', 'delete_node', 'batch_add', 'bind', 'unbind', 'orphans', 'health'])
29
+ .describe('操作类型'),
30
+ chartId: z.string().optional()
31
+ .describe('流程图ID (默认"main")'),
32
+ nodeId: z.string().optional()
33
+ .describe('节点ID (get_node/update_node/delete_node/bind/unbind)'),
34
+ // get_node 爆炸扩展控制 (全部默认开启, AI可选择性关闭)
35
+ expand: z.number().optional()
36
+ .describe('get_node: 连线扩展层数(默认3, 0=不扩展)'),
37
+ includeDocs: z.boolean().optional()
38
+ .describe('get_node: 显示绑定的参考文档摘要(默认true)'),
39
+ includeTasks: z.boolean().optional()
40
+ .describe('get_node: 显示绑定的任务摘要(默认true)'),
41
+ includeFiles: z.boolean().optional()
42
+ .describe('get_node: 显示绑定的代码文件(默认true)'),
43
+ includeDoc: z.boolean().optional()
44
+ .describe('get_node: 显示节点技术文档(默认true)'),
45
+ // update_node 更新字段
46
+ label: z.string().optional().describe('update_node: 新标签'),
47
+ description: z.string().optional().describe('update_node: 新描述'),
48
+ nodeType: z.string().optional().describe('update_node: 新类型'),
49
+ domain: z.string().optional().describe('update_node: 新领域'),
50
+ input: z.array(z.string()).optional().describe('update_node: 新输入 / bind的源代码文件'),
51
+ output: z.array(z.string()).optional().describe('update_node: 新输出'),
52
+ // bind/unbind
53
+ files: z.array(z.string()).optional()
54
+ .describe('源代码文件路径 (bind/unbind)'),
55
+ dirs: z.array(z.string()).optional()
56
+ .describe('源代码目录路径 (bind/unbind)'),
57
+ docs: z.array(z.string()).optional()
58
+ .describe('知识文档路径 (bind/unbind)'),
59
+ tasks: z.array(z.string()).optional()
60
+ .describe('任务ID (bind/unbind)'),
61
+ // batch_add
62
+ nodes: z.array(NodeSchema).optional()
63
+ .describe('批量添加的节点数组 (batch_add)'),
64
+ edges: z.array(EdgeSchema).optional()
65
+ .describe('批量添加的边数组 (batch_add)'),
66
+ }, async (args) => safeTool(async () => {
67
+ const decoded = decodeObjectStrings(args);
68
+ switch (decoded.action) {
69
+ case 'list': {
70
+ const charts = await client().listFlowcharts();
71
+ if (!charts || charts.length === 0)
72
+ return wrap('📋 暂无流程图');
73
+ const list = charts
74
+ .map(c => `• ${c.title} [${c.id}] — ${c.nodeCount}节点 ${c.edgeCount}连线`)
75
+ .join('\n');
76
+ return wrap(`📋 流程图列表:\n\n${list}`);
77
+ }
78
+ case 'get': {
79
+ const chartId = decoded.chartId || 'main';
80
+ const chart = await client().getFlowchart(chartId);
81
+ if (!chart)
82
+ return wrap(`❌ 流程图 "${chartId}" 不存在`);
83
+ const nodes = chart.nodes || [];
84
+ const edges = chart.edges || [];
85
+ const lines = [
86
+ `📊 ${chart.title || chartId} [${chartId}]`,
87
+ chart.parentChart ? `↩ 父图: ${chart.parentChart}` : '',
88
+ ``,
89
+ `### 节点 (${nodes.length})`,
90
+ ...nodes.map((n) => ` • ${n.label} [${n.id}] (${n.nodeType}/${n.domain}) ${n.boundDocs?.length ? `📄${n.boundDocs.length}` : ''} ${n.boundTasks?.length ? `📝${n.boundTasks.length}` : ''} ${n.boundFiles?.length ? `📂${n.boundFiles.length}` : ''}`),
91
+ ``,
92
+ `### 连线 (${edges.length})`,
93
+ ...edges.map((e) => ` ${e.from} →${e.label ? `[${e.label}]` : ''} ${e.to}`),
94
+ ].filter(Boolean);
95
+ return wrap(lines.join('\n'));
96
+ }
97
+ case 'get_node': {
98
+ const chartId = decoded.chartId || 'main';
99
+ if (!decoded.nodeId)
100
+ return wrap('❌ get_node 需要 nodeId');
101
+ const chart = await client().getFlowchart(chartId);
102
+ if (!chart)
103
+ return wrap(`❌ 流程图 "${chartId}" 不存在`);
104
+ const allNodes = chart.nodes || [];
105
+ const allEdges = chart.edges || [];
106
+ const target = allNodes.find((n) => n.id === decoded.nodeId);
107
+ if (!target)
108
+ return wrap(`❌ 节点 "${decoded.nodeId}" 不存在于 "${chartId}"`);
109
+ // 扩展参数 (全部默认开启)
110
+ const expandDepth = decoded.expand !== undefined ? Number(decoded.expand) : 3;
111
+ const showDocs = decoded.includeDocs !== false;
112
+ const showTasks = decoded.includeTasks !== false;
113
+ const showFiles = decoded.includeFiles !== false;
114
+ const showDoc = decoded.includeDoc !== false;
115
+ const nodeMap = new Map(allNodes.map((n) => [n.id, n]));
116
+ const lines = [];
117
+ // ======== 节点基础信息 ========
118
+ lines.push(`🔍 节点详情: ${target.label} [${target.id}]`);
119
+ lines.push(`类型: ${target.nodeType || 'process'} | 领域: ${target.domain || 'system'}`);
120
+ if (target.description)
121
+ lines.push(`📝 ${target.description}`);
122
+ if (target.input?.length)
123
+ lines.push(`📥 输入: ${target.input.join(', ')}`);
124
+ if (target.output?.length)
125
+ lines.push(`📤 输出: ${target.output.join(', ')}`);
126
+ if (target.subFlowchart)
127
+ lines.push(`📂 子流程图: ${target.subFlowchart}`);
128
+ if (target.lastUpdated)
129
+ lines.push(`🕐 最后更新: ${target.lastUpdated}`);
130
+ lines.push('');
131
+ // ======== 节点技术文档 ========
132
+ if (showDoc && target.doc) {
133
+ lines.push(`### 📖 技术文档`);
134
+ lines.push(`版本: ${target.doc.version} | ${target.doc.date}`);
135
+ if (target.doc.overview)
136
+ lines.push(target.doc.overview);
137
+ if (target.doc.details?.length) {
138
+ for (const d of target.doc.details) {
139
+ lines.push(` **${d.section}**: ${d.content?.substring(0, 200)}`);
140
+ }
141
+ }
142
+ lines.push('');
143
+ }
144
+ // ======== 爆炸扩展: N层关联节点 ========
145
+ if (expandDepth > 0) {
146
+ const visited = new Set([target.id]);
147
+ let frontier = [target.id];
148
+ const layerResults = [];
149
+ for (let depth = 1; depth <= expandDepth; depth++) {
150
+ const nextFrontier = [];
151
+ const layerLines = [];
152
+ for (const currentId of frontier) {
153
+ // 出边
154
+ for (const e of allEdges) {
155
+ if (e.from === currentId && !visited.has(e.to)) {
156
+ visited.add(e.to);
157
+ nextFrontier.push(e.to);
158
+ const neighbor = nodeMap.get(e.to);
159
+ const label = neighbor?.label || e.to;
160
+ layerLines.push(` → ${label} [${e.to}] ${e.label ? `(${e.label})` : ''} ${e.edgeType ? `[${e.edgeType}]` : ''}`);
161
+ }
162
+ // 入边
163
+ if (e.to === currentId && !visited.has(e.from)) {
164
+ visited.add(e.from);
165
+ nextFrontier.push(e.from);
166
+ const neighbor = nodeMap.get(e.from);
167
+ const label = neighbor?.label || e.from;
168
+ layerLines.push(` ← ${label} [${e.from}] ${e.label ? `(${e.label})` : ''} ${e.edgeType ? `[${e.edgeType}]` : ''}`);
169
+ }
170
+ }
171
+ }
172
+ if (layerLines.length > 0)
173
+ layerResults.push(layerLines);
174
+ frontier = nextFrontier;
175
+ if (frontier.length === 0)
176
+ break;
177
+ }
178
+ if (layerResults.length > 0) {
179
+ lines.push(`### 🔗 关联节点 (扩展${layerResults.length}层, 共${visited.size - 1}个)`);
180
+ layerResults.forEach((layer, i) => {
181
+ lines.push(`**L${i + 1}** (${layer.length}个):`);
182
+ lines.push(...layer);
183
+ });
184
+ lines.push('');
185
+ }
186
+ }
187
+ // ======== 绑定的参考文档 ========
188
+ if (showDocs && target.boundDocs?.length) {
189
+ lines.push(`### 📄 绑定参考文档 (${target.boundDocs.length})`);
190
+ for (const docPath of target.boundDocs) {
191
+ try {
192
+ const doc = await client().getDoc(docPath);
193
+ if (doc) {
194
+ lines.push(` 📄 ${docPath} — ${doc.summary || '无摘要'}`);
195
+ if (doc.status)
196
+ lines.push(` 状态: ${doc.status}`);
197
+ }
198
+ else {
199
+ lines.push(` 📄 ${docPath} (未找到)`);
200
+ }
201
+ }
202
+ catch {
203
+ lines.push(` 📄 ${docPath}`);
204
+ }
205
+ }
206
+ lines.push('');
207
+ }
208
+ // ======== 绑定的任务 ========
209
+ if (showTasks && target.boundTasks?.length) {
210
+ lines.push(`### 📝 绑定任务 (${target.boundTasks.length})`);
211
+ for (const taskId of target.boundTasks) {
212
+ try {
213
+ const task = await client().getTask(taskId);
214
+ if (task) {
215
+ lines.push(` 📝 [${taskId}] ${task.title} — ${task.status}`);
216
+ }
217
+ else {
218
+ lines.push(` 📝 [${taskId}] (未找到)`);
219
+ }
220
+ }
221
+ catch {
222
+ lines.push(` 📝 [${taskId}]`);
223
+ }
224
+ }
225
+ lines.push('');
226
+ }
227
+ // ======== 绑定的代码文件 ========
228
+ if (showFiles) {
229
+ const files = target.boundFiles || [];
230
+ const dirs = target.boundDirs || [];
231
+ if (files.length + dirs.length > 0) {
232
+ lines.push(`### 📂 绑定代码 (${files.length}文件 ${dirs.length}目录)`);
233
+ for (const f of files)
234
+ lines.push(` 📄 ${f}`);
235
+ for (const d of dirs)
236
+ lines.push(` 📁 ${d}`);
237
+ lines.push('');
238
+ }
239
+ }
240
+ // ======== 版本历史 ========
241
+ if (target.versions?.length) {
242
+ lines.push(`### 📋 版本历史 (${target.versions.length})`);
243
+ for (const v of target.versions.slice(-5)) {
244
+ lines.push(` v${v.version} (${v.date}): ${v.changes}`);
245
+ }
246
+ }
247
+ return wrap(lines.join('\n'));
248
+ }
249
+ case 'update_node': {
250
+ const chartId = decoded.chartId || 'main';
251
+ if (!decoded.nodeId)
252
+ return wrap('❌ update_node 需要 nodeId');
253
+ // 构建待更新的节点字段
254
+ const updates = { id: decoded.nodeId };
255
+ const changes = [];
256
+ if (decoded.label !== undefined) {
257
+ updates.label = decoded.label;
258
+ changes.push('label');
259
+ }
260
+ if (decoded.description !== undefined) {
261
+ updates.description = decoded.description;
262
+ changes.push('description');
263
+ }
264
+ if (decoded.nodeType !== undefined) {
265
+ updates.nodeType = decoded.nodeType;
266
+ changes.push('nodeType');
267
+ }
268
+ if (decoded.domain !== undefined) {
269
+ updates.domain = decoded.domain;
270
+ changes.push('domain');
271
+ }
272
+ if (decoded.input !== undefined) {
273
+ updates.input = decoded.input;
274
+ changes.push('input');
275
+ }
276
+ if (decoded.output !== undefined) {
277
+ updates.output = decoded.output;
278
+ changes.push('output');
279
+ }
280
+ if (changes.length === 0)
281
+ return wrap('ℹ️ 无变更 — 请提供要更新的字段');
282
+ updates.lastUpdated = new Date().toISOString();
283
+ // 原子调用: PUT /flowcharts/:chartId/nodes/:nodeId
284
+ await client().updateFlowchartNode(chartId, decoded.nodeId, updates, `MCP更新: ${changes.join(', ')}`);
285
+ return wrap(`✅ 节点已更新: ${decoded.label || decoded.nodeId} [${chartId}/${decoded.nodeId}]\n更新字段: ${changes.join(', ')}`);
286
+ }
287
+ case 'delete_node': {
288
+ const chartId = decoded.chartId || 'main';
289
+ if (!decoded.nodeId)
290
+ return wrap('❌ delete_node 需要 nodeId');
291
+ // 原子调用: DELETE /flowcharts/:chartId/nodes/:nodeId (后端自动清理关联边)
292
+ await client().deleteFlowchartNode(chartId, decoded.nodeId);
293
+ return wrap(`✅ 节点已删除 [${chartId}/${decoded.nodeId}]`);
294
+ }
295
+ case 'batch_add': {
296
+ const chartId = decoded.chartId || 'main';
297
+ if (!decoded.nodes || decoded.nodes.length === 0) {
298
+ return wrap('❌ batch_add 需要 nodes 数组');
299
+ }
300
+ // 自动填充默认值 — AI 只需填 id+label+description
301
+ const fullNodes = decoded.nodes.map((n) => ({
302
+ id: n.id,
303
+ label: n.label,
304
+ nodeType: n.nodeType || 'process',
305
+ domain: n.domain || 'system',
306
+ input: n.input || [],
307
+ output: n.output || [],
308
+ description: n.description || '',
309
+ affiliation: n.affiliation || 'root',
310
+ boundDocs: [],
311
+ boundFiles: [],
312
+ boundDirs: [],
313
+ boundTasks: [],
314
+ subFlowchart: null,
315
+ position: null,
316
+ versions: [],
317
+ lastUpdated: '',
318
+ lastQueried: '',
319
+ }));
320
+ const fullEdges = (decoded.edges || []).map((e) => ({
321
+ from: e.from,
322
+ to: e.to,
323
+ label: e.label || null,
324
+ edgeType: e.edgeType || 'call',
325
+ }));
326
+ const result = await client().batchAddToFlowchart(chartId, fullNodes, fullEdges);
327
+ // 构建报告
328
+ let report = `✅ 批量操作完成 [${chartId}]\n`;
329
+ report += ` 添加: ${result.addedNodes.length}节点 ${result.addedEdges}连线\n`;
330
+ if (result.failedNodes.length > 0) {
331
+ report += `\n❌ 失败节点:\n`;
332
+ result.failedNodes.forEach(([id, reason]) => {
333
+ report += ` • ${id}: ${reason}\n`;
334
+ });
335
+ }
336
+ if (result.failedEdges.length > 0) {
337
+ report += `\n❌ 失败连线:\n`;
338
+ result.failedEdges.forEach(([from, to, reason]) => {
339
+ report += ` • ${from} → ${to}: ${reason}\n`;
340
+ });
341
+ }
342
+ if (result.orphanedNodes.length > 0) {
343
+ report += `\n⚠️ 孤立节点 (无任何连线):\n`;
344
+ result.orphanedNodes.forEach(id => {
345
+ report += ` • ${id}\n`;
346
+ });
347
+ }
348
+ else {
349
+ report += `\n✅ 无孤立节点`;
350
+ }
351
+ return wrap(report);
352
+ }
353
+ case 'orphans': {
354
+ const orphans = await client().getFlowchartOrphans();
355
+ if (!orphans || orphans.length === 0) {
356
+ return wrap('✅ 无孤立文档 — 所有文档均已绑定到流程图节点');
357
+ }
358
+ const list = orphans
359
+ .map(o => `• ${o.docPath}${o.docSummary ? ` — ${o.docSummary}` : ''}`)
360
+ .join('\n');
361
+ return wrap(`⚠️ 发现 ${orphans.length} 个孤立文档 (未绑定到任何流程图节点):\n\n${list}\n\n💡 使用 kg_doc(update, bindTo: "节点ID") 绑定`);
362
+ }
363
+ case 'health': {
364
+ const health = await client().getFlowchartHealth();
365
+ if (!health || health.length === 0) {
366
+ return wrap('📊 暂无节点健康数据');
367
+ }
368
+ const items = health;
369
+ const stale = items.filter(h => h.status === 'stale');
370
+ const active = items.filter(h => h.status === 'active');
371
+ const normal = items.filter(h => h.status === 'normal');
372
+ let report = `📊 知识冷热分布 (${items.length}节点):\n`;
373
+ report += ` 🟢 active (7天内查询): ${active.length}\n`;
374
+ report += ` 🟡 normal (30天内): ${normal.length}\n`;
375
+ report += ` 🔴 stale (30天+未查询): ${stale.length}\n`;
376
+ if (stale.length > 0) {
377
+ report += `\n🔴 冷知识节点:\n`;
378
+ stale.forEach(h => {
379
+ report += ` • ${h.nodeLabel} [${h.chartId}/${h.nodeId}] — ${h.daysSinceQuery}天未查询\n`;
380
+ });
381
+ }
382
+ return wrap(report);
383
+ }
384
+ case 'bind':
385
+ case 'unbind': {
386
+ const chartId = decoded.chartId || 'main';
387
+ if (!decoded.nodeId)
388
+ return wrap(`❌ ${decoded.action} 需要 nodeId`);
389
+ const chart = await client().getFlowchart(chartId);
390
+ if (!chart)
391
+ return wrap(`❌ 流程图 "${chartId}" 不存在`);
392
+ const node = chart.nodes?.find((n) => n.id === decoded.nodeId);
393
+ if (!node)
394
+ return wrap(`❌ 节点 "${decoded.nodeId}" 不存在于流程图 "${chartId}"`);
395
+ const isBind = decoded.action === 'bind';
396
+ const changes = [];
397
+ const modify = (items, field, label) => {
398
+ if (!items || items.length === 0)
399
+ return;
400
+ if (!node[field])
401
+ node[field] = [];
402
+ if (isBind) {
403
+ let added = 0;
404
+ for (const item of items) {
405
+ if (!node[field].includes(item)) {
406
+ node[field].push(item);
407
+ added++;
408
+ }
409
+ }
410
+ if (added > 0)
411
+ changes.push(`+${added} ${label}`);
412
+ }
413
+ else {
414
+ const before = node[field].length;
415
+ node[field] = node[field].filter((x) => !items.includes(x));
416
+ const removed = before - node[field].length;
417
+ if (removed > 0)
418
+ changes.push(`-${removed} ${label}`);
419
+ }
420
+ };
421
+ modify(decoded.files, 'boundFiles', '文件');
422
+ modify(decoded.dirs, 'boundDirs', '目录');
423
+ modify(decoded.docs, 'boundDocs', '文档');
424
+ modify(decoded.tasks, 'boundTasks', '任务');
425
+ if (changes.length === 0) {
426
+ return wrap(`ℹ️ 无变更 — 请提供 files/dirs/docs/tasks 参数`);
427
+ }
428
+ await client().saveFlowchart(chartId, chart);
429
+ // P2: 双向同步 — 更新参考文档的 adoptedBy
430
+ const docsList = decoded.docs;
431
+ if (docsList && docsList.length > 0) {
432
+ const nodeId = decoded.nodeId;
433
+ for (const docPath of docsList) {
434
+ try {
435
+ const doc = await client().getDoc(docPath);
436
+ if (!doc?.content)
437
+ continue;
438
+ const content = doc.content;
439
+ // 解析 frontmatter
440
+ if (!content.startsWith('---'))
441
+ continue;
442
+ const endIdx = content.indexOf('---', 3);
443
+ if (endIdx === -1)
444
+ continue;
445
+ const yaml = content.slice(3, endIdx);
446
+ const body = content.slice(endIdx + 3);
447
+ // 提取现有 adoptedBy
448
+ const adoptedMatch = yaml.match(/adoptedBy:\s*\[(.*?)\]/);
449
+ let adopted = [];
450
+ if (adoptedMatch) {
451
+ adopted = adoptedMatch[1].split(',').map(s => s.trim()).filter(Boolean);
452
+ }
453
+ // 修改 adoptedBy
454
+ if (isBind) {
455
+ if (!adopted.includes(nodeId))
456
+ adopted.push(nodeId);
457
+ }
458
+ else {
459
+ adopted = adopted.filter(id => id !== nodeId);
460
+ }
461
+ // 重建 frontmatter
462
+ const newAdoptedLine = `adoptedBy: [${adopted.join(', ')}]`;
463
+ let newYaml;
464
+ if (adoptedMatch) {
465
+ newYaml = yaml.replace(/adoptedBy:\s*\[.*?\]/, newAdoptedLine);
466
+ }
467
+ else {
468
+ newYaml = yaml.trimEnd() + '\n' + newAdoptedLine + '\n';
469
+ }
470
+ const newContent = `---${newYaml}---${body}`;
471
+ await client().updateDoc(docPath, { content: newContent });
472
+ }
473
+ catch { /* 单个文档同步失败不阻断 */ }
474
+ }
475
+ }
476
+ const icon = isBind ? '🔗' : '⛓️';
477
+ return wrap(`${icon} ${isBind ? '绑定' : '解绑'}完成 [${decoded.nodeId}]: ${changes.join(', ')}`);
478
+ }
479
+ default:
480
+ return wrap(`❌ 未知 action: ${decoded.action}`);
481
+ }
482
+ }));
483
+ }
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * MCP 工具注册入口
3
- * 18 个工具, 7 个子模块
3
+ * 14 个工具, 7 个子模块
4
4
  *
5
- * 🔗 初始化: kg_init (1个)
6
- * 📊 导航: kg_status, kg_tree (2个)
7
- * 📚 知识: kg_doc, kg_projects, kg_rules (3个)
8
- * 📝 工作流: kg_task, kg_files, kg_discuss (3个)
9
- * 🔬 代码分析: code_scan, code_query, code_impact, code_context, code_path, code_smart_context, code_full_path (7个)
10
- * 🏛️ 协作: kg_meeting (1个)
5
+ * 🔗 初始化: kg_init (1个)
6
+ * 📊 导航: kg_status, kg_tree (2个)
7
+ * 📚 知识: kg_doc(参考文档), kg_projects, kg_rules (3个)
8
+ * 📝 工作流: kg_task(任务记录), kg_files, kg_discuss(讨论区) (3个)
9
+ * 🔀 关系核心: kg_flowchart(逻辑流程图 关系型知识锚点) (1个)
10
+ * 🔬 代码分析: code_scan, code_smart_context, code_full_path (3个)
11
+ * 🏛️ 协作: kg_meeting (1个)
11
12
  */
12
13
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
14
  export declare function registerTools(server: McpServer, projectId: string, user: string, onProjectChange?: (newProjectId: string, newApiUrl: string) => void): void;
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * MCP 工具注册入口
3
- * 18 个工具, 7 个子模块
3
+ * 14 个工具, 7 个子模块
4
4
  *
5
- * 🔗 初始化: kg_init (1个)
6
- * 📊 导航: kg_status, kg_tree (2个)
7
- * 📚 知识: kg_doc, kg_projects, kg_rules (3个)
8
- * 📝 工作流: kg_task, kg_files, kg_discuss (3个)
9
- * 🔬 代码分析: code_scan, code_query, code_impact, code_context, code_path, code_smart_context, code_full_path (7个)
10
- * 🏛️ 协作: kg_meeting (1个)
5
+ * 🔗 初始化: kg_init (1个)
6
+ * 📊 导航: kg_status, kg_tree (2个)
7
+ * 📚 知识: kg_doc(参考文档), kg_projects, kg_rules (3个)
8
+ * 📝 工作流: kg_task(任务记录), kg_files, kg_discuss(讨论区) (3个)
9
+ * 🔀 关系核心: kg_flowchart(逻辑流程图 关系型知识锚点) (1个)
10
+ * 🔬 代码分析: code_scan, code_smart_context, code_full_path (3个)
11
+ * 🏛️ 协作: kg_meeting (1个)
11
12
  */
12
13
  import { createContext } from './shared.js';
13
14
  import { registerInitTool } from './init.js';
@@ -20,6 +21,7 @@ import { registerFileTools } from './files.js';
20
21
  import { registerDiscussionTools } from './discussion.js';
21
22
  import { registerAnalyzerTools } from './analyzer.js';
22
23
  import { registerMeetingTools } from './meeting.js';
24
+ import { registerFlowchartTools } from './flowchart.js';
23
25
  export function registerTools(server, projectId, user, onProjectChange) {
24
26
  // 创建共享可变上下文 — 所有工具捕获此对象引用
25
27
  const ctx = createContext(projectId, user);
@@ -35,8 +37,10 @@ export function registerTools(server, projectId, user, onProjectChange) {
35
37
  registerTaskTools(server, ctx);
36
38
  registerFileTools(server);
37
39
  registerDiscussionTools(server, ctx);
38
- // 🔬 代码分析 (code_scan, code_query, code_impact, code_context, code_path, code_smart_context, code_full_path)
40
+ // 🔬 代码分析 (code_scan, code_smart_context, code_full_path)
39
41
  registerAnalyzerTools(server, ctx);
40
42
  // 🏛️ 多AI协作 (kg_meeting)
41
43
  registerMeetingTools(server, ctx);
44
+ // 🔀 流程图 (kg_flowchart)
45
+ registerFlowchartTools(server, ctx);
42
46
  }
@@ -10,7 +10,7 @@ import { wrap, safeTool, crossPrefix } from './shared.js';
10
10
  export function registerRuleTools(server, ctx) {
11
11
  const client = () => getClient();
12
12
  server.tool('kg_rules', '📏 项目规则管理 — 读取或保存代码风格、审查规则等。action: get(读取规则)|save(保存规则)|get_meta(读取触发配置)|save_meta(保存触发配置)', {
13
- action: z.enum(['get', 'save', 'get_meta', 'save_meta'])
13
+ action: z.enum(['get', 'save', 'get_meta', 'save_meta', 'delete'])
14
14
  .describe('操作类型'),
15
15
  ruleType: z.string().optional()
16
16
  .describe('规则类型(如 userStyles, codeStyle, reviewRules)。get/save 时使用,不传则获取全部'),
@@ -96,6 +96,24 @@ export function registerRuleTools(server, ctx) {
96
96
  return wrap('❌ 保存失败');
97
97
  return wrap(`✅ 触发配置已保存 (更新${Object.keys(decoded.meta).length}个类型, 共${Object.keys(merged).length}个类型)`);
98
98
  }
99
+ case 'delete': {
100
+ if (!decoded.ruleType)
101
+ return wrap('❌ delete 需要 ruleType');
102
+ if (!decoded.rules || decoded.rules.length === 0)
103
+ return wrap('❌ delete 需要 rules 数组(要删除的规则内容)');
104
+ const existing = await client().getRulesApi(decoded.ruleType);
105
+ const toDelete = new Set(decoded.rules.map((r) => r.trim()));
106
+ const filtered = existing.filter((r) => !toDelete.has(r.trim()));
107
+ const removed = existing.length - filtered.length;
108
+ if (removed === 0)
109
+ return wrap('ℹ️ 未找到匹配的规则');
110
+ const success = await client().saveRulesApi(decoded.ruleType, filtered);
111
+ if (!success)
112
+ return wrap('❌ 删除失败');
113
+ const meta = await client().getRulesMeta();
114
+ const label = meta[decoded.ruleType]?.label || decoded.ruleType;
115
+ return wrap(`✅ ${label}: 已删除${removed}条, 剩余${filtered.length}条`);
116
+ }
99
117
  default:
100
118
  return wrap(`❌ 未知 action: ${decoded.action}`);
101
119
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * 📝 kg_task (4→1)
3
- * 合并: task_create, task_get, task_update, task_archive
2
+ * 📝 kg_task (4→1) — 极简任务管理
3
+ * AI 只需说人话写内容,后端负责所有格式化、分类、时间戳
4
4
  */
5
5
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
6
  import { type McpContext } from './shared.js';