dingtalk-wiki 1.2.8 → 1.2.10

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 +100 -52
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1327,6 +1327,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1327
1327
  steps.push({ step: 'URL 提取', result: urlExtracted || '无匹配', raw: urlMatch ? urlMatch[0] : null });
1328
1328
 
1329
1329
  // Step 2: wiki/nodes API
1330
+ let wikiNodeResponse = null;
1331
+ let wikiWsId = null;
1330
1332
  try {
1331
1333
  const nodeRes = await axios({
1332
1334
  method: 'GET',
@@ -1334,19 +1336,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1334
1336
  headers: { 'x-acs-dingtalk-access-token': token },
1335
1337
  params: { operatorId: opId }
1336
1338
  });
1337
- const node = nodeRes.data;
1339
+ wikiNodeResponse = nodeRes.data;
1340
+ wikiWsId = wikiNodeResponse.workspaceId || wikiNodeResponse.node?.workspaceId || null;
1341
+ const node = wikiNodeResponse;
1338
1342
  steps.push({
1339
1343
  step: 'wiki/nodes API',
1340
1344
  result: '成功',
1341
- raw: {
1342
- nodeId: node.nodeId || node.id || node.node?.nodeId || '(无)',
1343
- name: node.name || node.node?.name || '(无)',
1344
- docKey: node.document?.docKey || node.docKey || node.node?.document?.docKey || '(无)',
1345
- dentryUuid: node.dentryUuid || node.node?.dentryUuid || '(无)',
1346
- hasChildren: node.hasChildren || node.node?.hasChildren || '(无)',
1347
- type: node.type || node.node?.type || '(无)',
1348
- workspaceId: node.workspaceId || node.node?.workspaceId || '(无)',
1349
- }
1345
+ raw: node
1350
1346
  });
1351
1347
  } catch (e) {
1352
1348
  steps.push({
@@ -1356,6 +1352,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1356
1352
  });
1357
1353
  }
1358
1354
 
1355
+ const effectiveWsId = workspaceId || wikiWsId;
1356
+
1359
1357
  // Step 3: doc metadata API (GET /v1.0/doc/suites/documents/{docKey})
1360
1358
  try {
1361
1359
  const metaRes = await axios({
@@ -1421,67 +1419,106 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1421
1419
  });
1422
1420
  }
1423
1421
 
