@ppdocs/mcp 3.2.21 → 3.2.22

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.
@@ -87,12 +87,6 @@ export declare class PpdocsApiClient {
87
87
  }>;
88
88
  /** 扫描项目代码, 构建/更新索引 */
89
89
  analyzerScan(projectPath: string, force?: boolean): Promise<unknown>;
90
- /** 查询代码符号 (模糊匹配) */
91
- analyzerQuery(projectPath: string, query: string): Promise<unknown>;
92
- /** 分层影响分析 (BFS 递归, 可指定深度) */
93
- analyzerImpactTree(projectPath: string, symbolName: string, depth?: number): Promise<unknown>;
94
- /** 获取文件 360° 上下文 */
95
- analyzerContext(projectPath: string, filePath: string): Promise<unknown>;
96
90
  /** 智能上下文: 代码+文档+规则+任务全关联 */
97
91
  analyzerSmartContext(projectPath: string, symbolName: string): Promise<unknown>;
98
92
  /** 全关联路径: 两个符号之间的代码+文档路径 */
@@ -465,27 +465,6 @@ export class PpdocsApiClient {
465
465
  body: JSON.stringify({ project_path: projectPath, force }),
466
466
  });
467
467
  }
468
- /** 查询代码符号 (模糊匹配) */
469
- async analyzerQuery(projectPath, query) {
470
- return this.request('/analyzer/query', {
471
- method: 'POST',
472
- body: JSON.stringify({ project_path: projectPath, query }),
473
- });
474
- }
475
- /** 分层影响分析 (BFS 递归, 可指定深度) */
476
- async analyzerImpactTree(projectPath, symbolName, depth = 2) {
477
- return this.request('/analyzer/impact-tree', {
478
- method: 'POST',
479
- body: JSON.stringify({ project_path: projectPath, symbol_name: symbolName, depth }),
480
- });
481
- }
482
- /** 获取文件 360° 上下文 */
483
- async analyzerContext(projectPath, filePath) {
484
- return this.request('/analyzer/context', {
485
- method: 'POST',
486
- body: JSON.stringify({ project_path: projectPath, file_path: filePath }),
487
- });
488
- }
489
468
  /** 智能上下文: 代码+文档+规则+任务全关联 */
