@ppdocs/mcp 3.2.7 → 3.2.9

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.
@@ -8,6 +8,7 @@
8
8
  * 📝 工作流: kg_task, kg_files, kg_discuss (3个)
9
9
  * 🔬 代码分析: code_scan, code_query, code_impact, code_context (4个)
10
10
  */
11
+ import { createContext } from './shared.js';
11
12
  import { registerInitTool } from './init.js';
12
13
  import { registerStatusTool } from './kg_status.js';
13
14
  import { registerDocTools } from './docs.js';
@@ -18,18 +19,20 @@ import { registerFileTools } from './files.js';
18
19
  import { registerDiscussionTools } from './discussion.js';
19
20
  import { registerAnalyzerTools } from './analyzer.js';
20
21
  export function registerTools(server, projectId, user, onProjectChange) {
21
- // 🔗 初始化 (kg_init 运行时项目切换)
22
- registerInitTool(server, projectId, onProjectChange || (() => { }));
22
+ // 创建共享可变上下文所有工具捕获此对象引用
23
+ const ctx = createContext(projectId, user);
24
+ // 🔗 初始化 (kg_init — 运行时项目切换,更新 ctx)
25
+ registerInitTool(server, ctx, onProjectChange || (() => { }));
23
26
  // 📊 导航 (kg_status + kg_tree在docs中)
24
- registerStatusTool(server, projectId);
27
+ registerStatusTool(server, ctx);
25
28
  // 📚 知识 (kg_doc + kg_tree + kg_projects + kg_rules)
26
- registerDocTools(server, projectId);
27
- registerProjectTools(server, projectId);
28
- registerRuleTools(server, projectId);
29
+ registerDocTools(server, ctx.projectId);
30
+ registerProjectTools(server, ctx.projectId);
31
+ registerRuleTools(server, ctx.projectId);
29
32
  // 📝 工作流 (kg_task + kg_files + kg_discuss)
30
- registerTaskTools(server, projectId, user);
33
+ registerTaskTools(server, ctx.projectId, ctx.user);
31
34
  registerFileTools(server);
32
- registerDiscussionTools(server, projectId);
35
+ registerDiscussionTools(server, ctx.projectId);
33
36
  // 🔬 代码分析 (不变)
34
- registerAnalyzerTools(server, projectId);
37
+ registerAnalyzerTools(server, ctx.projectId);
35
38
  }
@@ -5,9 +5,8 @@
5
5
  * - 智能体调用 kg_init 传入 projectPath
6
6
  * - MCP 从该路径读取 .ppdocs 配置
7
7
  * - 重新初始化 HTTP Client 指向正确项目
8
- * - 后续所有工具调用自动使用新的项目上下文
8
+ * - 更新共享 McpContext,所有工具自动获取新 projectId
9
9
  */
10
10
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
- /** 获取当前已初始化的 projectId */
12
- export declare function getActiveProjectId(): string | null;
13
- export declare function registerInitTool(server: McpServer, defaultProjectId: string, onProjectChange: (newProjectId: string, newApiUrl: string) => void): void;
11
+ import { type McpContext } from './shared.js';
12
+ export declare function registerInitTool(server: McpServer, ctx: McpContext, onProjectChange: (newProjectId: string, newApiUrl: string) => void): void;
@@ -5,20 +5,15 @@
5
5
  * - 智能体调用 kg_init 传入 projectPath
6
6
  * - MCP 从该路径读取 .ppdocs 配置
7
7
  * - 重新初始化 HTTP Client 指向正确项目
8
- * - 后续所有工具调用自动使用新的项目上下文
8
+ * - 更新共享 McpContext,所有工具自动获取新 projectId
9
9
  */
10
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import { z } from 'zod';
13
13
  import { initClient } from '../storage/httpClient.js';
14
14
  import { wrap, safeTool } from './shared.js';
15
- // 当前会话的项目上下文 (模块级单例)
16
- let currentProjectId = null;
15
+ // 当前会话的 API URL (用于幂等检查)
17
16
  let currentApiUrl = null;
