@ppdocs/mcp 2.6.29 → 2.6.31
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/dist/tools/index.js +8 -52
- package/dist/vector/manager.d.ts +14 -5
- package/dist/vector/manager.js +89 -5
- package/package.json +1 -1
package/dist/tools/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import * as storage from '../storage/httpClient.js';
|
|
3
3
|
import { decodeUnicodeEscapes, decodeObjectStrings, getRules, RULE_TYPE_LABELS } from '../utils.js';
|
|
4
4
|
import * as vector from '../vector/index.js';
|
|
5
|
-
|
|
5
|
+
// vectorManager 已弃用,向量索引由 Tauri 后端自动维护
|
|
6
6
|
import { wrap, fetchRelations, mergeSearchResults, formatNodeMarkdown, formatRelationsMarkdown, buildDirectoryTree, formatTreeText, countTreeNodes } from './helpers.js';
|
|
7
7
|
export function registerTools(server, projectId, _user) {
|
|
8
8
|
// 1. 创建节点
|
|
@@ -35,27 +35,13 @@ export function registerTools(server, projectId, _user) {
|
|
|
35
35
|
dependencies: decoded.dependencies || [],
|
|
36
36
|
relatedFiles: decoded.relatedFiles || []
|
|
37
37
|
});
|
|
38
|
-
//
|
|
39
|
-
try {
|
|
40
|
-
await vector.upsertNode(projectId, node.id, node.title, node.description, node.categories);
|
|
41
|
-
}
|
|
42
|
-
catch (e) {
|
|
43
|
-
console.warn('[kg_create_node] Vector index update failed:', e);
|
|
44
|
-
}
|
|
38
|
+
// 向量索引由 Tauri 后端自动维护,MCP 不再手动更新
|
|
45
39
|
return wrap(JSON.stringify(node, null, 2));
|
|
46
40
|
});
|
|
47
41
|
// 2. 删除节点
|
|
48
42
|
server.tool('kg_delete_node', '删除节点(锁定节点和根节点不可删除)', { nodeId: z.string().describe('节点ID') }, async (args) => {
|
|
49
43
|
const success = await storage.deleteNode(projectId, args.nodeId);
|
|
50
|
-
//
|
|
51
|
-
if (success) {
|
|
52
|
-
try {
|
|
53
|
-
await vector.removeNode(projectId, args.nodeId);
|
|
54
|
-
}
|
|
55
|
-
catch (e) {
|
|
56
|
-
console.warn('[kg_delete_node] Vector index removal failed:', e);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
44
|
+
// 向量索引由 Tauri 后端自动维护,MCP 不再手动删除
|
|
59
45
|
return wrap(success ? '删除成功' : '删除失败(节点不存在/已锁定/是根节点)');
|
|
60
46
|
});
|
|
61
47
|
// 3. 更新节点
|
|
@@ -110,15 +96,7 @@ export function registerTools(server, projectId, _user) {
|
|
|
110
96
|
if (lastSyncAt !== undefined)
|
|
111
97
|
updates.lastSyncAt = lastSyncAt;
|
|
112
98
|
const node = await storage.updateNode(projectId, nodeId, updates);
|
|
113
|
-
//
|
|
114
|
-
if (node && (rest.title || rest.description || tags)) {
|
|
115
|
-
try {
|
|
116
|
-
await vector.upsertNode(projectId, node.id, node.title, node.description, node.categories);
|
|
117
|
-
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
console.warn('[kg_update_node] Vector index update failed:', e);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
99
|
+
// 向量索引由 Tauri 后端自动维护,MCP 不再手动更新
|
|
122
100
|
return wrap(node ? JSON.stringify(node, null, 2) : '更新失败(节点不存在或已锁定)');
|
|
123
101
|
});
|
|
124
102
|
// 3.5 更新根节点 (项目介绍)
|
|
@@ -207,8 +185,7 @@ export function registerTools(server, projectId, _user) {
|
|
|
207
185
|
let semanticResults = [];
|
|
208
186
|
if (mode === 'hybrid' || mode === 'semantic') {
|
|
209
187
|
try {
|
|
210
|
-
//
|
|
211
|
-
await vectorManager.ensureIndex(projectId);
|
|
188
|
+
// 向量索引由 Tauri 后端自动维护,MCP 直接读取
|
|
212
189
|
const rawSemantic = await vector.semanticSearch(projectId, query, limit * 2);
|
|
213
190
|
// 补全节点信息 (关键词结果中已有的复用,没有的获取)
|
|
214
191
|
const keywordMap = new Map(keywordResults.map(r => [r.id, r]));
|
|
@@ -241,18 +218,6 @@ export function registerTools(server, projectId, _user) {
|
|
|
241
218
|
const json = JSON.stringify(output, null, 2);
|
|
242
219
|
return wrap(`${json}\n\n💡 需要详细内容请使用 kg_read_node(nodeId) 获取节点详情\n🔍 搜索模式: ${mode}`);
|
|
243
220
|
});
|
|
244
|
-
// 5.5 列出所有标签
|
|
245
|
-
server.tool('kg_list_tags', '获取所有节点使用过的标签列表(去重)', {}, async () => {
|
|
246
|
-
const nodes = await storage.listNodes(projectId);
|
|
247
|
-
const tagSet = new Set();
|
|
248
|
-
for (const node of nodes) {
|
|
249
|
-
if (node.categories && Array.isArray(node.categories)) {
|
|
250
|
-
node.categories.forEach(cat => tagSet.add(cat));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
const tags = Array.from(tagSet).sort();
|
|
254
|
-
return wrap(JSON.stringify({ total: tags.length, tags }, null, 2));
|
|
255
|
-
});
|
|
256
221
|
// 6. 路径查找
|
|
257
222
|
server.tool('kg_find_path', '查找两节点间的依赖路径', {
|
|
258
223
|
startId: z.string().describe('起点节点ID'),
|
|
@@ -438,22 +403,13 @@ export function registerTools(server, projectId, _user) {
|
|
|
438
403
|
return wrap(JSON.stringify(result, null, 2));
|
|
439
404
|
});
|
|
440
405
|
// ===================== 向量索引管理 =====================
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
const count = await vectorManager.rebuildIndex(projectId);
|
|
444
|
-
return wrap(`✅ 向量索引构建完成,共 ${count} 个节点`);
|
|
445
|
-
});
|
|
446
|
-
// 17. 查看索引状态
|
|
406
|
+
// 注意: 向量索引由 Tauri 后端自动维护,MCP 只提供只读查询
|
|
407
|
+
// 16. 查看索引状态 (只读)
|
|
447
408
|
server.tool('kg_index_stats', '查看向量索引状态', {}, async () => {
|
|
448
409
|
const stats = await vector.getIndexStats(projectId);
|
|
449
|
-
const rebuildCheck = await vectorManager.needsRebuild(projectId);
|
|
450
410
|
return wrap(JSON.stringify({
|
|
451
411
|
...stats,
|
|
452
|
-
|
|
453
|
-
rebuildReason: rebuildCheck.reason,
|
|
454
|
-
tip: rebuildCheck.needed
|
|
455
|
-
? `索引需要重建,原因: ${rebuildCheck.reason}`
|
|
456
|
-
: '索引正常 (搜索时会自动检测并重建)'
|
|
412
|
+
tip: '向量索引由软件端自动维护,节点增删改时自动更新'
|
|
457
413
|
}, null, 2));
|
|
458
414
|
});
|
|
459
415
|
}
|
package/dist/vector/manager.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 向量索引管理器
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 向量索引管理器 (已弃用)
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ 自 v2.7.0 起,向量索引由 Tauri 后端自动维护
|
|
5
|
+
* 此模块保留仅用于向后兼容,不再被主动调用
|
|
6
|
+
*
|
|
7
|
+
* @deprecated 请使用 Tauri 后端的 vector 模块
|
|
8
|
+
* @version 0.2
|
|
5
9
|
*/
|
|
6
10
|
interface RebuildReason {
|
|
7
11
|
needed: boolean;
|
|
8
|
-
reason: 'missing' | 'model_mismatch' | 'empty' | 'none';
|
|
12
|
+
reason: 'missing' | 'model_mismatch' | 'empty' | 'count_mismatch' | 'none';
|
|
13
|
+
missingCount?: number;
|
|
9
14
|
}
|
|
10
15
|
interface RebuildResult {
|
|
11
16
|
projectId: string;
|
|
@@ -21,7 +26,11 @@ export declare function needsRebuild(projectId: string): Promise<RebuildReason>;
|
|
|
21
26
|
*/
|
|
22
27
|
export declare function rebuildIndex(projectId: string): Promise<number>;
|
|
23
28
|
/**
|
|
24
|
-
*
|
|
29
|
+
* 增量同步缺失节点 (仅补漏,不全量重建)
|
|
30
|
+
*/
|
|
31
|
+
export declare function syncMissingNodes(projectId: string): Promise<number>;
|
|
32
|
+
/**
|
|
33
|
+
* 确保索引存在 (自动检测 + 按需构建/增量补漏)
|
|
25
34
|
*/
|
|
26
35
|
export declare function ensureIndex(projectId: string): Promise<void>;
|
|
27
36
|
/**
|
package/dist/vector/manager.js
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 向量索引管理器
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 向量索引管理器 (已弃用)
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ 自 v2.7.0 起,向量索引由 Tauri 后端自动维护
|
|
5
|
+
* 此模块保留仅用于向后兼容,不再被主动调用
|
|
6
|
+
*
|
|
7
|
+
* @deprecated 请使用 Tauri 后端的 vector 模块
|
|
8
|
+
* @version 0.2
|
|
5
9
|
*/
|
|
6
10
|
import * as fs from 'fs';
|
|
7
11
|
import * as path from 'path';
|
|
8
12
|
import * as vectorCore from './index.js';
|
|
9
13
|
// ==================== 检测函数 ====================
|
|
14
|
+
/**
|
|
15
|
+
* 获取节点目录下的节点ID列表
|
|
16
|
+
*/
|
|
17
|
+
function getNodeIdsFromDisk(projectId) {
|
|
18
|
+
const baseDir = process.env.PPDOCS_DATA_DIR || path.join(process.env.HOME || process.env.USERPROFILE || '', '.ppdocs');
|
|
19
|
+
const nodesDir = path.join(baseDir, 'projects', projectId, 'nodes');
|
|
20
|
+
const nodeIds = new Set();
|
|
21
|
+
if (!fs.existsSync(nodesDir))
|
|
22
|
+
return nodeIds;
|
|
23
|
+
try {
|
|
24
|
+
const files = fs.readdirSync(nodesDir).filter(f => f.endsWith('.json'));
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
try {
|
|
27
|
+
const content = fs.readFileSync(path.join(nodesDir, file), 'utf-8');
|
|
28
|
+
const node = JSON.parse(content);
|
|
29
|
+
// 跳过根节点
|
|
30
|
+
if (node.isOrigin || node.id === 'root')
|
|
31
|
+
continue;
|
|
32
|
+
nodeIds.add(node.id);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// 跳过无法解析的文件
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// 忽略读取错误
|
|
41
|
+
}
|
|
42
|
+
return nodeIds;
|
|
43
|
+
}
|
|
10
44
|
/**
|
|
11
45
|
* 检测索引是否需要重建
|
|
12
46
|
*/
|
|
@@ -28,6 +62,13 @@ export async function needsRebuild(projectId) {
|
|
|
28
62
|
if (!data.entries || data.entries.length === 0) {
|
|
29
63
|
return { needed: true, reason: 'empty' };
|
|
30
64
|
}
|
|
65
|
+
// 5. 检测节点数量不匹配 (新增)
|
|
66
|
+
const diskNodeIds = getNodeIdsFromDisk(projectId);
|
|
67
|
+
const indexedIds = new Set(data.entries.map(e => e.id));
|
|
68
|
+
const missingIds = [...diskNodeIds].filter(id => !indexedIds.has(id));
|
|
69
|
+
if (missingIds.length > 0) {
|
|
70
|
+
return { needed: true, reason: 'count_mismatch', missingCount: missingIds.length };
|
|
71
|
+
}
|
|
31
72
|
return { needed: false, reason: 'none' };
|
|
32
73
|
}
|
|
33
74
|
catch {
|
|
@@ -87,11 +128,54 @@ export async function rebuildIndex(projectId) {
|
|
|
87
128
|
return vectorCore.buildIndex(projectId, nodes);
|
|
88
129
|
}
|
|
89
130
|
/**
|
|
90
|
-
*
|
|
131
|
+
* 增量同步缺失节点 (仅补漏,不全量重建)
|
|
132
|
+
*/
|
|
133
|
+
export async function syncMissingNodes(projectId) {
|
|
134
|
+
const indexPath = vectorCore.getIndexPath(projectId);
|
|
135
|
+
// 读取当前索引
|
|
136
|
+
let indexedIds = new Set();
|
|
137
|
+
try {
|
|
138
|
+
if (fs.existsSync(indexPath)) {
|
|
139
|
+
const data = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
140
|
+
indexedIds = new Set(data.entries.map(e => e.id));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// 索引损坏,需要全量重建
|
|
145
|
+
return rebuildIndex(projectId);
|
|
146
|
+
}
|
|
147
|
+
// 找出缺失的节点
|
|
148
|
+
const allNodes = await fetchNodesForIndex(projectId);
|
|
149
|
+
const missingNodes = allNodes.filter(n => !indexedIds.has(n.id));
|
|
150
|
+
if (missingNodes.length === 0) {
|
|
151
|
+
console.log(`[VectorManager] No missing nodes to sync`);
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
console.log(`[VectorManager] Syncing ${missingNodes.length} missing nodes...`);
|
|
155
|
+
// 增量添加缺失节点
|
|
156
|
+
for (let i = 0; i < missingNodes.length; i++) {
|
|
157
|
+
const node = missingNodes[i];
|
|
158
|
+
await vectorCore.upsertNode(projectId, node.id, node.title, node.description, node.categories || []);
|
|
159
|
+
if ((i + 1) % 10 === 0) {
|
|
160
|
+
console.log(`[VectorManager] Synced ${i + 1}/${missingNodes.length}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log(`[VectorManager] Sync complete: ${missingNodes.length} nodes added`);
|
|
164
|
+
return missingNodes.length;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 确保索引存在 (自动检测 + 按需构建/增量补漏)
|
|
91
168
|
*/
|
|
92
169
|
export async function ensureIndex(projectId) {
|
|
93
170
|
const check = await needsRebuild(projectId);
|
|
94
|
-
if (check.needed)
|
|
171
|
+
if (!check.needed)
|
|
172
|
+
return;
|
|
173
|
+
// count_mismatch 使用增量补漏,其他情况全量重建
|
|
174
|
+
if (check.reason === 'count_mismatch') {
|
|
175
|
+
console.log(`[VectorManager] Auto-syncing ${check.missingCount} missing nodes`);
|
|
176
|
+
await syncMissingNodes(projectId);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
95
179
|
console.log(`[VectorManager] Auto-rebuilding index, reason: ${check.reason}`);
|
|
96
180
|
await rebuildIndex(projectId);
|
|
97
181
|
}
|