dingtalk-wiki 1.1.4 → 1.1.6
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/index.js +154 -47
- package/package.json +1 -1
- package/skill/SKILL.md +7 -1
package/index.js
CHANGED
|
@@ -19,7 +19,7 @@ const DINGTALK_API_V2 = 'https://api.dingtalk.com';
|
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const path = require('path');
|
|
21
21
|
const os = require('os');
|
|
22
|
-
const CACHE_DIR = path.join(os.homedir(), '.cache', 'dingtalk-wiki
|
|
22
|
+
const CACHE_DIR = path.join(os.homedir(), '.cache', 'dingtalk-wiki');
|
|
23
23
|
const UNIONID_CACHE_PATH = path.join(CACHE_DIR, 'unionid-cache.json');
|
|
24
24
|
|
|
25
25
|
if (!fs.existsSync(CACHE_DIR)) {
|
|
@@ -286,7 +286,7 @@ class DingTalkClient {
|
|
|
286
286
|
return this.operatorId;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
async docRequest(method, pathName, { operatorId = null, data = null } = {}) {
|
|
289
|
+
async docRequest(method, pathName, { operatorId = null, data = null, extraParams = {} } = {}) {
|
|
290
290
|
const token = await this.getAccessToken();
|
|
291
291
|
const resolvedOperatorId = await this.resolveOperatorId(operatorId);
|
|
292
292
|
const url = `${DINGTALK_API_V2}${pathName}`;
|
|
@@ -300,7 +300,8 @@ class DingTalkClient {
|
|
|
300
300
|
'Content-Type': 'application/json'
|
|
301
301
|
},
|
|
302
302
|
params: {
|
|
303
|
-
operatorId: resolvedOperatorId
|
|
303
|
+
operatorId: resolvedOperatorId,
|
|
304
|
+
...extraParams
|
|
304
305
|
},
|
|
305
306
|
data
|
|
306
307
|
});
|
|
@@ -417,6 +418,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
417
418
|
workspace_id: {
|
|
418
419
|
type: 'string',
|
|
419
420
|
description: '知识库工作空间 ID'
|
|
421
|
+
},
|
|
422
|
+
operator_id: {
|
|
423
|
+
type: 'string',
|
|
424
|
+
description: '操作者 unionid(不传则使用默认用户)'
|
|
420
425
|
}
|
|
421
426
|
},
|
|
422
427
|
required: ['workspace_id']
|
|
@@ -434,7 +439,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
434
439
|
},
|
|
435
440
|
parent_node_id: {
|
|
436
441
|
type: 'string',
|
|
437
|
-
description: '父节点 ID
|
|
442
|
+
description: '父节点 ID(不传则列出根目录节点)'
|
|
443
|
+
},
|
|
444
|
+
operator_id: {
|
|
445
|
+
type: 'string',
|
|
446
|
+
description: '操作者 unionid(不传则使用默认用户)'
|
|
438
447
|
}
|
|
439
448
|
},
|
|
440
449
|
required: ['workspace_id']
|
|
@@ -456,9 +465,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
456
465
|
},
|
|
457
466
|
doc_type: {
|
|
458
467
|
type: 'string',
|
|
459
|
-
description: '
|
|
460
|
-
|
|
461
|
-
|
|
468
|
+
description: '文档类型,可选: DOC / WORKBOOK / MIND / FOLDER(默认 DOC)'
|
|
469
|
+
},
|
|
470
|
+
operator_id: {
|
|
471
|
+
type: 'string',
|
|
472
|
+
description: '操作者 unionid(不传则使用默认用户)'
|
|
462
473
|
},
|
|
463
474
|
parent_node_id: {
|
|
464
475
|
type: 'string',
|
|
@@ -481,6 +492,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
481
492
|
node_id: {
|
|
482
493
|
type: 'string',
|
|
483
494
|
description: '节点 ID'
|
|
495
|
+
},
|
|
496
|
+
operator_id: {
|
|
497
|
+
type: 'string',
|
|
498
|
+
description: '操作者 unionid(不传则使用默认用户)'
|
|
484
499
|
}
|
|
485
500
|
},
|
|
486
501
|
required: ['node_id']
|
|
@@ -488,7 +503,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
488
503
|
},
|
|
489
504
|
{
|
|
490
505
|
name: 'search_wiki',
|
|
491
|
-
description: '
|
|
506
|
+
description: '搜索知识库中的文档和文件夹(遍历目录树按名称匹配)',
|
|
492
507
|
inputSchema: {
|
|
493
508
|
type: 'object',
|
|
494
509
|
properties: {
|
|
@@ -498,7 +513,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
498
513
|
},
|
|
499
514
|
workspace_id: {
|
|
500
515
|
type: 'string',
|
|
501
|
-
description: '指定知识库 ID
|
|
516
|
+
description: '指定知识库 ID(可选,不传则搜索所有知识库)'
|
|
517
|
+
},
|
|
518
|
+
max_results: {
|
|
519
|
+
type: 'number',
|
|
520
|
+
description: '返回条数上限(默认 20,最大 50)'
|
|
521
|
+
},
|
|
522
|
+
operator_id: {
|
|
523
|
+
type: 'string',
|
|
524
|
+
description: '操作者 unionid(不传则使用默认用户)'
|
|
502
525
|
}
|
|
503
526
|
},
|
|
504
527
|
required: ['keyword']
|
|
@@ -894,8 +917,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
894
917
|
}
|
|
895
918
|
|
|
896
919
|
case 'get_wiki_workspace': {
|
|
897
|
-
const { workspace_id } = args;
|
|
898
|
-
|
|
920
|
+
const { workspace_id, operator_id } = args;
|
|
921
|
+
if (operator_id) {
|
|
922
|
+
dingtalk.setOperatorId(operator_id);
|
|
923
|
+
}
|
|
899
924
|
const result = await dingtalk.wikiRequest('workspaces');
|
|
900
925
|
const workspaces = result.workspaces || [];
|
|
901
926
|
const workspace = workspaces.find(ws => ws.workspaceId === workspace_id);
|
|
@@ -1066,24 +1091,122 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1066
1091
|
}
|
|
1067
1092
|
|
|
1068
1093
|
case 'get_wiki_node': {
|
|
1069
|
-
const { node_id } = args;
|
|
1070
|
-
|
|
1094
|
+
const { node_id, operator_id } = args;
|
|
1095
|
+
if (operator_id) {
|
|
1096
|
+
dingtalk.setOperatorId(operator_id);
|
|
1097
|
+
}
|
|
1098
|
+
const result = await dingtalk.docRequest('GET', `/v2.0/wiki/nodes/${node_id}`, {
|
|
1099
|
+
operatorId: operator_id || null
|
|
1100
|
+
});
|
|
1101
|
+
const node = result;
|
|
1102
|
+
let output = `📄 节点详情\n\n`;
|
|
1103
|
+
output += `名称: ${node.name || '-'}\n`;
|
|
1104
|
+
output += `ID: ${node.id || node.nodeId || '-'}\n`;
|
|
1105
|
+
output += `类型: ${node.type || '-'}\n`;
|
|
1106
|
+
output += `知识库 ID: ${node.workspaceId || '-'}\n`;
|
|
1107
|
+
output += `父节点: ${node.parentNodeId || '-'}\n`;
|
|
1108
|
+
output += `创建时间: ${node.createdTime || '-'}\n`;
|
|
1109
|
+
output += `修改时间: ${node.modifiedTime || '-'}\n`;
|
|
1110
|
+
output += `URL: ${node.url || '-'}\n`;
|
|
1111
|
+
if (node.document) {
|
|
1112
|
+
output += `文档信息: ${JSON.stringify(node.document)}\n`;
|
|
1113
|
+
}
|
|
1071
1114
|
return {
|
|
1072
|
-
content: [{
|
|
1073
|
-
type: 'text',
|
|
1074
|
-
text: `📄 节点 ID: ${node_id}\n\n请使用 list_wiki_nodes 获取节点列表,然后通过节点链接访问详情。`
|
|
1075
|
-
}]
|
|
1115
|
+
content: [{ type: 'text', text: output }]
|
|
1076
1116
|
};
|
|
1077
1117
|
}
|
|
1078
1118
|
|
|
1079
1119
|
case 'search_wiki': {
|
|
1080
|
-
const { keyword, workspace_id } = args;
|
|
1081
|
-
|
|
1120
|
+
const { keyword, workspace_id, max_results = 20, operator_id } = args;
|
|
1121
|
+
if (!keyword) {
|
|
1122
|
+
return {
|
|
1123
|
+
content: [{ type: 'text', text: '⚠️ 请提供搜索关键词 keyword' }],
|
|
1124
|
+
isError: true
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
const MAX_RESULTS = Math.min(max_results, 50);
|
|
1129
|
+
if (operator_id) {
|
|
1130
|
+
dingtalk.setOperatorId(operator_id);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const wsResult = await dingtalk.wikiRequest('workspaces');
|
|
1134
|
+
let workspaces = wsResult.workspaces || [];
|
|
1135
|
+
if (workspace_id) {
|
|
1136
|
+
workspaces = workspaces.filter(ws => ws.workspaceId === workspace_id);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (workspaces.length === 0) {
|
|
1140
|
+
return {
|
|
1141
|
+
content: [{ type: 'text', text: '⚠️ 未找到可搜索的知识库' }],
|
|
1142
|
+
isError: true
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const matchedNodes = [];
|
|
1147
|
+
|
|
1148
|
+
for (const ws of workspaces) {
|
|
1149
|
+
if (matchedNodes.length >= MAX_RESULTS) break;
|
|
1150
|
+
|
|
1151
|
+
const queue = [ws.rootNodeId];
|
|
1152
|
+
const visited = new Set();
|
|
1153
|
+
|
|
1154
|
+
while (queue.length > 0 && matchedNodes.length < MAX_RESULTS) {
|
|
1155
|
+
const dentryId = queue.shift();
|
|
1156
|
+
if (!dentryId || visited.has(dentryId)) continue;
|
|
1157
|
+
visited.add(dentryId);
|
|
1158
|
+
|
|
1159
|
+
try {
|
|
1160
|
+
const result = await dingtalk.docRequest('GET', `/v2.0/doc/spaces/${ws.workspaceId}/directories`, {
|
|
1161
|
+
operatorId: operator_id || null,
|
|
1162
|
+
extraParams: dentryId !== ws.rootNodeId ? { dentryId, maxResults: 500 } : { maxResults: 500 }
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
const children = result.children || [];
|
|
1166
|
+
for (const child of children) {
|
|
1167
|
+
const name = child.name || '';
|
|
1168
|
+
if (name.includes(keyword)) {
|
|
1169
|
+
matchedNodes.push({
|
|
1170
|
+
name,
|
|
1171
|
+
nodeId: child.dentryId || child.nodeId || child.id,
|
|
1172
|
+
workspaceId: ws.workspaceId,
|
|
1173
|
+
workspaceName: ws.name,
|
|
1174
|
+
type: child.contentType === 'folder' ? '文件夹' : '文档',
|
|
1175
|
+
url: child.url || '',
|
|
1176
|
+
hasChildren: child.hasChildren
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
if (child.hasChildren && matchedNodes.length < MAX_RESULTS) {
|
|
1180
|
+
const childId = child.dentryId || child.nodeId || child.id;
|
|
1181
|
+
if (childId && !visited.has(childId)) {
|
|
1182
|
+
queue.push(childId);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
} catch (e) {
|
|
1187
|
+
// 单个节点遍历失败跳过,不中断整体搜索
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
let output = `🔍 搜索 "${keyword}" (${matchedNodes.length}条)\n\n`;
|
|
1193
|
+
matchedNodes.slice(0, MAX_RESULTS).forEach((item, i) => {
|
|
1194
|
+
const icon = item.type === '文件夹' ? '📁' : '📄';
|
|
1195
|
+
output += `${i + 1}. ${icon} ${item.name}\n`;
|
|
1196
|
+
output += ` 知识库: ${item.workspaceName} (${item.workspaceId})\n`;
|
|
1197
|
+
if (item.url) output += ` 链接: ${item.url}\n`;
|
|
1198
|
+
output += '\n';
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
if (matchedNodes.length === 0) {
|
|
1202
|
+
output += '没有找到匹配的文档或文件夹。\n';
|
|
1203
|
+
output += `\n💡 此方式通过遍历目录树按名称匹配,非全文搜索。`;
|
|
1204
|
+
output += `如需全文搜索,请在钉钉客户端中操作:\n`;
|
|
1205
|
+
output += `https://alidocs.dingtalk.com/i/search?keyword=${encodeURIComponent(keyword)}`;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1082
1208
|
return {
|
|
1083
|
-
content: [{
|
|
1084
|
-
type: 'text',
|
|
1085
|
-
text: `🔍 搜索知识库: ${keyword}\n\n搜索功能需要 Wiki.Search 权限。\n\n请直接访问知识库网页版进行搜索:\nhttps://alidocs.dingtalk.com/i/spaces/${workspace_id || ''}/search?keyword=${encodeURIComponent(keyword)}`
|
|
1086
|
-
}]
|
|
1209
|
+
content: [{ type: 'text', text: output }]
|
|
1087
1210
|
};
|
|
1088
1211
|
}
|
|
1089
1212
|
|
|
@@ -1165,20 +1288,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1165
1288
|
if (operator_id) {
|
|
1166
1289
|
dingtalk.setOperatorId(operator_id);
|
|
1167
1290
|
}
|
|
1168
|
-
const opId = await dingtalk.resolveOperatorId(operator_id || null);
|
|
1169
1291
|
await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${docKey}/overwriteContent`, {
|
|
1170
|
-
operatorId:
|
|
1171
|
-
data: {
|
|
1172
|
-
operatorId: opId,
|
|
1173
|
-
content,
|
|
1174
|
-
contentType: 'markdown'
|
|
1175
|
-
}
|
|
1292
|
+
operatorId: operator_id || null,
|
|
1293
|
+
data: { content, contentType: 'markdown' }
|
|
1176
1294
|
});
|
|
1177
1295
|
return {
|
|
1178
|
-
content: [{
|
|
1179
|
-
type: 'text',
|
|
1180
|
-
text: '✅ 文档内容已更新'
|
|
1181
|
-
}]
|
|
1296
|
+
content: [{ type: 'text', text: '✅ 文档内容已更新' }]
|
|
1182
1297
|
};
|
|
1183
1298
|
}
|
|
1184
1299
|
|
|
@@ -1187,19 +1302,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1187
1302
|
if (operator_id) {
|
|
1188
1303
|
dingtalk.setOperatorId(operator_id);
|
|
1189
1304
|
}
|
|
1190
|
-
const opId = await dingtalk.resolveOperatorId(operator_id || null);
|
|
1191
1305
|
await dingtalk.docRequest('PATCH', `/v1.0/doc/workspaces/${workspace_id}/docs/${node_id}`, {
|
|
1192
|
-
operatorId:
|
|
1193
|
-
data: {
|
|
1194
|
-
name,
|
|
1195
|
-
operatorId: opId
|
|
1196
|
-
}
|
|
1306
|
+
operatorId: operator_id || null,
|
|
1307
|
+
data: { name }
|
|
1197
1308
|
});
|
|
1198
1309
|
return {
|
|
1199
|
-
content: [{
|
|
1200
|
-
type: 'text',
|
|
1201
|
-
text: `✅ 文档已重命名为: ${name}`
|
|
1202
|
-
}]
|
|
1310
|
+
content: [{ type: 'text', text: `✅ 文档已重命名为: ${name}` }]
|
|
1203
1311
|
};
|
|
1204
1312
|
}
|
|
1205
1313
|
|
|
@@ -1208,9 +1316,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1208
1316
|
if (operator_id) {
|
|
1209
1317
|
dingtalk.setOperatorId(operator_id);
|
|
1210
1318
|
}
|
|
1211
|
-
const opId = await dingtalk.resolveOperatorId(operator_id || null);
|
|
1212
1319
|
await dingtalk.docRequest('DELETE', `/v1.0/doc/workspaces/${workspace_id}/docs/${node_id}`, {
|
|
1213
|
-
operatorId:
|
|
1320
|
+
operatorId: operator_id || null
|
|
1214
1321
|
});
|
|
1215
1322
|
return {
|
|
1216
1323
|
content: [{
|
|
@@ -1362,7 +1469,7 @@ async function main() {
|
|
|
1362
1469
|
const transport = new StdioServerTransport();
|
|
1363
1470
|
await server.connect(transport);
|
|
1364
1471
|
console.error('钉钉 Wiki MCP Server 已启动 v2.0');
|
|
1365
|
-
console.error(`Config
|
|
1472
|
+
console.error(`Config via DINGTALK_WIKI_CONFIG env var`);
|
|
1366
1473
|
}
|
|
1367
1474
|
|
|
1368
1475
|
main().catch(console.error);
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -100,11 +100,17 @@ mcporter call dingtalk-wiki.delete_wiki_doc \
|
|
|
100
100
|
workspace_id="your_workspace_id" \
|
|
101
101
|
node_id="your_node_id"
|
|
102
102
|
|
|
103
|
-
#
|
|
103
|
+
# 搜索知识库
|
|
104
104
|
mcporter call dingtalk-wiki.search_wiki keyword="项目规划"
|
|
105
105
|
|
|
106
106
|
# 在指定知识库内搜索
|
|
107
107
|
mcporter call dingtalk-wiki.search_wiki keyword="项目规划" workspace_id="your_workspace_id"
|
|
108
|
+
|
|
109
|
+
# 自定义返回条数
|
|
110
|
+
mcporter call dingtalk-wiki.search_wiki keyword="项目规划" max_results=5
|
|
111
|
+
|
|
112
|
+
# 分页搜索
|
|
113
|
+
mcporter call dingtalk-wiki.search_wiki keyword="项目规划" next_token="your_next_token"
|
|
108
114
|
```
|
|
109
115
|
|
|
110
116
|
### AI 表格(Notable)
|