18
- /** 获取当前已初始化的 projectId */
19
- export function getActiveProjectId() {
20
- return currentProjectId;
21
- }
22
17
  /** 尝试从指定目录读取 .ppdocs 配置 */
23
18
  function readPpdocsFrom(dir) {
24
19
  const configPath = path.join(dir, '.ppdocs');
@@ -38,8 +33,8 @@ function readPpdocsFrom(dir) {
38
33
  return null;
39
34
  }
40
35
  }
41
- export function registerInitTool(server, defaultProjectId, onProjectChange) {
42
- server.tool('kg_init', '🔗 项目上下文初始化 — 读取指定目录的 .ppdocs 配置,切换 MCP 连接到对应项目。首次对话建议调用。不传 projectPath 则使用当前工作目录', {
36
+ export function registerInitTool(server, ctx, onProjectChange) {
37
+ server.tool('kg_init', '🔗 项目上下文初始化 — 读取指定目录的 .ppdocs 配置,切换 MCP 连接到对应项目。首次对话必须调用。不传 projectPath 则使用当前工作目录', {
43
38
  projectPath: z.string().optional().describe('项目根目录的绝对路径(含 .ppdocs 文件),不传则使用 cwd'),
44
39
  }, async ({ projectPath }) => safeTool(async () => {
45
40
  const targetDir = projectPath || process.cwd();
@@ -52,7 +47,7 @@ export function registerInitTool(server, defaultProjectId, onProjectChange) {
52
47
  `或通过 WebUI 绑定项目:\n` +
53
48
  ` npx @ppdocs/mcp webui`);
54
49
  }
55
- // 2. 检查是否需要切换
50
+ // 2. 幂等检查
56
51
  if (currentApiUrl === config.apiUrl) {
57
52
  return wrap(`✅ 项目上下文已就绪 (无需切换)\n\n` +
58
53
  `📂 项目: ${config.projectId}\n` +
@@ -61,9 +56,11 @@ export function registerInitTool(server, defaultProjectId, onProjectChange) {
61
56
  }
62
57
  // 3. 重新初始化 HTTP Client
63
58
  initClient(config.apiUrl);
64
- currentProjectId = config.projectId;
65
59
  currentApiUrl = config.apiUrl;
66
- // 4. 通知主进程更新工具注册的 projectId
60
+ // 4. 更新共享上下文 — 所有工具立刻获得新 projectId
61
+ ctx.projectId = config.projectId;
62
+ ctx.user = config.user;
63
+ // 5. 通知主进程
67
64
  onProjectChange(config.projectId, config.apiUrl);
68
65
  return wrap(`✅ 项目上下文已初始化\n\n` +
69
66
  `📂 项目: ${config.projectId}\n` +
@@ -2,4 +2,5 @@
2
2
  * 📊 kg_status — 项目速览仪表盘 (新增)
3
3
  */
4
4
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
- export declare function registerStatusTool(server: McpServer, projectId: string): void;
5
+ import { type McpContext } from './shared.js';
6
+ export declare function registerStatusTool(server: McpServer, ctx: McpContext): void;
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { getClient } from '../storage/httpClient.js';
5
5
  import { wrap, safeTool } from './shared.js';
6
- export function registerStatusTool(server, projectId) {
6
+ export function registerStatusTool(server, ctx) {
7
7
  const client = () => getClient();
8
8
  server.tool('kg_status', '📊 项目速览仪表盘 — 一键了解项目健康。返回: 文档总数、目录数、活跃任务数、最近变更。每次对话开始建议首先调用', {}, async () => safeTool(async () => {
9
9
  const [docs, activeTasks] = await Promise.all([
@@ -20,7 +20,7 @@ export function registerStatusTool(server, projectId) {
20
20
  statusMap.set(s, (statusMap.get(s) || 0) + 1);
21
21
  }
22
22
  const lines = [
23
- `📊 项目速览 [${projectId}]`,
23
+ `📊 项目速览 [${ctx.projectId}]`,
24
24
  ``,
25
25
  `📄 文档: ${docCount} 个 | 📁 目录: ${dirCount} 个`,
26
26
  `📝 活跃任务: ${activeTasks.length} 个`,
@@ -3,6 +3,13 @@
3
3
  * 合并原 helpers.ts + 新增 safeTool / withCross 模式
4
4
  */
5
5
  import type { DocData } from '../storage/types.js';
6
+ /** 运行时可变上下文 — 所有工具通过引用获取最新值 */
7
+ export interface McpContext {
8
+ projectId: string;
9
+ user: string;
10
+ }
11
+ /** 创建可变上下文对象 (工具闭包捕获此对象的引用,kg_init 更新其属性) */
12
+ export declare function createContext(projectId: string, user: string): McpContext;
6
13
  export declare function wrap(text: string): {
7
14
  content: {
8
15
  type: "text";
@@ -2,6 +2,10 @@
2
2
  * MCP 工具共享模块
3
3
  * 合并原 helpers.ts + 新增 safeTool / withCross 模式
4
4
  */
5
+ /** 创建可变上下文对象 (工具闭包捕获此对象的引用,kg_init 更新其属性) */
6
+ export function createContext(projectId, user) {
7
+ return { projectId, user };
8
+ }
5
9
  // ==================== MCP 返回包装 ====================
6
10
  export function wrap(text) {
7
11
  return { content: [{ type: 'text', text }] };
package/dist/web/ui.js CHANGED
@@ -111,7 +111,8 @@ function init(){
111
111
  var entry=items[i].webkitGetAsEntry&&items[i].webkitGetAsEntry();
112
112
  if(entry&&entry.isDirectory){
113
113
  go('add',{addStep:1,addDir:entry.name});
114
- toast('📂 检测到 '+entry.name+',可直接发送授权或补全路径');
114
+ toast('📂 检测到 '+entry.name+',请补全绝对路径后发送授权');
115
+ setTimeout(function(){var inp=document.getElementById('iDir');if(inp){inp.focus();inp.setSelectionRange(0,0)}},150);
115
116
  return;
116
117
  }
117
118
  }
@@ -193,14 +194,14 @@ function renderAddStep1(){
193
194
  return '<span class="back" onclick="cancelAuth();go(\\x27list\\x27)">← 返回项目列表</span>'+
194
195
  '<div class="card no-hover"><div class="section"><h2>➕ 添加项目</h2>'+
195
196
  '<p style="font-size:13px;color:#64748b;margin-bottom:12px">点击发送授权,在主机端 PPDocs 应用中选择项目并批准</p>'+
196
- '<div class="fg"><label>本地项目目录 (可选,MCP安装时需要)</label><input id="iDir" value="'+S.addDir+'" placeholder="(可选) D:/projects/my-app" oninput="S.addDir=this.value"></div>'+
197
+ '<div class="fg"><label>本地项目目录 (可选,MCP安装时需要)</label><input id="iDir" value="'+S.addDir+'" placeholder="Win: D:\\\\projects\\\\app Mac: /Users/xxx/app" oninput="S.addDir=this.value"></div>'+
197
198
  '<div class="btn-row"><button class="btn btn-p" onclick="doAuthStart()">📡 发送授权请求</button></div>'+
198
199
  '</div></div>';
199
200
  }
200
201
 
201
202
  function doAuthStart(){
202
203
  var dir=(document.getElementById('iDir')?document.getElementById('iDir').value:'').trim();
203
- if(dir&&!/^[A-Za-z]:[\\\\\\/]|^\\//.test(dir)){toast('路径需为绝对路径或留空','err');return}
204
+ if(dir&&!/^[A-Za-z]:[\\\\\\/]|^\\/|^~\//.test(dir)){toast('路径需为绝对路径(D:\\xxx 或 /Users/xxx)或留空','err');return}
204
205
  S.addDir=dir;
205
206
  api('/api/auth/start',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({localDir:dir||''})}).then(function(r){
206
207
  if(!r.ok){toast(r.error||'授权请求失败','err');return}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppdocs/mcp",
3
- "version": "3.2.7",
3
+ "version": "3.2.9",
4
4
  "description": "ppdocs MCP Server - Knowledge Graph for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",