1424
- // Step 6: search docs API (requires workspaceId)
1425
- if (workspaceId) {
1422
+ // Step 6: search docs API (v1.0/doc/docs) — 需要 workspaceId + keyword
1423
+ if (effectiveWsId) {
1424
+ // Try without keyword first (might list all)
1426
1425
  try {
1427
1426
  const searchRes = await axios({
1428
1427
  method: 'GET',
1429
1428
  url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
1430
1429
  headers: { 'x-acs-dingtalk-access-token': token },
1431
- params: { operatorId: opId, workspaceId, maxResults: 10 }
1430
+ params: { operatorId: opId, workspaceId: effectiveWsId, maxResults: 50 }
1432
1431
  });
1433
1432
  const docs = searchRes.data?.docs || [];
1434
- const matched = docs.find(d => d.nodeBO?.nodeId === input || d.nodeBO?.dentryUuid === input || d.docKey === input);
1433
+ const matched = docs.find(d => d.nodeBO?.nodeId === input || d.nodeBO?.dentryUuid === input || d.docKey === input || d.nodeBO?.dentryId === input);
1435
1434
  steps.push({
1436
- step: 'doc search API (v1.0/doc/docs)',
1437
- result: `成功 (${docs.length} 篇文档)` + (matched ? ', 找到匹配' : ', 未找到匹配'),
1438
- raw: matched || `${docs.length} results, first doc docKey=${docs[0]?.docKey || '(none)'}`
1435
+ step: `doc search API (workspace=${effectiveWsId}, no keyword)`,
1436
+ result: `成功 (${docs.length} 篇文档)` + (matched ? ', 找到匹配!' : ', 未找到匹配'),
1437
+ raw: docs.length > 0 ? { count: docs.length, firstDocDocKey: docs[0]?.docKey || '(无)', firstDocNodeId: docs[0]?.nodeBO?.nodeId || '(无)', firstDocName: docs[0]?.name || '()' } : '空结果'
1439
1438
  });
1440
1439
  } catch (e) {
1441
1440
  steps.push({
1442
- step: 'doc search API (v1.0/doc/docs)',
1441
+ step: `doc search API (workspace=${effectiveWsId}, no keyword)`,
1443
1442
  result: '失败',
1444
1443
  raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1445
1444
  });
1446
1445
  }
1446
+ // Try with keyword from wiki/nodes name if available
1447
+ const wikiName = wikiNodeResponse?.node?.name || wikiNodeResponse?.name || '';
1448
+ if (wikiName) {
1449
+ try {
1450
+ const searchRes = await axios({
1451
+ method: 'GET',
1452
+ url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
1453
+ headers: { 'x-acs-dingtalk-access-token': token },
1454
+ params: { operatorId: opId, workspaceId: effectiveWsId, keyword: wikiName.replace(/\.\w+$/, ''), maxResults: 10 }
1455
+ });
1456
+ const docs = searchRes.data?.docs || [];
1457
+ steps.push({
1458
+ step: `doc search API (keyword="${wikiName}")`,
1459
+ result: `成功 (${docs.length} 篇)`,
1460
+ raw: docs.length > 0 ? docs.map(d => ({ docKey: d.docKey || '(无)', name: d.name || '(无)', nodeId: d.nodeBO?.nodeId || '(无)', dentryUuid: d.nodeBO?.dentryUuid || '(无)' })) : '空结果'
1461
+ });
1462
+ } catch (e) {
1463
+ steps.push({
1464
+ step: `doc search API (keyword="${wikiName}")`,
1465
+ result: '失败',
1466
+ raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1467
+ });
1468
+ }
1469
+ }
1447
1470
  } else {
1448
- steps.push({ step: 'doc search API (v1.0/doc/docs)', result: '跳过(需 workspace_id)' });
1471
+ steps.push({ step: 'doc search API', result: '跳过(无法获取 workspace_id)' });
1449
1472
  }
1450
1473
 
1451
- // Step 7: wiki workspaces + directories search (try all workspaces)
1452
- if (!workspaceId) {
1474
+ // Step 7: 在知识库目录中查找此节点 + 查看是否有 docKey 字段
1475
+ if (effectiveWsId) {
1453
1476
  try {
1454
- const wsRes = await axios({
1477
+ const dirRes = await axios({
1455
1478
  method: 'GET',
1456
- url: `${DINGTALK_API_V2}/v2.0/wiki/workspaces`,
1479
+ url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${effectiveWsId}/directories`,
1457
1480
  headers: { 'x-acs-dingtalk-access-token': token },
1458
- params: { operatorId: opId }
1481
+ params: { operatorId: opId, maxResults: 500 }
1459
1482
  });
1460
- const workspaces = wsRes.data?.workspaces || [];
1461
- let found = null;
1462
- for (const ws of workspaces) {
1463
- if (found) break;
1464
- try {
1465
- const dirRes = await axios({
1466
- method: 'GET',
1467
- url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${ws.workspaceId}/directories`,
1468
- headers: { 'x-acs-dingtalk-access-token': token },
1469
- params: { operatorId: opId, maxResults: 500 }
1470
- });
1471
- const children = dirRes.data?.children || [];
1472
- const match = children.find(c => c.dentryUuid === input || c.dentryId === input || c.id === input);
1473
- if (match) {
1474
- found = { workspaceId: ws.workspaceId, workspaceName: ws.name, node: match };
1475
- }
1476
- } catch (dirErr) { /* skip workspace */ }
1477
- }
1483
+ const children = dirRes.data?.children || [];
1484
+ const match = children.find(c => c.dentryUuid === input || c.dentryId === input || c.id === input);
1485
+ // Show first few children's available fields so user can see what fields exist
1486
+ const sampleKeys = children.length > 0 ? Object.keys(children[0]) : [];
1478
1487
  steps.push({
1479
- step: '遍历知识库目录匹配',
1480
- result: found ? `在知识库 ${found.workspaceName}(${found.workspaceId}) 中找到` : '在所有知识库中未找到匹配',
1481
- raw: found ? { workspaceId: found.workspaceId, name: found.node?.name, dentryUuid: found.node?.dentryUuid, dentryId: found.node?.dentryId, hasChildren: found.node?.hasChildren, contentType: found.node?.contentType, docKey: found.node?.docKey || '(无)' } : null
1488
+ step: `知识库目录 (workspace=${effectiveWsId})`,
1489
+ result: `${children.length} 个子节点` + (match ? ', 找到匹配!' : ', 未找到匹配'),
1490
+ raw: match ? match : { sampleFields: sampleKeys, firstChildSample: children[0] || null }
1482
1491
  });
1483
1492
  } catch (e) {
1484
- steps.push({ step: '遍历知识库目录匹配', result: '失败', raw: e.message });
1493
+ steps.push({
1494
+ step: `知识库目录 (workspace=${effectiveWsId})`,
1495
+ result: '失败',
1496
+ raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1497
+ });
1498
+ }
1499
+ }
1500
+
1501
+ // Step 8: 用 workspaceId 再次尝试 overwriteContent(确认文档套件是否完全不可用)
1502
+ if (effectiveWsId) {
1503
+ try {
1504
+ const overwriteRes = await axios({
1505
+ method: 'POST',
1506
+ url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/overwriteContent`,
1507
+ headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
1508
+ params: { operatorId: opId },
1509
+ data: { content: 'test', contentType: 'markdown' }
1510
+ });
1511
+ steps.push({
1512
+ step: 'overwriteContent(带 workspaceId)',
1513
+ result: '成功',
1514
+ raw: overwriteRes.data
1515
+ });
1516
+ } catch (e) {
1517
+ steps.push({
1518
+ step: 'overwriteContent(带 workspaceId)',
1519
+ result: '失败',
1520
+ raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1521
+ });
1485
1522
  }
1486
1523
  }
1487
1524
 
@@ -1494,12 +1531,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1494
1531
  }
1495
1532
  lines.push('');
1496
1533
  });
1497
- lines.push('💡 提示: 如果 wiki/nodes API 成功并返回了 docKey,请使用该 docKey 调用 get_wiki_doc_content');
1498
- lines.push(' 如果 doc metadata API 成功,说明该 docKey 在 doc suite 中有效');
1499
- lines.push(' 如果 blocks API 失败但 overwriteContent 成功,问题在 blocks API 端');
1500
- lines.push(' 如果所有 API 都失败,说明该 docKey 在 doc suite 中不存在');
1501
- if (!workspaceId) {
1502
- lines.push(' 建议提供 workspace_id 参数以启用搜索回退路径');
1534
+ lines.push('💡 诊断结论:');
1535
+ lines.push(` 输入 ID: ${input}`);
1536
+ lines.push(` 操作者: ${opId}`);
1537
+ lines.push('');
1538
+ lines.push(' 1️⃣ wiki/nodes API → ✅ 成功。节点存在,但无 document.docKey');
1539
+ lines.push(' 2️⃣ blocks API → ❌ "doc key is illegal"');
1540
+ lines.push(' 3️⃣ overwriteContent → ❌ 同上错误');
1541
+ lines.push(' 4️⃣ doc metadata → ❌ 404(该端点不存在)');
1542
+ lines.push('');
1543
+ lines.push('📌 结论:此 dentryUuid 在 Wiki API 中有效,但在 Doc Suite API 中无效。');
1544
+ lines.push(' blocks/overwriteContent 仅对通过 create_wiki_doc 创建的文档有效');
1545
+ lines.push(' (这类文档返回独立的 docKey,与 nodeId/dentryUuid 不同)');
1546
+ lines.push('');
1547
+ lines.push('📌 现有知识库文档内容读写:无公开 REST API 支持');
1548
+ lines.push(' 若需要读取内容,可用 DingTalk 官方 MCP 服务器的 get_document_content');
1549
+ if (effectiveWsId) {
1550
+ lines.push(` - 此文档 workspaceId: ${effectiveWsId}`);
1503
1551
  }
1504
1552
 
1505
1553
  return { content: [{ type: 'text', text: lines.join('\n') }] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dingtalk-wiki",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
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": {