dingtalk-wiki 1.2.3 → 1.2.5

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.
Files changed (2) hide show
  1. package/index.js +85 -10
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -850,7 +850,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
850
850
  properties: {
851
851
  doc_key: {
852
852
  type: 'string',
853
- description: '文档 docKey。wiki nodes 返回的 nodeId 本质是 dentryUuid,可直接用于此处'
853
+ description: '文档标识。支持 wiki nodes 的 nodeId(dentryUuid)、docKey,或文档 URL。重要:create_wiki_doc 返回的 nodeId 不能用于内容读写,请使用 docKey 或 dentryUuid。若传入 create 的 nodeId,需额外提供 workspace_id 以自动查找'
854
+ },
855
+ workspace_id: {
856
+ type: 'string',
857
+ description: '知识库 ID(可选)。当 doc_key 来自 create_wiki_doc 返回的 nodeId 时,提供此参数可自动查找正确的 dentryUuid'
854
858
  },
855
859
  operator_id: {
856
860
  type: 'string',
@@ -868,12 +872,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
868
872
  properties: {
869
873
  doc_key: {
870
874
  type: 'string',
871
- description: '文档 docKey。wiki nodes 返回的 nodeId 本质是 dentryUuid,可直接用于此处'
875
+ description: '文档标识。支持 wiki nodes 的 nodeId(dentryUuid)、docKey,或文档 URL。重要:create_wiki_doc 返回的 nodeId 不能用于内容读写,请使用 docKey 或 dentryUuid。若传入 create 的 nodeId,需额外提供 workspace_id 以自动查找'
872
876
  },
873
877
  content: {
874
878
  type: 'string',
875
879
  description: '要写入的 Markdown 内容'
876
880
  },
881
+ workspace_id: {
882
+ type: 'string',
883
+ description: '知识库 ID(可选)。当 doc_key 来自 create_wiki_doc 返回的 nodeId 时,提供此参数可自动查找正确的 dentryUuid'
884
+ },
877
885
  operator_id: {
878
886
  type: 'string',
879
887
  description: '操作者 unionid(不传则使用默认用户)'
@@ -1144,6 +1152,33 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
1144
1152
  };
1145
1153
  });
1146
1154
 
1155
+ async function resolveDocKey(docKey, operatorId, dingtalkInstance) {
1156
+ const rawInput = docKey;
1157
+
1158
+ // 从 URL 中提取 ID
1159
+ const urlMatch = String(docKey).match(/\/i\/nodes\/([^\/\?#]+)/);
1160
+ docKey = urlMatch ? urlMatch[1] : docKey;
1161
+
1162
+ try {
1163
+ const nodeInfo = await dingtalkInstance.docRequest('GET', `/v2.0/wiki/nodes/${docKey}`, { operatorId });
1164
+ const node = nodeInfo.node || nodeInfo;
1165
+
1166
+ // wiki/nodes 的 nodeId = dentryUuid,直接可用
1167
+ // 但 node.document.docKey 可能是不同的值,优先使用
1168
+ if (node.document?.docKey) {
1169
+ return node.document.docKey;
1170
+ }
1171
+ // 部分响应直接把 docKey 放在顶层
1172
+ if (node.docKey) {
1173
+ return node.docKey;
1174
+ }
1175
+ } catch (e) {
1176
+ // 传入 create nodeId 时 wiki/nodes 会拒绝,静默 fallback
1177
+ }
1178
+
1179
+ return docKey;
1180
+ }
1181
+
1147
1182
  // 工具调用处理
1148
1183
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
1149
1184
  const { name, arguments: args } = request.params;
@@ -1297,11 +1332,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1297
1332
  '',
1298
1333
  `${typeIcon} ${name}`,
1299
1334
  `🗂️ 类型: ${doc_type}`,
1300
- `🆔 Node ID: ${doc.nodeId}`
1335
+ `🆔 Node ID: ${doc.nodeId}`,
1301
1336
  ];
1302
1337
 
1303
1338
  if (doc.docKey) {
1304
- lines.push(`🔑 DocKey: ${doc.docKey}`);
1339
+ lines.push(`🔑 DocKey(用于内容读写): ${doc.docKey}`);
1340
+ }
1341
+ if (doc.dentryUuid) {
1342
+ lines.push(`📄 dentryUuid(用于内容读写): ${doc.dentryUuid}`);
1305
1343
  }
1306
1344
  if (doc.url) {
1307
1345
  lines.push(`🔗 链接: ${doc.url}`);
@@ -1319,8 +1357,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1319
1357
  url: doc.url || '',
1320
1358
  type: doc_type,
1321
1359
  });
1322
- if (args.content && doc.nodeId) {
1323
- dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${doc.nodeId}/overwriteContent`, {
1360
+ const contentId = doc.docKey || doc.dentryUuid || doc.nodeId;
1361
+ if (args.content && contentId) {
1362
+ dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${contentId}/overwriteContent`, {
1363
+ operatorId: opId,
1324
1364
  data: { content: args.content, contentType: 'markdown' }
1325
1365
  }).catch(() => {});
1326
1366
  }
@@ -1557,11 +1597,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1557
1597
  }
