dingtalk-wiki 1.2.17 → 1.2.18

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 +24 -14
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -871,17 +871,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
871
871
  },
872
872
  {
873
873
  name: 'get_wiki_doc_content',
874
- description: '读取文档正文内容(返回 Block 结构)。注意:仅对 create_wiki_doc 创建的文档有效;知识库中已有文档(非通过此工具创建的)不受支持——钉钉未提供公开 REST API 读取已有知识库文档内容。若需读取已有文档内容,可使用 DingTalk 官方 MCP 服务器的 get_document_content 工具。',
874
+ description: '读取文档内容。对 .adoc 在线文档使用 blocks API;对上传的 .md 等文件使用存储下载 API(需 Storage.DownloadInfo.Read 权限)。',
875
875
  inputSchema: {
876
876
  type: 'object',
877
877
  properties: {
878
878
  doc_key: {
879
879
  type: 'string',
880
- description: '文档标识。支持 wiki nodes 的 nodeId(dentryUuid)、docKey,或文档 URL。重要:create_wiki_doc 返回的 nodeId 不能用于内容读写,请使用 docKey 或 dentryUuid。若传入 create 的 nodeId,需额外提供 workspace_id 以自动查找'
880
+ description: '文档标识。支持 nodeId(dentryUuid)、docKey,或文档 URL'
881
881
  },
882
882
  workspace_id: {
883
883
  type: 'string',
884
- description: '知识库 ID(可选)。当 doc_key 来自 create_wiki_doc 返回的 nodeId 时,提供此参数可自动查找正确的 dentryUuid'
884
+ description: '知识库 ID(可选)。自动通过 wiki/nodes API 解析。'
885
885
  },
886
886
  operator_id: {
887
887
  type: 'string',
@@ -893,13 +893,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
893
893
  },
894
894
  {
895
895
  name: 'update_wiki_doc_content',
896
- description: '覆写文档正文内容(⚠️ 全量覆盖,不可撤销)。注意:仅对 create_wiki_doc 创建的文档有效;知识库中已有文档(非通过此工具创建的)不受支持。',
896
+ description: '覆写 .adoc 在线文档正文内容(⚠️ 全量覆盖,不可撤销)。对上传的 .md 等文件不支持修改。',
897
897
  inputSchema: {
898
898
  type: 'object',
899
899
  properties: {
900
900
  doc_key: {
901
901
  type: 'string',
902
- description: '文档标识。支持 wiki nodes 的 nodeId(dentryUuid)、docKey,或文档 URL。重要:create_wiki_doc 返回的 nodeId 不能用于内容读写,请使用 docKey 或 dentryUuid。若传入 create 的 nodeId,需额外提供 workspace_id 以自动查找'
902
+ description: '文档标识。支持 nodeId(dentryUuid)、docKey,或文档 URL。仅 .adoc 在线文档支持写入。'
903
903
  },
904
904
  content: {
905
905
  type: 'string',
@@ -1192,6 +1192,11 @@ async function resolveDocKey(docKey, operatorId, dingtalkInstance) {
1192
1192
  const node = nodeInfo.node || nodeInfo;
1193
1193
  console.error(`[DEBUG] resolveDocKey: wiki/nodes response keys=${Object.keys(node).join(',')}`);
1194
1194
 
1195
+ // 缓存 workspaceId,供后续 fallback 使用
1196
+ if (node.workspaceId) {
1197
+ dingtalkInstance._resolvedWorkspaceId = node.workspaceId;
1198
+ }
1199
+
1195
1200
  // wiki/nodes 的 nodeId = dentryUuid,直接可用
1196
1201
  // 但 node.document.docKey 可能是不同的值,优先使用
1197
1202
  if (node.document?.docKey) {
@@ -1345,6 +1350,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1345
1350
  });
1346
1351
 
1347
1352
  const doc = response.data;
1353
+ const nodeId = doc.nodeId || doc.id;
1348
1354
  const typeLabels = {
1349
1355
  DOC: '文档',
1350
1356
  WORKBOOK: '表格',
@@ -1364,7 +1370,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1364
1370
  '',
1365
1371
  `${typeIcon} ${name}`,
1366
1372
  `🗂️ 类型: ${doc_type}`,
1367
- `🆔 Node ID: ${doc.nodeId}`,
1373
+ `🆔 Node ID: ${nodeId}`,
1368
1374
  ];
1369
1375
 
1370
1376
  if (doc.docKey) {
@@ -1381,7 +1387,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1381
1387
  }
1382
1388
 
1383
1389
  setImmediate(() => {
1384
- wikiIndex.add(doc.nodeId || doc.docKey, {
1390
+ wikiIndex.add(nodeId || doc.docKey, {
1385
1391
  title: name,
1386
1392
  content: args.content || '',
1387
1393
  workspaceId: workspace_id || doc.workspaceId,
@@ -1389,7 +1395,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1389
1395
  url: doc.url || '',
1390
1396
  type: doc_type,
1391
1397
  });
1392
- const contentId = doc.docKey || doc.dentryUuid || doc.nodeId;
1398
+ const contentId = doc.docKey || doc.dentryUuid || nodeId;
1393
1399
  if (args.content && contentId) {
1394
1400
  dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${contentId}/overwriteContent`, {
1395
1401
  operatorId: opId,
@@ -1444,7 +1450,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1444
1450
  const result = await dingtalk.docRequest('GET', `/v2.0/wiki/nodes/${node_id}`, {
1445
1451
  operatorId: operator_id || null
1446
1452
  });
1447
- const node = result;
1453
+ const node = result.node || result;
1448
1454
  let output = `📄 节点详情\n\n`;
1449
1455
  output += `名称: ${node.name || '-'}\n`;
1450
1456
  output += `ID: ${node.id || node.nodeId || '-'}\n`;
@@ -1506,7 +1512,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1506
1512
  for (const child of children) {
1507
1513
  const name = child.name || '';
1508
1514
  if (name.includes(keyword)) {
1509
- matchedNodes.push({ name, nodeId: child.dentryId || child.nodeId || child.id, workspaceId: ws.workspaceId, workspaceName: ws.name, type: child.contentType === 'folder' ? '文件夹' : '文档', url: child.url || '' });
1515
+ matchedNodes.push({ name, nodeId: child.dentryUuid || child.dentryId || child.nodeId || child.id, workspaceId: ws.workspaceId, workspaceName: ws.name, type: child.contentType === 'folder' ? '文件夹' : '文档', url: child.url || '' });
1510
1516
  }
1511
1517
  const childId = child.dentryId || child.nodeId || child.id;
1512
1518
  if (child.hasChildren && childId && !visited.has(childId)) queue.push(childId);
@@ -1562,6 +1568,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1562
1568
  const icon = item.type === 'FOLDER' || item.type === 'folder' ? '📁' : '📄';
1563
1569
  output += `${i + 1}. ${icon} ${item.title}\n`;
1564
1570
  output += ` 知识库: ${item.workspaceName} (${item.workspaceId})\n`;
1571
+ output += ` Node ID: ${item.id}\n`;
1565
1572
  output += ` 匹配度: ${(item.score * 100).toFixed(0)}%\n`;
1566
1573
  if (item.url) output += ` 链接: ${item.url}\n`;
1567
1574
  if (item.content) {
@@ -1633,10 +1640,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1633
1640
  if (operator_id) {
1634
1641
  dingtalk.setOperatorId(operator_id);
1635
1642
  }
1643
+ dingtalk._resolvedWorkspaceId = null;
1636
1644
 
1637
1645
  let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
1638
1646
  console.error(`[DEBUG] get_wiki_doc_content: input=${docKey}, resolved=${realDocKey}`);
1639
1647
 
1648
+ const effectiveWsId = workspaceId || dingtalk._resolvedWorkspaceId;
1649
+
1640
1650
  // 尝试 blocks API 读取 .adoc 在线文档内容
1641
1651
  try {
1642
1652
  const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${realDocKey}/blocks`, { operatorId: operator_id || null });
@@ -1659,7 +1669,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1659
1669
  const isDocKeyIllegal = blocksErr.message.includes('doc key is illegal');
1660
1670
 
1661
1671
  // Fallback: 尝试存储 v1.0 下载 API(对上传的 .md 等文件有效)
1662
- if (isDocKeyIllegal && workspaceId) {
1672
+ if (isDocKeyIllegal && effectiveWsId) {
1663
1673
  try {
1664
1674
  const token = await dingtalk.getAccessToken();
1665
1675
  const opId = await dingtalk.resolveOperatorId(operator_id || null);
@@ -1678,7 +1688,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1678
1688
  if (parentId) dirParams.dentryId = parentId;
1679
1689
  const dirRes = await axios({
1680
1690
  method: 'GET',
1681
- url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${workspaceId}/directories`,
1691
+ url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${effectiveWsId}/directories`,
1682
1692
  headers: { 'x-acs-dingtalk-access-token': token },
1683
1693
  params: dirParams
1684
1694
  });
@@ -1722,7 +1732,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1722
1732
  let hint = '';
1723
1733
  if (isDocKeyIllegal) {
1724
1734
  hint = `\n\n原因: 此文档不是 .adoc 在线文档,blocks API 无法读取。`;
1725
- if (workspaceId) {
1735
+ if (effectiveWsId) {
1726
1736
  hint += `\n已尝试存储 v1.0 下载 API 但仍失败(需开通 Storage.DownloadInfo.Read 权限)。`;
1727
1737
  } else {
1728
1738
  hint += `\n请提供 workspace_id 以尝试存储 v1.0 下载 API。`;
@@ -1731,7 +1741,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1731
1741
  return {
1732
1742
  content: [{
1733
1743
  type: 'text',
1734
- text: `❌ 读取文档内容失败\n错误: ${blocksErr.message}${hint}\n\n调试信息:\n- 输入 doc_key: ${docKey}\n- 解析后 realDocKey: ${realDocKey}\n- workspaceId: ${workspaceId || '未提供'}\n- operator_id: ${operator_id || '未提供(将自动解析)'}`
1744
+ text: `❌ 读取文档内容失败\n错误: ${blocksErr.message}${hint}\n\n调试信息:\n- 输入 doc_key: ${docKey}\n- 解析后 realDocKey: ${realDocKey}\n- workspaceId: ${effectiveWsId || '未提供'}\n- operator_id: ${operator_id || '未提供(将自动解析)'}`
1735
1745
  }],
1736
1746
  isError: true
1737
1747
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dingtalk-wiki",
3
- "version": "1.2.17",
3
+ "version": "1.2.18",
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": {