@ppdocs/mcp 3.0.7 → 3.1.0
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/storage/httpClient.d.ts +6 -6
- package/dist/storage/httpClient.js +17 -17
- package/dist/tools/index.js +21 -20
- package/package.json +1 -1
|
@@ -72,8 +72,8 @@ export declare class PpdocsApiClient {
|
|
|
72
72
|
listFiles(dir?: string): Promise<FileInfo[]>;
|
|
73
73
|
/** 读取项目文件 */
|
|
74
74
|
readFile(filePath: string): Promise<string>;
|
|
75
|
-
/**
|
|
76
|
-
|
|
75
|
+
/** 下载项目文件或目录 (zip) → 自动解压到指定目录或 temp 目录 */
|
|
76
|
+
download(remotePath: string, localPath?: string): Promise<{
|
|
77
77
|
localPath: string;
|
|
78
78
|
fileCount: number;
|
|
79
79
|
}>;
|
|
@@ -87,8 +87,8 @@ export declare class PpdocsApiClient {
|
|
|
87
87
|
crossListFiles(target: string, dir?: string): Promise<FileInfo[]>;
|
|
88
88
|
/** 跨项目: 读取文件 */
|
|
89
89
|
crossReadFile(target: string, filePath: string): Promise<string>;
|
|
90
|
-
/** 跨项目:
|
|
91
|
-
|
|
90
|
+
/** 跨项目: 下载文件或目录 */
|
|
91
|
+
crossDownload(target: string, remotePath: string, localPath?: string): Promise<{
|
|
92
92
|
localPath: string;
|
|
93
93
|
fileCount: number;
|
|
94
94
|
}>;
|
|
@@ -137,13 +137,13 @@ export declare function crossGetDocsByStatus(target: string, statusList: string[
|
|
|
137
137
|
export declare function crossGetRules(target: string, ruleType: string): Promise<string[]>;
|
|
138
138
|
export declare function listFiles(dir?: string): Promise<FileInfo[]>;
|
|
139
139
|
export declare function readFile(filePath: string): Promise<string>;
|
|
140
|
-
export declare function
|
|
140
|
+
export declare function download(remotePath: string, localPath?: string): Promise<{
|
|
141
141
|
localPath: string;
|
|
142
142
|
fileCount: number;
|
|
143
143
|
}>;
|
|
144
144
|
export declare function crossListFiles(target: string, dir?: string): Promise<FileInfo[]>;
|
|
145
145
|
export declare function crossReadFile(target: string, filePath: string): Promise<string>;
|
|
146
|
-
export declare function
|
|
146
|
+
export declare function crossDownload(target: string, remotePath: string, localPath?: string): Promise<{
|
|
147
147
|
localPath: string;
|
|
148
148
|
fileCount: number;
|
|
149
149
|
}>;
|
|
@@ -14,8 +14,8 @@ const EXCLUDED_DIRS = new Set([
|
|
|
14
14
|
'node_modules', '.git', 'target', 'dist', 'build', '.next',
|
|
15
15
|
'__pycache__', '.venv', 'venv', '.idea', '.vs', '.vscode',
|
|
16
16
|
]);
|
|
17
|
-
/** 下载 zip
|
|
18
|
-
async function fetchAndExtractZip(url) {
|
|
17
|
+
/** 下载 zip 并解压到指定目录或 temp 目录 */
|
|
18
|
+
async function fetchAndExtractZip(url, localPath) {
|
|
19
19
|
const controller = new AbortController();
|
|
20
20
|
const timeout = setTimeout(() => controller.abort(), 60000);
|
|
21
21
|
try {
|
|
@@ -29,10 +29,10 @@ async function fetchAndExtractZip(url) {
|
|
|
29
29
|
const path = await import('path');
|
|
30
30
|
const fs = await import('fs');
|
|
31
31
|
const AdmZip = (await import('adm-zip')).default;
|
|
32
|
-
const
|
|
33
|
-
fs.mkdirSync(
|
|
32
|
+
const targetDir = localPath || path.join(os.tmpdir(), `ppdocs-files-${Date.now()}`);
|
|
33
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
34
34
|
const zip = new AdmZip(buffer);
|
|
35
|
-
zip.extractAllTo(
|
|
35
|
+
zip.extractAllTo(targetDir, true);
|
|
36
36
|
let fileCount = 0;
|
|
37
37
|
const countFiles = (dirPath) => {
|
|
38
38
|
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
@@ -42,8 +42,8 @@ async function fetchAndExtractZip(url) {
|
|
|
42
42
|
fileCount++;
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
-
countFiles(
|
|
46
|
-
return { localPath:
|
|
45
|
+
countFiles(targetDir);
|
|
46
|
+
return { localPath: targetDir, fileCount };
|
|
47
47
|
}
|
|
48
48
|
catch (err) {
|
|
49
49
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
@@ -393,9 +393,9 @@ export class PpdocsApiClient {
|
|
|
393
393
|
async readFile(filePath) {
|
|
394
394
|
return this.request(`/files/${cleanPath(filePath)}`);
|
|
395
395
|
}
|
|
396
|
-
/**
|
|
397
|
-
async
|
|
398
|
-
return fetchAndExtractZip(`${this.baseUrl}/files-download/${cleanPath(
|
|
396
|
+
/** 下载项目文件或目录 (zip) → 自动解压到指定目录或 temp 目录 */
|
|
397
|
+
async download(remotePath, localPath) {
|
|
398
|
+
return fetchAndExtractZip(`${this.baseUrl}/files-download/${cleanPath(remotePath)}`, localPath);
|
|
399
399
|
}
|
|
400
400
|
/** 上传本地目录到中心服务器 (打包 zip → POST) */
|
|
401
401
|
async uploadFiles(localDir, remoteDir) {
|
|
@@ -460,9 +460,9 @@ export class PpdocsApiClient {
|
|
|
460
460
|
async crossReadFile(target, filePath) {
|
|
461
461
|
return this.request(`/cross/${encodeURIComponent(target)}/files/${cleanPath(filePath)}`);
|
|
462
462
|
}
|
|
463
|
-
/** 跨项目:
|
|
464
|
-
async
|
|
465
|
-
return fetchAndExtractZip(`${this.baseUrl}/cross/${encodeURIComponent(target)}/files-download/${cleanPath(
|
|
463
|
+
/** 跨项目: 下载文件或目录 */
|
|
464
|
+
async crossDownload(target, remotePath, localPath) {
|
|
465
|
+
return fetchAndExtractZip(`${this.baseUrl}/cross/${encodeURIComponent(target)}/files-download/${cleanPath(remotePath)}`, localPath);
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
// ============ 模块级 API ============
|
|
@@ -566,8 +566,8 @@ export async function listFiles(dir) {
|
|
|
566
566
|
export async function readFile(filePath) {
|
|
567
567
|
return getClient().readFile(filePath);
|
|
568
568
|
}
|
|
569
|
-
export async function
|
|
570
|
-
return getClient().
|
|
569
|
+
export async function download(remotePath, localPath) {
|
|
570
|
+
return getClient().download(remotePath, localPath);
|
|
571
571
|
}
|
|
572
572
|
export async function crossListFiles(target, dir) {
|
|
573
573
|
return getClient().crossListFiles(target, dir);
|
|
@@ -575,8 +575,8 @@ export async function crossListFiles(target, dir) {
|
|
|
575
575
|
export async function crossReadFile(target, filePath) {
|
|
576
576
|
return getClient().crossReadFile(target, filePath);
|
|
577
577
|
}
|
|
578
|
-
export async function
|
|
579
|
-
return getClient().
|
|
578
|
+
export async function crossDownload(target, remotePath, localPath) {
|
|
579
|
+
return getClient().crossDownload(target, remotePath, localPath);
|
|
580
580
|
}
|
|
581
581
|
export async function uploadFiles(localDir, remoteDir) {
|
|
582
582
|
return getClient().uploadFiles(localDir, remoteDir);
|
package/dist/tools/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { wrap, formatDocMarkdown, formatTreeText, countTreeDocs, formatFileSize
|
|
|
5
5
|
export function registerTools(server, projectId, _user) {
|
|
6
6
|
// ===================== 跨项目访问 =====================
|
|
7
7
|
// 0. 列出所有可访问的项目
|
|
8
|
-
server.tool('kg_list_projects', '列出所有可访问的项目(
|
|
8
|
+
server.tool('kg_list_projects', '列出所有可访问的项目(返回name和id)。跨项目操作的第一步:先调用此工具获取项目ID,再作为其他工具的targetProject参数', {}, async () => {
|
|
9
9
|
try {
|
|
10
10
|
const projects = await storage.crossListProjects();
|
|
11
11
|
if (projects.length === 0) {
|
|
@@ -197,10 +197,10 @@ export function registerTools(server, projectId, _user) {
|
|
|
197
197
|
return wrap(doc ? '✅ 项目介绍已更新' : '更新失败');
|
|
198
198
|
});
|
|
199
199
|
// 3.6 获取项目规则
|
|
200
|
-
server.tool('kg_get_rules', '获取项目规则(可指定类型或获取全部)。支持跨项目只读访问', {
|
|
200
|
+
server.tool('kg_get_rules', '获取项目规则(可指定类型或获取全部)。支持跨项目只读访问(需先用kg_list_projects获取项目ID)', {
|
|
201
201
|
ruleType: z.string().optional()
|
|
202
202
|
.describe('规则类型(如 userStyles, codeStyle, reviewRules, testRules, unitTests, 或自定义类型)。不传则返回全部'),
|
|
203
|
-
targetProject: z.string().optional().describe('目标项目ID或名称(
|
|
203
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
204
204
|
}, async (args) => {
|
|
205
205
|
// 跨项目访问
|
|
206
206
|
if (args.targetProject) {
|
|
@@ -331,9 +331,9 @@ export function registerTools(server, projectId, _user) {
|
|
|
331
331
|
}
|
|
332
332
|
});
|
|
333
333
|
// 5. 读取单个文档详情
|
|
334
|
-
server.tool('kg_read_node', '读取文档完整内容(简介、正文、版本历史、修复记录)。支持跨项目只读访问', {
|
|
334
|
+
server.tool('kg_read_node', '读取文档完整内容(简介、正文、版本历史、修复记录)。支持跨项目只读访问(需先用kg_list_projects获取项目ID)', {
|
|
335
335
|
nodeId: z.string().describe('文档路径'),
|
|
336
|
-
targetProject: z.string().optional().describe('目标项目ID或名称(
|
|
336
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
337
337
|
}, async (args) => {
|
|
338
338
|
// 跨项目访问
|
|
339
339
|
if (args.targetProject) {
|
|
@@ -354,8 +354,8 @@ export function registerTools(server, projectId, _user) {
|
|
|
354
354
|
return wrap(lines.join('\n'));
|
|
355
355
|
});
|
|
356
356
|
// 10. 获取知识库树状图
|
|
357
|
-
server.tool('kg_get_tree', '获取知识库目录树。格式: 📄 文档名 # 简介。支持跨项目只读访问', {
|
|
358
|
-
targetProject: z.string().optional().describe('目标项目ID或名称(
|
|
357
|
+
server.tool('kg_get_tree', '获取知识库目录树。格式: 📄 文档名 # 简介。支持跨项目只读访问(需先用kg_list_projects获取项目ID)', {
|
|
358
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
359
359
|
}, async (args) => {
|
|
360
360
|
try {
|
|
361
361
|
// 跨项目访问
|
|
@@ -386,9 +386,9 @@ export function registerTools(server, projectId, _user) {
|
|
|
386
386
|
}
|
|
387
387
|
});
|
|
388
388
|
// 11. 按状态筛选文档
|
|
389
|
-
server.tool('kg_get_docs_by_status', '获取指定状态的文档列表。支持跨项目只读访问', {
|
|
389
|
+
server.tool('kg_get_docs_by_status', '获取指定状态的文档列表。支持跨项目只读访问(需先用kg_list_projects获取项目ID)', {
|
|
390
390
|
statusList: z.array(z.string()).describe('状态列表(如["未完成","待修复"])'),
|
|
391
|
-
targetProject: z.string().optional().describe('目标项目ID或名称(
|
|
391
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
392
392
|
}, async (args) => {
|
|
393
393
|
// 跨项目访问
|
|
394
394
|
if (args.targetProject) {
|
|
@@ -518,9 +518,9 @@ export function registerTools(server, projectId, _user) {
|
|
|
518
518
|
}
|
|
519
519
|
});
|
|
520
520
|
// 列出项目文件
|
|
521
|
-
server.tool('project_list_files', '
|
|
521
|
+
server.tool('project_list_files', '浏览项目源码目录结构(仅列出文件名和大小,不读内容)。目标项目需已上传文件或关联源码目录。跨项目时先用kg_list_projects获取项目ID。查看文件内容请用project_read_file', {
|
|
522
522
|
dir: z.string().optional().describe('子目录路径(如"src/components"),不填则列出根目录'),
|
|
523
|
-
targetProject: z.string().optional().describe('目标项目ID或名称(
|
|
523
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
524
524
|
}, async (args) => {
|
|
525
525
|
try {
|
|
526
526
|
const files = args.targetProject
|
|
@@ -543,9 +543,9 @@ export function registerTools(server, projectId, _user) {
|
|
|
543
543
|
}
|
|
544
544
|
});
|
|
545
545
|
// 读取项目文件
|
|
546
|
-
server.tool('project_read_file', '
|
|
546
|
+
server.tool('project_read_file', '读取单个源码文件的文本内容(限1MB)。适合查看具体代码实现。跨项目时先用kg_list_projects获取项目ID。浏览目录结构请用project_list_files', {
|
|
547
547
|
path: z.string().describe('文件路径(如"src/main.ts")'),
|
|
548
|
-
targetProject: z.string().optional().describe('目标项目ID或名称(
|
|
548
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
549
549
|
}, async (args) => {
|
|
550
550
|
try {
|
|
551
551
|
const content = args.targetProject
|
|
@@ -558,17 +558,18 @@ export function registerTools(server, projectId, _user) {
|
|
|
558
558
|
return wrap(`❌ ${String(e)}`);
|
|
559
559
|
}
|
|
560
560
|
});
|
|
561
|
-
//
|
|
562
|
-
server.tool('
|
|
563
|
-
|
|
564
|
-
|
|
561
|
+
// 下载项目文件或目录 (zip → 自动解压)
|
|
562
|
+
server.tool('project_download', '下载项目源码到本地(自动识别文件/目录,打包zip解压)。目标项目需已上传文件或关联源码目录。跨项目时先用kg_list_projects获取项目ID', {
|
|
563
|
+
remotePath: z.string().describe('远程路径,可以是目录(如"src")或单个文件(如"src/main.ts")'),
|
|
564
|
+
localPath: z.string().optional().describe('本地保存路径(不填则保存到系统临时目录,目录不存在会自动创建)'),
|
|
565
|
+
targetProject: z.string().optional().describe('目标项目ID或名称(不填=当前项目)。ID可从kg_list_projects获取')
|
|
565
566
|
}, async (args) => {
|
|
566
567
|
try {
|
|
567
568
|
const result = args.targetProject
|
|
568
|
-
? await storage.
|
|
569
|
-
: await storage.
|
|
569
|
+
? await storage.crossDownload(args.targetProject, args.remotePath, args.localPath)
|
|
570
|
+
: await storage.download(args.remotePath, args.localPath);
|
|
570
571
|
const prefix = args.targetProject ? `[跨项目: ${args.targetProject}]\n\n` : '';
|
|
571
|
-
return wrap(`${prefix}✅
|
|
572
|
+
return wrap(`${prefix}✅ 已下载并解压\n\n- 本地路径: ${result.localPath}\n- 文件数量: ${result.fileCount}`);
|
|
572
573
|
}
|
|
573
574
|
catch (e) {
|
|
574
575
|
return wrap(`❌ ${String(e)}`);
|