@ppdocs/mcp 3.2.24 → 3.2.26

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.
@@ -51,6 +51,8 @@ export declare class PpdocsApiClient {
51
51
  updateFlowchartNode(chartId: string, nodeId: string, node: unknown, changeDesc?: string): Promise<unknown>;
52
52
  /** 原子删除单个节点 (后端自动清理关联边) */
53
53
  deleteFlowchartNode(chartId: string, nodeId: string): Promise<unknown>;
54
+ /** 删除流程图 (后端自动级联删子图+清父节点引用) */
55
+ deleteFlowchart(chartId: string): Promise<unknown>;
54
56
  getFlowchartOrphans(): Promise<unknown[]>;
55
57
  getFlowchartHealth(): Promise<unknown[]>;
56
58
  /** 列出所有可访问的项目 */
@@ -372,6 +372,12 @@ export class PpdocsApiClient {
372
372
  method: 'DELETE'
373
373
  });
374
374
  }
375
+ /** 删除流程图 (后端自动级联删子图+清父节点引用) */
376
+ async deleteFlowchart(chartId) {
377
+ return this.request(`/flowcharts/${chartId}`, {
378
+ method: 'DELETE'
379
+ });
380
+ }
375
381
  async getFlowchartOrphans() {
376
382
  return this.request('/flowcharts/orphans');
377
383
  }
@@ -24,8 +24,8 @@ const EdgeSchema = z.object({
24
24
  });
