dingtalk-wiki 1.2.16 → 1.2.17

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 +7 -459
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -223,7 +223,7 @@ async function rebuildSearchIndex() {
223
223
  method: 'POST',
224
224
  url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${child.spaceId}/dentries/${child.dentryId}/downloadInfos/query`,
225
225
  headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
226
- params: { unionId: await dingtalk.getCurrentUserUnionId() || '' },
226
+ params: { unionId: await dingtalk.resolveOperatorId(null) },
227
227
  data: {}
228
228
  });
229
229
  const data = dlRes.data;
@@ -747,28 +747,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
747
747
  required: ['node_id']
748
748
  }
749
749
  },
750
- {
751
- name: 'debug_resolve_doc_key',
752
- description: '[调试] 诊断 docKey 解析问题,测试所有可能的 API 路径并返回原始响应',
753
- inputSchema: {
754
- type: 'object',
755
- properties: {
756
- doc_key: {
757
- type: 'string',
758
- description: '文档标识(dentryUuid / docKey / nodeId / URL)'
759
- },
760
- workspace_id: {
761
- type: 'string',
762
- description: '知识库 ID(可选)'
763
- },
764
- operator_id: {
765
- type: 'string',
766
- description: '操作者 unionid(不传则使用默认用户)'
767
- }
768
- },
769
- required: ['doc_key']
770
- }
771
- },
772
750
  {
773
751
  name: 'search_wiki',
774
752
  description: '按名称搜索知识库中的文档和文件夹(遍历目录树,无需索引即可使用)',
@@ -1329,410 +1307,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1329
1307
  };
1330
1308
  }
1331
1309
 
1332
- case 'debug_resolve_doc_key': {
1333
- const { doc_key: docKey, workspace_id: workspaceId, operator_id } = args;
1334
- if (operator_id) {
1335
- dingtalk.setOperatorId(operator_id);
1336
- }
1337
-
1338
- const token = await dingtalk.getAccessToken();
1339
- const opId = await dingtalk.resolveOperatorId(operator_id || null);
1340
- const steps = [];
1341
- const input = String(docKey);
1342
-
1343
- steps.push({ step: '输入', result: input });
1344
-
1345
- // Step 1: URL extraction
1346
- const urlMatch = input.match(/\/i\/nodes\/([^\/\?#]+)/);
1347
- const urlExtracted = urlMatch ? urlMatch[1] : null;
1348
- steps.push({ step: 'URL 提取', result: urlExtracted || '无匹配', raw: urlMatch ? urlMatch[0] : null });
1349
-
1350
- // Step 2: wiki/nodes API
1351
- let wikiNodeResponse = null;
1352
- let wikiWsId = null;
1353
- try {
1354
- const nodeRes = await axios({
1355
- method: 'GET',
1356
- url: `${DINGTALK_API_V2}/v2.0/wiki/nodes/${input}`,
1357
- headers: { 'x-acs-dingtalk-access-token': token },
1358
- params: { operatorId: opId }
1359
- });
1360
- wikiNodeResponse = nodeRes.data;
1361
- wikiWsId = wikiNodeResponse.workspaceId || wikiNodeResponse.node?.workspaceId || null;
1362
- const node = wikiNodeResponse;
1363
- steps.push({
1364
- step: 'wiki/nodes API',
1365
- result: '成功',
1366
- raw: node
1367
- });
1368
- } catch (e) {
1369
- steps.push({
1370
- step: 'wiki/nodes API',
1371
- result: '失败',
1372
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1373
- });
1374
- }
1375
-
1376
- const effectiveWsId = workspaceId || wikiWsId;
1377
-
1378
- // Step 3: doc metadata API (GET /v1.0/doc/suites/documents/{docKey})
1379
- try {
1380
- const metaRes = await axios({
1381
- method: 'GET',
1382
- url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}`,
1383
- headers: { 'x-acs-dingtalk-access-token': token },
1384
- params: { operatorId: opId }
1385
- });
1386
- steps.push({
1387
- step: 'doc metadata API (GET /documents/{docKey})',
1388
- result: '成功',
1389
- raw: metaRes.data
1390
- });
1391
- } catch (e) {
1392
- steps.push({
1393
- step: 'doc metadata API (GET /documents/{docKey})',
1394
- result: '失败',
1395
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1396
- });
1397
- }
1398
-
1399
- // Step 4: blocks API
1400
- try {
1401
- const blocksRes = await axios({
1402
- method: 'GET',
1403
- url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/blocks`,
1404
- headers: { 'x-acs-dingtalk-access-token': token },
1405
- params: { operatorId: opId }
1406
- });
1407
- const blockCount = blocksRes.data?.result?.data?.length || 0;
1408
- steps.push({
1409
- step: 'blocks API',
1410
- result: `成功 (${blockCount} 个 block)`,
1411
- raw: blocksRes.data
1412
- });
1413
- } catch (e) {
1414
- steps.push({
1415
- step: 'blocks API',
1416
- result: '失败',
1417
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1418
- });
1419
- }
1420
-
1421
- // Step 5: overwriteContent API (test with empty content — don't actually write)
1422
- try {
1423
- const overwriteRes = await axios({
1424
- method: 'POST',
1425
- url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/overwriteContent`,
1426
- headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
1427
- params: { operatorId: opId },
1428
- data: { content: 'test', contentType: 'markdown' }
1429
- });
1430
- steps.push({
1431
- step: 'overwriteContent API',
1432
- result: '成功(可写入)',
1433
- raw: overwriteRes.data
1434
- });
1435
- } catch (e) {
1436
- steps.push({
1437
- step: 'overwriteContent API',
1438
- result: '失败',
1439
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1440
- });
1441
- }
1442
-
1443
- // Step 6: search docs API (v1.0/doc/docs) — 需要 workspaceId + keyword
1444
- if (effectiveWsId) {
1445
- // Try without keyword first (might list all)
1446
- try {
1447
- const searchRes = await axios({
1448
- method: 'GET',
1449
- url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
1450
- headers: { 'x-acs-dingtalk-access-token': token },
1451
- params: { operatorId: opId, workspaceId: effectiveWsId, maxResults: 50 }
1452
- });
1453
- const docs = searchRes.data?.docs || [];
1454
- const matched = docs.find(d => d.nodeBO?.nodeId === input || d.nodeBO?.dentryUuid === input || d.docKey === input || d.nodeBO?.dentryId === input);
1455
- steps.push({
1456
- step: `doc search API (workspace=${effectiveWsId}, no keyword)`,
1457
- result: `成功 (${docs.length} 篇文档)` + (matched ? ', 找到匹配!' : ', 未找到匹配'),
1458
- raw: docs.length > 0 ? { count: docs.length, firstDocDocKey: docs[0]?.docKey || '(无)', firstDocNodeId: docs[0]?.nodeBO?.nodeId || '(无)', firstDocName: docs[0]?.name || '(无)' } : '空结果'
1459
- });
1460
- } catch (e) {
1461
- steps.push({
1462
- step: `doc search API (workspace=${effectiveWsId}, no keyword)`,
1463
- result: '失败',
1464
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1465
- });
1466
- }
1467
- // Try with keyword from wiki/nodes name if available
1468
- const wikiName = wikiNodeResponse?.node?.name || wikiNodeResponse?.name || '';
1469
- if (wikiName) {
1470
- try {
1471
- const searchRes = await axios({
1472
- method: 'GET',
1473
- url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
1474
- headers: { 'x-acs-dingtalk-access-token': token },
1475
- params: { operatorId: opId, workspaceId: effectiveWsId, keyword: wikiName.replace(/\.\w+$/, ''), maxResults: 10 }
1476
- });
1477
- const docs = searchRes.data?.docs || [];
1478
- steps.push({
1479
- step: `doc search API (keyword="${wikiName}")`,
1480
- result: `成功 (${docs.length} 篇)`,
1481
- raw: docs.length > 0 ? docs.map(d => ({ docKey: d.docKey || '(无)', name: d.name || '(无)', nodeId: d.nodeBO?.nodeId || '(无)', dentryUuid: d.nodeBO?.dentryUuid || '(无)' })) : '空结果'
1482
- });
1483
- } catch (e) {
1484
- steps.push({
1485
- step: `doc search API (keyword="${wikiName}")`,
1486
- result: '失败',
1487
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1488
- });
1489
- }
1490
- }
1491
- } else {
1492
- steps.push({ step: 'doc search API', result: '跳过(无法获取 workspace_id)' });
1493
- }
1494
-
1495
- // Step 7: 在知识库目录中查找此节点 + 查看是否有 docKey 字段
1496
- if (effectiveWsId) {
1497
- try {
1498
- const dirRes = await axios({
1499
- method: 'GET',
1500
- url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${effectiveWsId}/directories`,
1501
- headers: { 'x-acs-dingtalk-access-token': token },
1502
- params: { operatorId: opId, maxResults: 500 }
1503
- });
1504
- const children = dirRes.data?.children || [];
1505
- const match = children.find(c => c.dentryUuid === input || c.dentryId === input || c.id === input);
1506
- // Show first few children's available fields so user can see what fields exist
1507
- const sampleKeys = children.length > 0 ? Object.keys(children[0]) : [];
1508
- steps.push({
1509
- step: `知识库目录 (workspace=${effectiveWsId})`,
1510
- result: `${children.length} 个子节点` + (match ? ', 找到匹配!' : ', 未找到匹配'),
1511
- raw: match ? match : { sampleFields: sampleKeys, firstChildSample: children[0] || null }
1512
- });
1513
- } catch (e) {
1514
- steps.push({
1515
- step: `知识库目录 (workspace=${effectiveWsId})`,
1516
- result: '失败',
1517
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1518
- });
1519
- }
1520
- }
1521
-
1522
- // Step 8: 用 workspaceId 再次尝试 overwriteContent(确认文档套件是否完全不可用)
1523
- if (effectiveWsId) {
1524
- try {
1525
- const overwriteRes = await axios({
1526
- method: 'POST',
1527
- url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/overwriteContent`,
1528
- headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
1529
- params: { operatorId: opId },
1530
- data: { content: 'test', contentType: 'markdown' }
1531
- });
1532
- steps.push({
1533
- step: 'overwriteContent(带 workspaceId)',
1534
- result: '成功',
1535
- raw: overwriteRes.data
1536
- });
1537
- } catch (e) {
1538
- steps.push({
1539
- step: 'overwriteContent(带 workspaceId)',
1540
- result: '失败',
1541
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1542
- });
1543
- }
1544
- }
1545
-
1546
- // Step 9: 递归遍历目录树查找文档 + 存储API下载内容
1547
- let foundDentry = null;
1548
- let foundSpaceId = null;
1549
- if (effectiveWsId) {
1550
- try {
1551
- // BFS 遍历目录树查找 dentry
1552
- const queue = [''];
1553
- const visited = new Set();
1554
- while (queue.length > 0 && !foundDentry) {
1555
- const parentId = queue.shift();
1556
- if (visited.has(parentId)) continue;
1557
- visited.add(parentId);
1558
- try {
1559
- const dirParams = { operatorId: opId, maxResults: 500 };
1560
- if (parentId) dirParams.dentryId = parentId;
1561
- const dirRes = await axios({
1562
- method: 'GET',
1563
- url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${effectiveWsId}/directories`,
1564
- headers: { 'x-acs-dingtalk-access-token': token },
1565
- params: dirParams
1566
- });
1567
- const children = dirRes.data?.children || [];
1568
- for (const child of children) {
1569
- if (child.dentryUuid === input || child.dentryId === input) {
1570
- foundDentry = child;
1571
- foundSpaceId = child.spaceId;
1572
- break;
1573
- }
1574
- if (child.hasChildren && child.dentryId && !visited.has(child.dentryId)) {
1575
- queue.push(child.dentryId);
1576
- }
1577
- }
1578
- } catch (e) { /* skip */ }
1579
- }
1580
-
1581
- if (foundDentry) {
1582
- steps.push({
1583
- step: 'BFS 目录遍历找到文档',
1584
- result: `spaceId=${foundSpaceId}, dentryId=${foundDentry.dentryId}, name=${foundDentry.name}`,
1585
- raw: foundDentry
1586
- });
1587
-
1588
- // 尝试存储 API 获取 dentry 详情(含 docKey)
1589
- try {
1590
- const dentryRes = await axios({
1591
- method: 'GET',
1592
- url: `${DINGTALK_API_V2}/v2.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}`,
1593
- headers: { 'x-acs-dingtalk-access-token': token },
1594
- params: { operatorId: opId }
1595
- });
1596
- steps.push({
1597
- step: '存储 API dentry 详情',
1598
- result: '成功',
1599
- raw: dentryRes.data
1600
- });
1601
- } catch (e) {
1602
- steps.push({
1603
- step: '存储 API dentry 详情',
1604
- result: '失败',
1605
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1606
- });
1607
- }
1608
-
1609
- // 尝试存储 API 下载
1610
- try {
1611
- const dlRes = await axios({
1612
- method: 'POST',
1613
- url: `${DINGTALK_API_V2}/v2.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}/downloadInfos/query`,
1614
- headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
1615
- params: { operatorId: opId },
1616
- data: {}
1617
- });
1618
- steps.push({
1619
- step: '存储 API 获取下载链接',
1620
- result: '成功',
1621
- raw: dlRes.data
1622
- });
1623
- // 尝试从下载链接获取内容
1624
- const dlInfo = dlRes.data;
1625
- const downloadUrl = dlInfo.headerSignatureInfo?.resourceUrls?.[0] || dlInfo.downloadUrl || dlInfo.url;
1626
- if (downloadUrl) {
1627
- try {
1628
- const contentRes = await axios({ method: 'GET', url: downloadUrl, responseType: 'text' });
1629
- steps.push({
1630
- step: '存储 API 下载内容',
1631
- result: `成功 (${contentRes.data.length} 字符)`,
1632
- raw: contentRes.data.slice(0, 500) + (contentRes.data.length > 500 ? '\n... (截断)' : '')
1633
- });
1634
- } catch (dlErr) {
1635
- steps.push({
1636
- step: '存储 API 下载内容',
1637
- result: '失败',
1638
- raw: dlErr.message
1639
- });
1640
- }
1641
- }
1642
- } catch (e) {
1643
- steps.push({
1644
- step: '存储 API 获取下载链接',
1645
- result: '失败',
1646
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1647
- });
1648
- }
1649
- } else {
1650
- steps.push({ step: 'BFS 目录遍历', result: '未找到匹配(文档可能在深层目录)' });
1651
- }
1652
- } catch (e) {
1653
- steps.push({ step: 'BFS 目录遍历', result: '失败', raw: e.message });
1654
- }
1655
- }
1656
-
1657
- // Step 10: 存储 API v1.0 - 文件下载(.md 文件内容 = 文件下载)
1658
- if (foundSpaceId) {
1659
- try {
1660
- const dlRes = await axios({
1661
- method: 'POST',
1662
- url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}/downloadInfos/query`,
1663
- headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
1664
- params: { unionId: opId },
1665
- data: {}
1666
- });
1667
- const data = dlRes.data;
1668
- steps.push({
1669
- step: '存储 v1.0 获取下载链接 (POST /v1.0/storage/spaces/{sid}/dentries/{did}/downloadInfos/query)',
1670
- result: '成功',
1671
- raw: data
1672
- });
1673
- // 如果有下载链接,尝试下载内容
1674
- const downloadUrl = data.downloadInfo?.resourceUrl || data.resourceUrl || data.downloadUrl || data.url;
1675
- const downloadHeaders = data.downloadInfo?.headers || data.headers || {};
1676
- if (downloadUrl) {
1677
- try {
1678
- const contentRes = await axios({ method: 'GET', url: downloadUrl, headers: downloadHeaders, responseType: 'text' });
1679
- steps.push({
1680
- step: '文件内容下载',
1681
- result: `成功 (${contentRes.data.length} 字符)`,
1682
- raw: contentRes.data.slice(0, 1000) + (contentRes.data.length > 1000 ? '\n...(截断)' : '')
1683
- });
1684
- } catch (dlErr) {
1685
- steps.push({ step: '文件内容下载', result: '失败', raw: dlErr.message });
1686
- }
1687
- }
1688
- } catch (e) {
1689
- steps.push({
1690
- step: '存储 v1.0 获取下载链接',
1691
- result: '失败',
1692
- raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
1693
- });
1694
- }
1695
- }
1696
-
1697
-
1698
-
1699
- const lines = ['🔍 docKey 诊断报告', '', `输入: ${input}`, `operatorId: ${opId}`, ''];
1700
- steps.forEach(s => {
1701
- lines.push(`--- ${s.step} ---`);
1702
- lines.push(`结果: ${s.result}`);
1703
- if (s.raw) {
1704
- lines.push(`详情: ${JSON.stringify(s.raw, null, 2)}`);
1705
- }
1706
- lines.push('');
1707
- });
1708
- const isAdoc = foundDentry?.name?.endsWith('.adoc') || input.length > 20;
1709
- lines.push('💡 诊断结论:');
1710
- lines.push(` 输入 ID: ${input}`);
1711
- lines.push(` 操作者: ${opId}`);
1712
- lines.push('');
1713
- if (foundDentry) {
1714
- lines.push(` 文档名: ${foundDentry.name}`);
1715
- lines.push(` 类型推断: ${foundDentry.name?.endsWith('.adoc') ? '✅ .adoc 在线文档(可读写)' : foundDentry.name?.match(/\.(md|docx|xlsx|txt|xmind|mark|markdown)$/i) ? '❌ 导入的文档(非原生. adoc,内容 API 不支持)' : '⚠️ 未知类型'}`);
1716
- }
1717
- lines.push('');
1718
- lines.push(' 1️⃣ wiki/nodes API → ✅ 成功。节点存在');
1719
- lines.push(` 2️⃣ blocks API → ${foundDentry?.name?.endsWith('.adoc') ? '✅ 应对 .adoc 在线文档有效' : '❌ "doc key is illegal"(非 .adoc 文档无 docKey)'}`);
1720
- lines.push(` 3️⃣ overwriteContent → ${foundDentry?.name?.endsWith('.adoc') ? '✅ 应对 .adoc 在线文档有效' : '❌ 同上错误'}`);
1721
- lines.push(' 4️⃣ doc metadata → ❌ 404(该端点不存在)');
1722
- lines.push('');
1723
- lines.push('📌 关键区别:');
1724
- lines.push(' - 知识库"导入为在线文档" → 转为 .adoc → 有 docKey → blocks API 可读写');
1725
- lines.push(' - "上传文件" → 保持原格式 → 无 docKey → blocks API 不可用');
1726
- lines.push(' - create_wiki_doc 创建的是 .adoc 在线文档 → blocks API 可读写');
1727
- lines.push('');
1728
- lines.push('📌 存储 v1.0 下载 API (POST /v1.0/storage/spaces/{sid}/dentries/{did}/downloadInfos/query)');
1729
- lines.push(' 使用 unionId 参数,可获取上传文件的下载链接');
1730
- lines.push('📌 DocContent API (GET /v2.0/doc/dentries/{uuid}/contents) 此前 503');
1731
- lines.push('📌 请重新运行此工具测试以上 API');
1732
-
1733
- return { content: [{ type: 'text', text: lines.join('\n') }] };
1734
- }
1735
-
1736
1310
  case 'create_wiki_doc': {
1737
1311
  const { workspace_id, parent_node_id, name, doc_type = 'DOC', operator_id } = args;
1738
1312
  if (operator_id) {
@@ -2063,22 +1637,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2063
1637
  let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
2064
1638
  console.error(`[DEBUG] get_wiki_doc_content: input=${docKey}, resolved=${realDocKey}`);
2065
1639
 
2066
- // 如果 resolve 后值没变(可能是 create 返回的 nodeId),尝试搜索 API 查找
2067
- if (realDocKey === docKey && workspaceId) {
2068
- try {
2069
- const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
2070
- operatorId: operator_id || null,
2071
- extraParams: { workspaceId, keyword: '', maxResults: 50 }
2072
- });
2073
- const docs = searchRes.docs || [];
2074
- const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
2075
- if (matched?.nodeBO?.nodeId) {
2076
- realDocKey = matched.nodeBO.nodeId;
2077
- console.error(`[DEBUG] get_wiki_doc_content: search fallback resolved to ${realDocKey}`);
2078
- }
2079
- } catch (e) { console.error(`[DEBUG] get_wiki_doc_content: search fallback failed: ${e.message}`); }
2080
- }
2081
-
1640
+ // 尝试 blocks API 读取 .adoc 在线文档内容
2082
1641
  try {
2083
1642
  const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${realDocKey}/blocks`, { operatorId: operator_id || null });
2084
1643
  const blocks = result.result?.data || [];
@@ -2164,7 +1723,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2164
1723
  if (isDocKeyIllegal) {
2165
1724
  hint = `\n\n原因: 此文档不是 .adoc 在线文档,blocks API 无法读取。`;
2166
1725
  if (workspaceId) {
2167
- hint += `\n已尝试存储 v1.0 下载 API 但仍失败。`;
1726
+ hint += `\n已尝试存储 v1.0 下载 API 但仍失败(需开通 Storage.DownloadInfo.Read 权限)。`;
1727
+ } else {
1728
+ hint += `\n请提供 workspace_id 以尝试存储 v1.0 下载 API。`;
2168
1729
  }
2169
1730
  }
2170
1731
  return {
@@ -2185,20 +1746,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2185
1746
 
2186
1747
  let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
2187
1748
 
2188
- if (realDocKey === docKey && workspaceId) {
2189
- try {
2190
- const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
2191
- operatorId: operator_id || null,
2192
- extraParams: { workspaceId, keyword: '', maxResults: 50 }
2193
- });
2194
- const docs = searchRes.docs || [];
2195
- const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
2196
- if (matched?.nodeBO?.nodeId) {
2197
- realDocKey = matched.nodeBO.nodeId;
2198
- }
2199
- } catch (e) { /* ignore */ }
2200
- }
2201
-
1749
+ // 尝试 overwriteContent API 写入 .adoc 在线文档内容
2202
1750
  try {
2203
1751
  await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${realDocKey}/overwriteContent`, {
2204
1752
  operatorId: operator_id || null,
@@ -2208,7 +1756,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2208
1756
  const isDocKeyIllegal = e.message.includes('doc key is illegal');
2209
1757
  let hint = '';
2210
1758
  if (isDocKeyIllegal) {
2211
- hint = `\n\n原因: 此文档在 Doc Suite API 中无关联 docKey。overwriteContent 仅对通过 create_wiki_doc 创建的文档有效。`;
1759
+ hint = `\n\n原因: 此文档不是 .adoc 在线文档(可能是上传的 .md 文件),不支持修改。overwriteContent 仅对 .adoc 在线文档有效。`;
2212
1760
  }
2213
1761
  return {
2214
1762
  content: [{ type: 'text', text: `❌ 写入文档内容失败\n${e.message}${hint}` }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dingtalk-wiki",
3
- "version": "1.2.16",
3
+ "version": "1.2.17",
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": {