@ppdocs/mcp 3.2.9 → 3.2.11
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/discussion.d.ts +2 -1
- package/dist/tools/discussion.js +6 -6
- package/dist/tools/index.js +3 -3
- package/dist/tools/projects.d.ts +2 -1
- package/dist/tools/projects.js +2 -2
- package/dist/tools/rules.d.ts +2 -1
- package/dist/tools/rules.js +2 -2
- package/dist/web/ui.js +18 -33
- package/package.json +1 -1
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* kg_discussion_reply, kg_discussion_close_and_archive, kg_discussion_delete
|
|
5
5
|
*/
|
|
6
6
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
-
|
|
7
|
+
import { type McpContext } from './shared.js';
|
|
8
|
+
export declare function registerDiscussionTools(server: McpServer, ctx: McpContext): void;
|
package/dist/tools/discussion.js
CHANGED
|
@@ -8,7 +8,7 @@ import { getClient } from '../storage/httpClient.js';
|
|
|
8
8
|
import { decodeObjectStrings } from '../utils.js';
|
|
9
9
|
import { wrap, safeTool } from './shared.js';
|
|
10
10
|
import { DiscussionManager } from '../storage/discussion.js';
|
|
11
|
-
export function registerDiscussionTools(server,
|
|
11
|
+
export function registerDiscussionTools(server, ctx) {
|
|
12
12
|
const client = () => getClient();
|
|
13
13
|
server.tool('kg_discuss', '💬 跨项目讨论 — 发起、回复、归档协同讨论。action: list(列出活跃讨论)|read(读取详情)|create(发起)|reply(回复)|close(结案归档)|delete(删除)', {
|
|
14
14
|
action: z.enum(['list', 'read', 'create', 'reply', 'close', 'delete'])
|
|
@@ -35,8 +35,8 @@ export function registerDiscussionTools(server, projectId) {
|
|
|
35
35
|
const active = DiscussionManager.listActive();
|
|
36
36
|
const cleanMsg = cleaned > 0 ? `\n⚠️ 已自动清理 ${cleaned} 条超过7天不活跃的讨论` : '';
|
|
37
37
|
if (active.length === 0)
|
|
38
|
-
return wrap(`当前无活跃的讨论 (本项目ID: ${projectId})${cleanMsg}`);
|
|
39
|
-
return wrap(`本项目ID: ${projectId}\n活跃讨论区 (${active.length} 个):${cleanMsg}\n\n` + JSON.stringify(active, null, 2));
|
|
38
|
+
return wrap(`当前无活跃的讨论 (本项目ID: ${ctx.projectId})${cleanMsg}`);
|
|
39
|
+
return wrap(`本项目ID: ${ctx.projectId}\n活跃讨论区 (${active.length} 个):${cleanMsg}\n\n` + JSON.stringify(active, null, 2));
|
|
40
40
|
}
|
|
41
41
|
case 'read': {
|
|
42
42
|
const readIds = decoded.ids || (decoded.id ? [decoded.id] : []);
|
|
@@ -57,15 +57,15 @@ export function registerDiscussionTools(server, projectId) {
|
|
|
57
57
|
const count = DiscussionManager.activeCount();
|
|
58
58
|
if (count >= 10)
|
|
59
59
|
return wrap('❌ 活跃讨论已达上限(10条)');
|
|
60
|
-
const id = DiscussionManager.create(decoded.title, projectId, decoded.participants, decoded.content);
|
|
61
|
-
return wrap(`✅ 讨论已发起,ID: ${id}\n发起方: ${projectId}\n参与方: ${decoded.participants.join(', ')}`);
|
|
60
|
+
const id = DiscussionManager.create(decoded.title, ctx.projectId, decoded.participants, decoded.content);
|
|
61
|
+
return wrap(`✅ 讨论已发起,ID: ${id}\n发起方: ${ctx.projectId}\n参与方: ${decoded.participants.join(', ')}`);
|
|
62
62
|
}
|
|
63
63
|
case 'reply': {
|
|
64
64
|
if (!decoded.id)
|
|
65
65
|
return wrap('❌ reply 需要 id');
|
|
66
66
|
if (!decoded.content)
|
|
67
67
|
return wrap('❌ reply 需要 content');
|
|
68
|
-
const success = DiscussionManager.reply(decoded.id, projectId, decoded.content, decoded.newSummary);
|
|
68
|
+
const success = DiscussionManager.reply(decoded.id, ctx.projectId, decoded.content, decoded.newSummary);
|
|
69
69
|
return wrap(success ? `✅ 回复成功 (ID: ${decoded.id})` : `❌ 回复失败,讨论不存在或已关闭`);
|
|
70
70
|
}
|
|
71
71
|
case 'close': {
|
package/dist/tools/index.js
CHANGED
|
@@ -27,12 +27,12 @@ export function registerTools(server, projectId, user, onProjectChange) {
|
|
|
27
27
|
registerStatusTool(server, ctx);
|
|
28
28
|
// 📚 知识 (kg_doc + kg_tree + kg_projects + kg_rules)
|
|
29
29
|
registerDocTools(server, ctx.projectId);
|
|
30
|
-
registerProjectTools(server, ctx
|
|
31
|
-
registerRuleTools(server, ctx
|
|
30
|
+
registerProjectTools(server, ctx);
|
|
31
|
+
registerRuleTools(server, ctx);
|
|
32
32
|
// 📝 工作流 (kg_task + kg_files + kg_discuss)
|
|
33
33
|
registerTaskTools(server, ctx.projectId, ctx.user);
|
|
34
34
|
registerFileTools(server);
|
|
35
|
-
registerDiscussionTools(server, ctx
|
|
35
|
+
registerDiscussionTools(server, ctx);
|
|
36
36
|
// 🔬 代码分析 (不变)
|
|
37
37
|
registerAnalyzerTools(server, ctx.projectId);
|
|
38
38
|
}
|
package/dist/tools/projects.d.ts
CHANGED
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* 删除: kg_create_project (桌面端操作)
|
|
5
5
|
*/
|
|
6
6
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
-
|
|
7
|
+
import { type McpContext } from './shared.js';
|
|
8
|
+
export declare function registerProjectTools(server: McpServer, ctx: McpContext): void;
|
package/dist/tools/projects.js
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { getClient } from '../storage/httpClient.js';
|
|
7
7
|
import { wrap, safeTool } from './shared.js';
|
|
8
|
-
export function registerProjectTools(server,
|
|
8
|
+
export function registerProjectTools(server, ctx) {
|
|
9
9
|
const client = () => getClient();
|
|
10
10
|
server.tool('kg_projects', '📋 列出所有可访问的项目(返回名称和ID)。跨项目操作的第一步: 获取项目ID后用于其他工具的 targetProject 参数', {}, async () => safeTool(async () => {
|
|
11
11
|
const projects = await client().crossListProjects();
|
|
12
12
|
if (projects.length === 0)
|
|
13
13
|
return wrap('暂无可访问的项目');
|
|
14
14
|
const lines = projects.map(p => {
|
|
15
|
-
const isCurrent = p.id === projectId ? ' ★当前' : '';
|
|
15
|
+
const isCurrent = p.id === ctx.projectId ? ' ★当前' : '';
|
|
16
16
|
return `- ${p.name} (${p.id})${isCurrent}`;
|
|
17
17
|
});
|
|
18
18
|
return wrap(`可访问的项目 (${projects.length} 个):\n\n${lines.join('\n')}`);
|
package/dist/tools/rules.d.ts
CHANGED
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* 删除: kg_get_global_rules_meta, kg_save_global_rules_meta (管理员操作)
|
|
5
5
|
*/
|
|
6
6
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
-
|
|
7
|
+
import { type McpContext } from './shared.js';
|
|
8
|
+
export declare function registerRuleTools(server: McpServer, ctx: McpContext): void;
|
package/dist/tools/rules.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
import { getClient } from '../storage/httpClient.js';
|
|
8
8
|
import { decodeObjectStrings, getRules, RULE_TYPE_LABELS } from '../utils.js';
|
|
9
9
|
import { wrap, safeTool, crossPrefix } from './shared.js';
|
|
10
|
-
export function registerRuleTools(server,
|
|
10
|
+
export function registerRuleTools(server, ctx) {
|
|
11
11
|
const client = () => getClient();
|
|
12
12
|
server.tool('kg_rules', '📏 项目规则管理 — 读取或保存代码风格、审查规则等。action: get(读取规则)|save(保存规则)|get_meta(读取触发配置)|save_meta(保存触发配置)', {
|
|
13
13
|
action: z.enum(['get', 'save', 'get_meta', 'save_meta'])
|
|
@@ -51,7 +51,7 @@ export function registerRuleTools(server, projectId) {
|
|
|
51
51
|
return wrap(`暂无项目规则(项目: ${decoded.targetProject})`);
|
|
52
52
|
return wrap(`${crossPrefix(decoded.targetProject)}${allRules.join('\n\n')}`);
|
|
53
53
|
}
|
|
54
|
-
const rules = await getRules(projectId, decoded.ruleType || undefined);
|
|
54
|
+
const rules = await getRules(ctx.projectId, decoded.ruleType || undefined);
|
|
55
55
|
if (!rules || rules.trim() === '') {
|
|
56
56
|
const meta = await client().getRulesMeta();
|
|
57
57
|
const typeName = decoded.ruleType ? (meta[decoded.ruleType]?.label || decoded.ruleType) : '项目';
|
package/dist/web/ui.js
CHANGED
|
@@ -74,9 +74,6 @@ label{font-size:12px;color:#64748b;font-weight:500;display:block;margin-bottom:4
|
|
|
74
74
|
.tab.active{color:#60a5fa;border-color:#60a5fa}
|
|
75
75
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
|
76
76
|
</style>
|
|
77
|
-
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
78
|
-
<meta http-equiv="Pragma" content="no-cache">
|
|
79
|
-
<meta http-equiv="Expires" content="0">
|
|
80
77
|
</head>
|
|
81
78
|
<body>
|
|
82
79
|
<div class="app" id="app"></div>
|
|
@@ -92,7 +89,7 @@ function go(page,data){Object.assign(S,data||{});S.page=page;_lastDetailRid=null
|
|
|
92
89
|
function toast(m,t){t=t||'ok';var e=document.getElementById('toast');e.textContent=m;e.className='toast '+t+' show';setTimeout(function(){e.className='toast'},3000)}
|
|
93
90
|
|
|
94
91
|
// ============ API ============
|
|
95
|
-
function api(path,opts){return fetch(path,opts).then(function(r){return r.json()})
|
|
92
|
+
function api(path,opts){return fetch(path,opts).then(function(r){return r.json()})}
|
|
96
93
|
|
|
97
94
|
// ============ Init ============
|
|
98
95
|
function init(){
|
|
@@ -111,7 +108,7 @@ function init(){
|
|
|
111
108
|
var entry=items[i].webkitGetAsEntry&&items[i].webkitGetAsEntry();
|
|
112
109
|
if(entry&&entry.isDirectory){
|
|
113
110
|
go('add',{addStep:1,addDir:entry.name});
|
|
114
|
-
toast('📂
|
|
111
|
+
toast('📂 请补全绝对路径,例如 D:/projects/'+entry.name);
|
|
115
112
|
setTimeout(function(){var inp=document.getElementById('iDir');if(inp){inp.focus();inp.setSelectionRange(0,0)}},150);
|
|
116
113
|
return;
|
|
117
114
|
}
|
|
@@ -191,19 +188,22 @@ function renderAdd(){
|
|
|
191
188
|
}
|
|
192
189
|
|
|
193
190
|
function renderAddStep1(){
|
|
191
|
+
var dirName=S.addDir?S.addDir.replace(/\\\\/g,'/').split('/').filter(Boolean).pop():'';
|
|
192
|
+
var namePreview=dirName?'<div style="font-size:12px;color:#60a5fa;margin:-8px 0 12px">📂 项目名: <strong>'+dirName+'</strong></div>':'';
|
|
194
193
|
return '<span class="back" onclick="cancelAuth();go(\\x27list\\x27)">← 返回项目列表</span>'+
|
|
195
|
-
'<div class="card no-hover"><div class="section"><h2>➕
|
|
196
|
-
'<p style="font-size:13px;color:#64748b;margin-bottom:12px"
|
|
197
|
-
'<div class="fg"><label>本地项目目录 (
|
|
194
|
+
'<div class="card no-hover"><div class="section"><h2>➕ 添加项目 — Step 1/2</h2>'+
|
|
195
|
+
'<p style="font-size:13px;color:#64748b;margin-bottom:12px">指定本地项目目录,以目录名作为项目名称</p>'+
|
|
196
|
+
'<div class="fg"><label>本地项目目录 (绝对路径)</label><input id="iDir" value="'+S.addDir+'" placeholder="例: D:/projects/my-app" oninput="S.addDir=this.value;_render()"></div>'+
|
|
197
|
+
namePreview+
|
|
198
198
|
'<div class="btn-row"><button class="btn btn-p" onclick="doAuthStart()">📡 发送授权请求</button></div>'+
|
|
199
199
|
'</div></div>';
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
function doAuthStart(){
|
|
203
|
-
var dir=
|
|
204
|
-
if(dir&&!/^[A-Za-z]:[\\\\\\/]
|
|
203
|
+
var dir=document.getElementById('iDir').value.trim();
|
|
204
|
+
if(dir&&!/^[A-Za-z]:[\\\\\\/]|^\\//.test(dir)){toast('路径需为绝对路径(D:\\xxx 或 /Users/xxx)或留空','err');return}
|
|
205
205
|
S.addDir=dir;
|
|
206
|
-
api('/api/auth/start',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({localDir:dir
|
|
206
|
+
api('/api/auth/start',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({localDir:dir})}).then(function(r){
|
|
207
207
|
if(!r.ok){toast(r.error||'授权请求失败','err');return}
|
|
208
208
|
S.authRequestId=r.requestId;S.authStartTime=Date.now();S.authStatus='pending';S.addStep=2;
|
|
209
209
|
render();startAuthPoll();
|
|
@@ -304,7 +304,7 @@ function renderOverview(p){
|
|
|
304
304
|
'<div class="stat"><span>同步: '+(p.connected?'🟢 运行中':'🔴 '+p.syncStatus)+' '+(p.lastSync?'('+p.lastSync+')':'')+'</span></div>'+
|
|
305
305
|
'</div></div>'+
|
|
306
306
|
'<div class="card no-hover"><div class="section"><h2>📚 知识图谱</h2>'+
|
|
307
|
-
'<div class="tree-box" id="treeBox"
|
|
307
|
+
'<div class="tree-box" id="treeBox">加载中...</div></div></div>';
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
function renderMcpTab(p){
|
|
@@ -361,22 +361,11 @@ function renderPromptsTab(p){
|
|
|
361
361
|
|
|
362
362
|
// ============ Data Loading ============
|
|
363
363
|
function loadTree(rid){
|
|
364
|
-
var box=document.getElementById('treeBox');
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
try{
|
|
370
|
-
var r=JSON.parse(xhr.responseText);
|
|
371
|
-
var txt=r.tree||'知识库为空';
|
|
372
|
-
var NL=String.fromCharCode(10);
|
|
373
|
-
if(r.docCount>0) txt='共 '+r.docCount+' 文档, '+r.dirCount+' 目录'+NL+NL+txt;
|
|
374
|
-
if(r.readOnly) txt='🔒 只读模式'+NL+NL+txt;
|
|
375
|
-
box.textContent=txt;
|
|
376
|
-
}catch(e){box.textContent='解析失败: '+e}
|
|
377
|
-
};
|
|
378
|
-
xhr.onerror=function(){box.textContent='请求失败'};
|
|
379
|
-
xhr.send();
|
|
364
|
+
var box=document.getElementById('treeBox');if(!box)return Promise.resolve();
|
|
365
|
+
return api('/api/bind/'+rid+'/tree').then(function(r){
|
|
366
|
+
var prefix=r.readOnly?'🔒 只读模式\\n\\n':'';
|
|
367
|
+
box.textContent=r.tree?(prefix+(r.docCount>0?'共 '+r.docCount+' 文档, '+r.dirCount+' 目录\\n\\n':'')+r.tree):'知识库为空';
|
|
368
|
+
}).catch(function(e){box.textContent='加载失败: '+e});
|
|
380
369
|
}
|
|
381
370
|
function loadMcpStatus(rid){return api('/api/bind/'+rid+'/mcp').then(function(r){S.mcpStatus=r.platforms||{}}).catch(function(){S.mcpStatus={}})}
|
|
382
371
|
function loadMcpAll(rid){return api('/api/bind/'+rid+'/mcp/all').then(function(r){S.mcpAll=r.servers||[]}).catch(function(){S.mcpAll=[]})}
|
|
@@ -469,11 +458,7 @@ render=function(){
|
|
|
469
458
|
var _rid=S.projects[S.detailIdx]?S.projects[S.detailIdx].remoteId:null;
|
|
470
459
|
if(S.page==='detail'&&_rid&&!_detailLoading&&_lastDetailRid!==_rid){
|
|
471
460
|
_detailLoading=true;_lastDetailRid=_rid;
|
|
472
|
-
loadMcpStatus(_rid)
|
|
473
|
-
setTimeout(function(){
|
|
474
|
-
_detailLoading=false;
|
|
475
|
-
loadTree(_rid);
|
|
476
|
-
},100);
|
|
461
|
+
Promise.all([loadTree(_rid),loadMcpStatus(_rid),loadMcpAll(_rid),loadPrompts(_rid)]).then(function(){_detailLoading=false;_render()}).catch(function(){_detailLoading=false});
|
|
477
462
|
}
|
|
478
463
|
};
|
|
479
464
|
setInterval(function(){
|