490
469
  async analyzerSmartContext(projectPath, symbolName) {
491
470
  return this.request('/analyzer/smart-context', {
@@ -1,8 +1,9 @@
1
1
  /**
2
- * 代码分析引擎工具 (4个)
3
- * code_scan, code_query, code_impact, code_context
2
+ * 代码分析引擎工具 (3个)
3
+ * code_scan, code_smart_context, code_full_path
4
4
  *
5
- * AI Agent 通过 MCP 工具直接查询代码结构、依赖关系和影响范围
5
+ * code_smart_context: 代码+KG文档全关联 (含影响范围)
6
+ * code_full_path: 两点间代码链路+共享KG文档
6
7
  */
7
8
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
9
  import { type McpContext } from './shared.js';
@@ -1,8 +1,9 @@
1
1
  /**
2
- * 代码分析引擎工具 (4个)
3
- * code_scan, code_query, code_impact, code_context
2
+ * 代码分析引擎工具 (3个)
3
+ * code_scan, code_smart_context, code_full_path
4
4
  *
5
- * AI Agent 通过 MCP 工具直接查询代码结构、依赖关系和影响范围
5
+ * code_smart_context: 代码+KG文档全关联 (含影响范围)
6
+ * code_full_path: 两点间代码链路+共享KG文档
6
7
  */
7
8
  import { z } from 'zod';
8
9
  import { getClient } from '../storage/httpClient.js';
@@ -13,13 +14,10 @@ const SYMBOL_ICONS = {
13
14
  type_alias: '𝐓', enum: '𝐄', struct: '𝐒', trait: '⚡',
14
15
  constant: '𝐊', variable: '𝑣',
15
16
  };
16
- const SEVERITY_ICONS = {
17
- critical: '🔴', warning: '🟡', info: '🟢',
18
- };
19
17
  export function registerAnalyzerTools(server, ctx) {
20
18
  const client = () => getClient();
21
19
  // ===== code_scan: 扫描项目代码 =====
22
- server.tool('code_scan', '📡 扫描项目代码, 构建索引。返回文件数、符号数、语言统计。★首次使用 code_query/code_impact/code_context 前必须先执行★', {
20
+ server.tool('code_scan', '📡 扫描项目代码, 构建索引。返回文件数、符号数、语言统计。★首次使用 code_smart_context/code_full_path 前必须先执行★', {
23
21
  projectPath: z.string().optional().describe('项目源码的绝对路径(如"D:/projects/myapp")。不传则自动从Beacon同步目录或项目配置解析'),
24
22
  force: z.boolean().optional().describe('是否强制全量重建(默认false, 增量更新)'),
25
23
  }, async (args) => safeTool(async () => {
@@ -34,235 +32,8 @@ export function registerAnalyzerTools(server, ctx) {
34
32
  `- 索引时间: ${result.indexedAt}`,
35
33
  ].join('\n'));
36
34
  }));
37
- // ===== code_query: 搜索代码符号 =====
38
- server.tool('code_query', '🔤 搜索代码符号(函数/类/方法/接口/类型)。返回匹配列表+文件路径+行号。定位代码位置的最快方式。需先运行 code_scan', {
39
- projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
40
- query: z.string().describe('搜索关键词(函数名/类名/方法名等)'),
41
- }, async (args) => safeTool(async () => {
42
- const results = await client().analyzerQuery(args.projectPath || '', args.query);
43
- if (!results || results.length === 0) {
44
- return wrap(`未找到匹配 "${args.query}" 的符号。请确认已运行 code_scan`);
45
- }
46
- const lines = results.map(r => {
47
- const icon = SYMBOL_ICONS[r.symbol.kind] || '?';
48
- const exp = r.symbol.exported ? ' [export]' : '';
49
- const parent = r.symbol.parent ? ` ◀ ${r.symbol.parent}` : '';
50
- return ` ${icon} ${r.symbol.name}${parent}${exp} → ${r.filePath}:${r.symbol.lineStart}`;
51
- });
52
- return wrap([
53
- `🔤 找到 ${results.length} 个匹配符号:`,
54
- ``,
55
- ...lines,
56
- ].join('\n'));
57
- }));
58
- // ===== code_impact: 分层影响分析 =====
59
- server.tool('code_impact', '💥 爆炸半径分析 ★修改代码前必查★ 分析修改一个函数/类/类型会影响多少文件。BFS分层输出: L1🔴直接引用=必须检查, L2🟡间接引用=建议检查, L3🟢传递引用=注意。修改任何公共接口、函数签名、类型定义前务必先运行!', {
60
- projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
61
- symbolName: z.string().describe('要分析的符号名称(如"AuthService", "handleLogin")'),
62
- depth: z.number().optional().describe('分析深度层级(1-5, 默认2)。1=仅直接引用, 3=深度追踪'),
63
- }, async (args) => safeTool(async () => {
64
- const result = await client().analyzerImpactTree(args.projectPath || '', args.symbolName, args.depth ?? 2);
65
- if (!result) {
66
- return wrap(`未找到符号 "${args.symbolName}"。请确认名称正确且已运行 code_scan`);
67
- }
68
- if (result.levels.length === 0) {
69
- return wrap(`✅ "${args.symbolName}" 没有被其他文件引用, 修改安全`);
70
- }
71
- // 生成分层树形输出
72
- const lines = [
73
- `🎯 ${result.symbolName} (${result.symbolKind})`,
74
- `📍 定义: ${result.definedIn}:${result.lineStart}`,
75
- ``,
76
- `📊 ${result.summary}`,
77
- ``,
78
- ];
79
- for (const level of result.levels) {
80
- const icon = SEVERITY_ICONS[level.severity] || '⚪';
81
- const label = level.depth === 1 ? '直接引用 (必须检查)' :
82
- level.depth === 2 ? '间接引用 (建议检查)' :
83
- '传递引用 (注意)';
84
- lines.push(`### ${icon} L${level.depth} ${label} (${level.count}个)`);
85
- for (const entry of level.entries) {
86
- lines.push(` ${entry.filePath}:${entry.line} [${entry.refKind}] ${entry.context}`);
87
- }
88
- lines.push('');
89
- }
90
- return wrap(lines.join('\n'));
91
- }));
92
- // ===== code_context: 文件360°上下文 =====
93
- server.tool('code_context', '🔍 文件360°上下文 — 定义了什么符号、导入了什么、被谁引用。修改文件前使用, 快速了解所有依赖关系, 避免遗漏', {
94
- projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
95
- filePath: z.string().describe('目标文件的相对路径(如"src/services/auth.ts")'),
96
- }, async (args) => safeTool(async () => {
97
- const fileCtx = await client().analyzerContext(args.projectPath || '', args.filePath);
98
- if (!fileCtx) {
99
- return wrap(`未找到文件 "${args.filePath}"。请确认路径正确, 路径格式为相对路径(如 src/main.ts)`);
100
- }
101
- const lines = [
102
- `📄 ${fileCtx.filePath}`,
103
- `语言: ${fileCtx.language} | ${fileCtx.linesTotal} 行`,
104
- ``,
105
- ];
106
- // 定义的符号
107
- if (fileCtx.symbolsDefined.length > 0) {
108
- lines.push(`### 🔤 定义的符号 (${fileCtx.symbolsDefined.length})`);
109
- for (const sym of fileCtx.symbolsDefined) {
110
- const icon = SYMBOL_ICONS[sym.kind] || '?';
111
- const exp = sym.exported ? ' [export]' : '';
112
- lines.push(` ${icon} ${sym.name}${exp} L${sym.lineStart}-L${sym.lineEnd}`);
113
- }
114
- lines.push('');
115
- }
116
- // 导入
117
- if (fileCtx.imports.length > 0) {
118
- lines.push(`### 📥 导入 (${fileCtx.imports.length})`);
119
- for (const imp of fileCtx.imports) {
120
- const specs = imp.specifiers.length > 0 ? `{ ${imp.specifiers.join(', ')} }` : '*';
121
- lines.push(` → ${imp.source} ${specs} L${imp.line}`);
122
- }
123
- lines.push('');
124
- }
125
- // 被谁引用
126
- if (fileCtx.importedBy.length > 0) {
127
- lines.push(`### 📤 被引用 (${fileCtx.importedBy.length})`);
128
- for (const by of fileCtx.importedBy) {
129
- const specs = by.specifiers.length > 0 ? `{ ${by.specifiers.join(', ')} }` : '';
130
- lines.push(` ← ${by.filePath} ${specs}`);
131
- }
132
- lines.push('');
133
- }
134
- return wrap(lines.join('\n'));
135
- }));
136
- // ===== code_path: 两点间链路追踪 =====
137
- server.tool('code_path', '🔗 两点链路追踪 — 给定两个符号(如 fileA.funcA 和 fileB.funcB),自动搜索中间的引用链路,返回完整路径 + 绑定的知识图谱文档', {
138
- projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
139
- symbolA: z.string().describe('起点符号名(如 "handleLogin")'),
140
- symbolB: z.string().describe('终点符号名(如 "AuthService")'),
141
- maxDepth: z.number().optional().describe('最大搜索深度(1-5, 默认3)'),
142
- }, async (args) => safeTool(async () => {
143
- const depth = Math.min(5, Math.max(1, args.maxDepth ?? 3));
144
- const pp = args.projectPath || '';
145
- // 1. 获取两个符号的影响树
146
- const [treeA, treeB] = await Promise.all([
147
- client().analyzerImpactTree(pp, args.symbolA, depth),
148
- client().analyzerImpactTree(pp, args.symbolB, depth),
149
- ]);
150
- if (!treeA)
151
- return wrap(`❌ 未找到起点符号 "${args.symbolA}"。请确认名称正确且已运行 code_scan`);
152
- if (!treeB)
153
- return wrap(`❌ 未找到终点符号 "${args.symbolB}"。请确认名称正确且已运行 code_scan`);
154
- const graphA = new Map(); // 从 A 出发可达的文件
155
- const graphB = new Map(); // 从 B 出发可达的文件
156
- const buildGraph = (tree, graph) => {
157
- graph.set(tree.definedIn, []);
158
- for (const level of tree.levels) {
159
- for (const entry of level.entries) {
160
- if (!graph.has(entry.filePath))
161
- graph.set(entry.filePath, []);
162
- graph.get(entry.filePath).push({
163
- file: entry.filePath,
164
- line: entry.line,
165
- depth: level.depth,
166
- from: tree.symbolName,
167
- });
168
- }
169
- }
170
- };
171
- buildGraph(treeA, graphA);
172
- buildGraph(treeB, graphB);
173
- // 3. 查找交集: A 和 B 都能到达的文件
174
- const intersection = [];
175
- for (const [fileA] of graphA) {
176
- if (graphB.has(fileA)) {
177
- const refsA = graphA.get(fileA);
178
- const refsB = graphB.get(fileA);
179
- const minDepthA = refsA.length > 0 ? Math.min(...refsA.map(r => r.depth)) : 0;
180
- const minDepthB = refsB.length > 0 ? Math.min(...refsB.map(r => r.depth)) : 0;
181
- intersection.push({ file: fileA, depthFromA: minDepthA, depthFromB: minDepthB });
182
- }
183
- }
184
- // 4. 尝试获取 KG 文档绑定
185
- let docBindings = {};
186
- try {
187
- const docClient = client();
188
- const allDocs = await docClient.listDocs();
189
- for (const doc of allDocs) {
190
- if (!doc.isDir && doc.summary) {
191
- docBindings[doc.name] = doc.summary;
192
- }
193
- }
194
- }
195
- catch { /* KG 不可用时忽略 */ }
196
- // 5. 格式化输出
197
- const lines = [
198
- `🔗 两点链路追踪`,
199
- ``,
200
- `📍 起点: ${treeA.symbolName} (${treeA.symbolKind}) → ${treeA.definedIn}:${treeA.lineStart}`,
201
- `📍 终点: ${treeB.symbolName} (${treeB.symbolKind}) → ${treeB.definedIn}:${treeB.lineStart}`,
202
- ``,
203
- ];
204
- // 直接引用关系
205
- if (treeA.definedIn === treeB.definedIn) {
206
- lines.push(`✅ 两个符号定义在同一文件中!`);
207
- lines.push('');
208
- }
209
- // 检查 A 的影响树是否直接包含 B 的文件
210
- const directAtoB = graphA.has(treeB.definedIn);
211
- const directBtoA = graphB.has(treeA.definedIn);
212
- if (directAtoB || directBtoA) {
213
- lines.push(`### ✅ 发现直接链路`);
214
- if (directAtoB) {
215
- const refs = graphA.get(treeB.definedIn);
216
- lines.push(` ${treeA.symbolName} → [L${refs[0]?.depth || 1}] → ${treeB.definedIn} (含 ${treeB.symbolName})`);
217
- }
218
- if (directBtoA) {
219
- const refs = graphB.get(treeA.definedIn);
220
- lines.push(` ${treeB.symbolName} → [L${refs[0]?.depth || 1}] → ${treeA.definedIn} (含 ${treeA.symbolName})`);
221
- }
222
- lines.push('');
223
- }
224
- if (intersection.length > 0) {
225
- // 按总深度排序
226
- intersection.sort((a, b) => (a.depthFromA + a.depthFromB) - (b.depthFromA + b.depthFromB));
227
- lines.push(`### 🔗 共经文件 (${intersection.length}个)`);
228
- lines.push(`| 文件 | 距${treeA.symbolName} | 距${treeB.symbolName} | 📚 绑定文档 |`);
229
- lines.push(`|:---|:---:|:---:|:---|`);
230
- for (const node of intersection.slice(0, 20)) {
231
- const fileName = node.file.split('/').pop() || node.file;
232
- const docBind = docBindings[fileName] || '—';
233
- lines.push(`| ${node.file} | L${node.depthFromA} | L${node.depthFromB} | ${docBind} |`);
234
- }
235
- if (intersection.length > 20) {
236
- lines.push(`| ... | | | (还有 ${intersection.length - 20} 个) |`);
237
- }
238
- lines.push('');
239
- }
240
- else if (!directAtoB && !directBtoA) {
241
- lines.push(`⚠️ 在 ${depth} 层深度内未发现 ${treeA.symbolName} 与 ${treeB.symbolName} 的引用链路`);
242
- lines.push(`建议: 增加 maxDepth 参数(当前=${depth}, 最大=5) 重试`);
243
- lines.push('');
244
- }
245
- // 附加: 各符号的影响概要
246
- lines.push(`### 📊 影响概要`);
247
- lines.push(`- ${treeA.symbolName}: ${treeA.summary}`);
248
- lines.push(`- ${treeB.symbolName}: ${treeB.summary}`);
249
- // 附加: 绑定的 KG 文档
250
- const relevantDocs = [];
251
- const symbolNames = [treeA.symbolName, treeB.symbolName];
252
- for (const [docName, docSummary] of Object.entries(docBindings)) {
253
- if (symbolNames.some(s => docName.toLowerCase().includes(s.toLowerCase()) || s.toLowerCase().includes(docName.toLowerCase()))) {
254
- relevantDocs.push(` 📚 ${docName}: ${docSummary}`);
255
- }
256
- }
257
- if (relevantDocs.length > 0) {
258
- lines.push('');
259
- lines.push(`### 📚 相关知识图谱文档`);
260
- lines.push(...relevantDocs);
261
- }
262
- return wrap(lines.join('\n'));
263
- }));
264
35
  // ===== code_smart_context: 代码+文档全关联上下文 =====
265
- server.tool('code_smart_context', '🔍 代码+文档全关联上下文 — 输入一个函数名,一次调用返回:代码依赖、关联文档、匹配规则、活跃任务、影响范围摘要。需先运行 code_scan', {
36
+ server.tool('code_smart_context', '🔍 代码+文档全关联上下文 — 输入一个函数名,一次调用返回:代码依赖、关联文档、匹配规则、活跃任务、影响范围摘要。需先运行 code_scan', {
266
37
  projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
267
38
  symbolName: z.string().describe('要查询的符号名称(如"handleLogin", "AuthService")'),
268
39
  }, async (args) => safeTool(async () => {
@@ -322,7 +93,7 @@ export function registerAnalyzerTools(server, ctx) {
322
93
  return wrap(lines.join('\n'));
323
94
  }));
324
95
  // ===== code_full_path: 全关联路径 =====
325
- server.tool('code_full_path', '🔗 全关联路径 — 两个符号之间不仅返回代码引用链路,还返回共享的KG文档、共同导入、祖先模块。需先运行 code_scan', {
96
+ server.tool('code_full_path', '🔗 全关联路径 — 两个符号之间不仅返回代码引用链路,还返回共享的KG文档、共同导入、祖先模块。需先运行 code_scan', {
326
97
  projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
327
98
  symbolA: z.string().describe('起点符号名(如 "handleLogin")'),
328
99
  symbolB: z.string().describe('终点符号名(如 "AuthService")'),
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * MCP 工具注册入口
3
- * 18 个工具, 7 个子模块
3
+ * 14 个工具, 7 个子模块
4
4
  *
5
5
  * 🔗 初始化: kg_init (1个)
6
6
  * 📊 导航: kg_status, kg_tree (2个)
7
7
  * 📚 知识: kg_doc, kg_projects, kg_rules (3个)
8
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个)
9
+ * 🔬 代码分析: code_scan, code_smart_context, code_full_path (3个)
10
10
  * 🏛️ 协作: kg_meeting (1个)
11
11
  */
12
12
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * MCP 工具注册入口
3
- * 18 个工具, 7 个子模块
3
+ * 14 个工具, 7 个子模块
4
4
  *
5
5
  * 🔗 初始化: kg_init (1个)
6
6
  * 📊 导航: kg_status, kg_tree (2个)
7
7
  * 📚 知识: kg_doc, kg_projects, kg_rules (3个)
8
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个)
9
+ * 🔬 代码分析: code_scan, code_smart_context, code_full_path (3个)
10
10
  * 🏛️ 协作: kg_meeting (1个)
11
11
  */
12
12
  import { createContext } from './shared.js';
@@ -35,7 +35,7 @@ export function registerTools(server, projectId, user, onProjectChange) {
35
35
  registerTaskTools(server, ctx);
36
36
  registerFileTools(server);
37
37
  registerDiscussionTools(server, ctx);
38
- // 🔬 代码分析 (code_scan, code_query, code_impact, code_context, code_path, code_smart_context, code_full_path)
38
+ // 🔬 代码分析 (code_scan, code_smart_context, code_full_path)
39
39
  registerAnalyzerTools(server, ctx);
40
40
  // 🏛️ 多AI协作 (kg_meeting)
41
41
  registerMeetingTools(server, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppdocs/mcp",
3
- "version": "3.2.21",
3
+ "version": "3.2.22",
4
4
  "description": "ppdocs MCP Server - Knowledge Graph for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",