25
25
  export function registerFlowchartTools(server, ctx) {
26
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'])
27
+ server.tool('kg_flowchart', '🔀 逻辑流程图(关系型知识锚点) — action: list|get|get_node(★爆炸式上下文)|update_node|delete_node|batch_add|bind|unbind|orphans|health|create_chart|delete_chart', {
28
+ action: z.enum(['list', 'get', 'get_node', 'update_node', 'delete_node', 'batch_add', 'bind', 'unbind', 'orphans', 'health', 'create_chart', 'delete_chart'])
29
29
  .describe('操作类型'),
30
30
  chartId: z.string().optional()
31
31
  .describe('流程图ID (默认"main")'),
@@ -49,6 +49,15 @@ export function registerFlowchartTools(server, ctx) {
49
49
  domain: z.string().optional().describe('update_node: 新领域'),
50
50
  input: z.array(z.string()).optional().describe('update_node: 新输入 / bind的源代码文件'),
51
51
  output: z.array(z.string()).optional().describe('update_node: 新输出'),
52
+ subFlowchart: z.string().optional().describe('update_node: bind sub-chart ID / auto-set on create_chart'),
53
+ title: z.string().optional().describe('create_chart: sub-chart title'),
54
+ parentChart: z.string().optional().describe('create_chart: parent chart ID (default main)'),
55
+ parentNode: z.string().optional().describe('create_chart: mount to parent node ID'),
56
+ // update_node: append doc entry
57
+ docSummary: z.string().optional().describe('update_node: doc summary (one-line)'),
58
+ docContent: z.string().optional().describe('update_node: doc content (full markdown)'),
59
+ // get_node: doc display control
60
+ fullDoc: z.boolean().optional().describe('get_node: show ALL doc entries in full (default false, only latest full)'),
52
61
  // bind/unbind
53
62
  files: z.array(z.string()).optional()
54
63
  .describe('源代码文件路径 (bind/unbind)'),
@@ -128,15 +137,21 @@ export function registerFlowchartTools(server, ctx) {
128
137
  if (target.lastUpdated)
129
138
  lines.push(`🕐 最后更新: ${target.lastUpdated}`);
130
139
  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
+ // ======== node doc entries ========
141
+ if (showDoc && target.docEntries?.length) {
142
+ const entries = target.docEntries;
143
+ const showFullAll = decoded.fullDoc === true;
144
+ lines.push(`### 📖 Node Documentation (${entries.length} entries)`);
145
+ for (let i = 0; i < entries.length; i++) {
146
+ const e = entries[i];
147
+ const isLatest = i === entries.length - 1;
148
+ if (isLatest || showFullAll) {
149
+ lines.push(`\n**${e.version}** (${e.date})${isLatest ? ' ★LATEST' : ''}`);
150
+ lines.push(`> ${e.summary}`);
151
+ lines.push(e.content);
152
+ }
153
+ else {
154
+ lines.push(` ${e.version} (${e.date}): ${e.summary}`);
140
155
  }
141
156
  }
142
157
  lines.push('');
@@ -281,6 +296,33 @@ export function registerFlowchartTools(server, ctx) {
281
296
  node.output = decoded.output;
282
297
  changes.push('output');
283
298
  }
299
+ if (decoded.subFlowchart !== undefined) {
300
+ node.subFlowchart = decoded.subFlowchart;
301
+ changes.push('subFlowchart');
302
+ }
303
+ // append doc entry
304
+ if (decoded.docSummary || decoded.docContent) {
305
+ if (!decoded.docSummary || !decoded.docContent) {
306
+ return wrap('\u274c update_node: docSummary and docContent must both be provided');
307
+ }
308
+ const docEntries = node.docEntries || [];
309
+ // auto-increment version: v0.1, v0.2, ...
310
+ let nextVer = 0.1;
311
+ if (docEntries.length > 0) {
312
+ const lastVer = docEntries[docEntries.length - 1].version;
313
+ const num = parseFloat(lastVer.replace('v', ''));
314
+ if (!isNaN(num))
315
+ nextVer = Math.round((num + 0.1) * 10) / 10;
316
+ }
317
+ docEntries.push({
318
+ version: `v${nextVer.toFixed(1)}`,
319
+ date: new Date().toISOString(),
320
+ summary: decoded.docSummary,
321
+ content: decoded.docContent,
322
+ });
323
+ node.docEntries = docEntries;
324
+ changes.push(`docEntries(v${nextVer.toFixed(1)})`);
325
+ }
284
326
  if (changes.length === 0)
285
327
  return wrap('ℹ️ 无变更 — 请提供要更新的字段');
286
328
  node.lastUpdated = new Date().toISOString();
@@ -314,10 +356,10 @@ export function registerFlowchartTools(server, ctx) {
314
356
  boundFiles: [],
315
357
  boundDirs: [],
316
358
  boundTasks: [],
317
- subFlowchart: null,
359
+ subFlowchart: n.subFlowchart || null,
318
360
  position: null,
319
- versions: [],
320
- lastUpdated: '',
361
+ versions: [{ version: 'v1.0', date: new Date().toISOString(), changes: 'initial' }],
362
+ lastUpdated: new Date().toISOString(),
321
363
  lastQueried: '',
322
364
  }));
323
365
  const fullEdges = (decoded.edges || []).map((e) => ({
@@ -479,6 +521,72 @@ export function registerFlowchartTools(server, ctx) {
479
521
  const icon = isBind ? '🔗' : '⛓️';
480
522
  return wrap(`${icon} ${isBind ? '绑定' : '解绑'}完成 [${decoded.nodeId}]: ${changes.join(', ')}`);
481
523
  }
524
+ case 'create_chart': {
525
+ const newChartId = decoded.chartId;
526
+ if (!newChartId)
527
+ return wrap('❌ create_chart 需要 chartId (子图ID)');
528
+ if (newChartId === 'main')
529
+ return wrap('❌ 不能创建 main,主图已存在');
530
+ // 检查是否已存在
531
+ const existing = await client().getFlowchart(newChartId);
532
+ if (existing)
533
+ return wrap(`❌ 流程图 "${newChartId}" 已存在`);
534
+ const pChart = decoded.parentChart || 'main';
535
+ const pNode = decoded.parentNode;
536
+ // 构建子图数据
537
+ const subChartData = {
538
+ id: newChartId,
539
+ title: decoded.title || newChartId,
540
+ parentNode: pNode || null,
541
+ parentChart: pChart,
542
+ nodes: [],
543
+ edges: [],
544
+ };
545
+ // 如果提供了 nodes/edges,填充进去
546
+ if (decoded.nodes && decoded.nodes.length > 0) {
547
+ subChartData.nodes = decoded.nodes.map((n) => ({
548
+ id: n.id, label: n.label,
549
+ nodeType: n.nodeType || 'process', domain: n.domain || 'system',
550
+ input: n.input || [], output: n.output || [],
551
+ description: n.description || '', affiliation: n.affiliation || 'root',
552
+ boundDocs: [], boundFiles: [], boundDirs: [], boundTasks: [],
553
+ subFlowchart: null, position: null,
554
+ versions: [{ version: 'v1.0', date: new Date().toISOString(), changes: 'initial' }],
555
+ lastUpdated: new Date().toISOString(), lastQueried: '',
556
+ }));
557
+ }
558
+ if (decoded.edges && decoded.edges.length > 0) {
559
+ subChartData.edges = decoded.edges.map(e => ({
560
+ from: e.from, to: e.to, label: e.label || null, edgeType: e.edgeType || 'call',
561
+ }));
562
+ }
563
+ // 保存子图
564
+ await client().saveFlowchart(newChartId, subChartData);
565
+ // 自动更新父节点的 subFlowchart 引用
566
+ if (pNode) {
567
+ const parentChart = await client().getFlowchart(pChart);
568
+ if (parentChart) {
569
+ const parentNodeObj = (parentChart.nodes || []).find((n) => n.id === pNode);
570
+ if (parentNodeObj) {
571
+ parentNodeObj.subFlowchart = newChartId;
572
+ parentNodeObj.lastUpdated = new Date().toISOString();
573
+ await client().saveFlowchart(pChart, parentChart);
574
+ }
575
+ }
576
+ }
577
+ const nodeCount = subChartData.nodes.length;
578
+ return wrap(`✅ 子图已创建: ${decoded.title || newChartId} [${newChartId}]\n父图: ${pChart}${pNode ? ` → 节点: ${pNode}` : ''}\n初始: ${nodeCount}节点`);
579
+ }
580
+ case 'delete_chart': {
581
+ const chartId = decoded.chartId;
582
+ if (!chartId)
583
+ return wrap('❌ delete_chart 需要 chartId');
584
+ if (chartId === 'main')
585
+ return wrap('❌ 不能删除主图 main');
586
+ // 后端自动级联: 清父节点引用 + 删子图的子图
587
+ await client().deleteFlowchart(chartId);
588
+ return wrap(`✅ 流程图已删除 [${chartId}] (子图已级联清理)`);
589
+ }
482
590
  default:
483
591
  return wrap(`❌ 未知 action: ${decoded.action}`);
484
592
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppdocs/mcp",
3
- "version": "3.2.24",
3
+ "version": "3.2.26",
4
4
  "description": "ppdocs MCP Server - Knowledge Graph for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",