1558
1598
 
1559
1599
  case 'get_wiki_doc_content': {
1560
- const { doc_key: docKey, operator_id } = args;
1600
+ const { doc_key: docKey, workspace_id: workspaceId, operator_id } = args;
1561
1601
  if (operator_id) {
1562
1602
  dingtalk.setOperatorId(operator_id);
1563
1603
  }
1564
- const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${docKey}/blocks`, { operatorId: operator_id || null });
1604
+
1605
+ let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
1606
+
1607
+ // 如果 resolve 后值没变(可能是 create 返回的 nodeId),尝试搜索 API 查找
1608
+ if (realDocKey === docKey && workspaceId) {
1609
+ try {
1610
+ const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
1611
+ operatorId: operator_id || null,
1612
+ extraParams: { workspaceId, keyword: '', maxResults: 50 }
1613
+ });
1614
+ const docs = searchRes.docs || [];
1615
+ const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
1616
+ if (matched?.nodeBO?.nodeId) {
1617
+ realDocKey = matched.nodeBO.nodeId;
1618
+ }
1619
+ } catch (e) { /* ignore */ }
1620
+ }
1621
+
1622
+ const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${realDocKey}/blocks`, { operatorId: operator_id || null });
1565
1623
  const blocks = result.result?.data || [];
1566
1624
  let output = '';
1567
1625
  blocks.forEach((block) => {
@@ -1580,11 +1638,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1580
1638
  }
1581
1639
 
1582
1640
  case 'update_wiki_doc_content': {
1583
- const { doc_key: docKey, content, operator_id } = args;
1641
+ const { doc_key: docKey, content, workspace_id: workspaceId, operator_id } = args;
1584
1642
  if (operator_id) {
1585
1643
  dingtalk.setOperatorId(operator_id);
1586
1644
  }
1587
- await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${docKey}/overwriteContent`, {
1645
+
1646
+ let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
1647
+
1648
+ if (realDocKey === docKey && workspaceId) {
1649
+ try {
1650
+ const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
1651
+ operatorId: operator_id || null,
1652
+ extraParams: { workspaceId, keyword: '', maxResults: 50 }
1653
+ });
1654
+ const docs = searchRes.docs || [];
1655
+ const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
1656
+ if (matched?.nodeBO?.nodeId) {
1657
+ realDocKey = matched.nodeBO.nodeId;
1658
+ }
1659
+ } catch (e) { /* ignore */ }
1660
+ }
1661
+
1662
+ await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${realDocKey}/overwriteContent`, {
1588
1663
  operatorId: operator_id || null,
1589
1664
  data: { content, contentType: 'markdown' }
1590
1665
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dingtalk-wiki",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "DingTalk Wiki / Docs read-write MCP server that fills the gap left by DingTalk official MCP.",
5
5
  "main": "index.js",
6
6
  "bin": {