dingtalk-wiki 1.1.3 → 1.1.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 (3) hide show
  1. package/index.js +563 -20
  2. package/package.json +1 -1
  3. package/skill/SKILL.md +69 -1
package/index.js CHANGED
@@ -52,6 +52,64 @@ function loadEnvFile(filePath) {
52
52
  return true;
53
53
  }
54
54
 
55
+ function getElementsText(elements) {
56
+ if (!elements || !Array.isArray(elements)) return '';
57
+ return elements.map(el => {
58
+ if (el.textRun?.content) return el.textRun.content;
59
+ if (el.paragraphRun?.richText?.elements) return getElementsText(el.paragraphRun.richText.elements);
60
+ return '';
61
+ }).join('');
62
+ }
63
+
64
+ function extractBlockText(block) {
65
+ const getText = () => {
66
+ switch (block.blockType) {
67
+ case 'paragraph':
68
+ return getElementsText(block.paragraph?.richText?.elements);
69
+ case 'heading': {
70
+ const level = block.heading?.headingType || 1;
71
+ const headingText = getElementsText(block.heading?.richText?.elements);
72
+ return `${'#'.repeat(level)} ${headingText}`;
73
+ }
74
+ case 'table': {
75
+ const table = block.table;
76
+ if (!table?.cells) return '[空表格]';
77
+ const cellTexts = table.cells.map(cell => {
78
+ const cellBlocks = cell.blocks || [];
79
+ return cellBlocks.map(cb => extractBlockText(cb)).join(' | ');
80
+ });
81
+ const colCount = table.columnsCount || (table.cells[0]?.blocks?.length || 1);
82
+ const rows = [];
83
+ let currentRow = [];
84
+ cellTexts.forEach((t, i) => {
85
+ currentRow.push(t);
86
+ if (currentRow.length === colCount) {
87
+ rows.push(currentRow.join(' | '));
88
+ currentRow = [];
89
+ }
90
+ });
91
+ if (currentRow.length) rows.push(currentRow.join(' | '));
92
+ return rows.map(r => `| ${r} |`).join('\n');
93
+ }
94
+ case 'unorderedList':
95
+ return `• ${getElementsText(block.unorderedList?.richText?.elements)}`;
96
+ case 'orderedList':
97
+ return `1. ${getElementsText(block.orderedList?.richText?.elements)}`;
98
+ case 'blockquote':
99
+ return `> ${getElementsText(block.blockquote?.richText?.elements)}`;
100
+ case 'codeBlock':
101
+ return '```\n' + getElementsText(block.codeBlock?.richText?.elements) + '\n```';
102
+ case 'divider':
103
+ return '---';
104
+ case 'image':
105
+ return `[图片: ${block.image?.caption || ''}]`;
106
+ default:
107
+ return `[${block.blockType}] ${getElementsText(block.paragraph?.richText?.elements)}`;
108
+ }
109
+ };
110
+ return getText();
111
+ }
112
+
55
113
  const DOTENV_CANDIDATES = [
56
114
  process.env.DINGTALK_WIKI_ENV_PATH,
57
115
  path.join(process.cwd(), '.env'),
@@ -228,6 +286,33 @@ class DingTalkClient {
228
286
  return this.operatorId;
229
287
  }
230
288
 
289
+ async docRequest(method, pathName, { operatorId = null, data = null } = {}) {
290
+ const token = await this.getAccessToken();
291
+ const resolvedOperatorId = await this.resolveOperatorId(operatorId);
292
+ const url = `${DINGTALK_API_V2}${pathName}`;
293
+
294
+ try {
295
+ const response = await axios({
296
+ method,
297
+ url,
298
+ headers: {
299
+ 'x-acs-dingtalk-access-token': token,
300
+ 'Content-Type': 'application/json'
301
+ },
302
+ params: {
303
+ operatorId: resolvedOperatorId
304
+ },
305
+ data
306
+ });
307
+ return response.data;
308
+ } catch (error) {
309
+ if (error.response) {
310
+ throw new Error(`${error.response.data?.message || error.message} (code: ${error.response.data?.code})`);
311
+ }
312
+ throw error;
313
+ }
314
+ }
315
+
231
316
  async notableRequest(method, pathName, { operatorId = null, params = {}, data = null } = {}) {
232
317
  const token = await this.getAccessToken();
233
318
  const resolvedOperatorId = await this.resolveOperatorId(operatorId);
@@ -332,6 +417,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
332
417
  workspace_id: {
333
418
  type: 'string',
334
419
  description: '知识库工作空间 ID'
420
+ },
421
+ operator_id: {
422
+ type: 'string',
423
+ description: '操作者 unionid(不传则使用默认用户)'
335
424
  }
336
425
  },
337
426
  required: ['workspace_id']
@@ -349,7 +438,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
349
438
  },
350
439
  parent_node_id: {
351
440
  type: 'string',
352
- description: '父节点 ID(不传则获取根目录)'
441
+ description: '父节点 ID(不传则列出根目录节点)'
442
+ },
443
+ operator_id: {
444
+ type: 'string',
445
+ description: '操作者 unionid(不传则使用默认用户)'
353
446
  }
354
447
  },
355
448
  required: ['workspace_id']
@@ -371,9 +464,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
371
464
  },
372
465
  doc_type: {
373
466
  type: 'string',
374
- description: '文档类型: DOC(文字), WORKBOOK(表格), MIND(脑图), FOLDER(文件夹)',
375
- enum: ['DOC', 'WORKBOOK', 'MIND', 'FOLDER'],
376
- default: 'DOC'
467
+ description: '文档类型,可选: DOC / WORKBOOK / MIND / FOLDER(默认 DOC)'
468
+ },
469
+ operator_id: {
470
+ type: 'string',
471
+ description: '操作者 unionid(不传则使用默认用户)'
377
472
  },
378
473
  parent_node_id: {
379
474
  type: 'string',
@@ -396,6 +491,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
396
491
  node_id: {
397
492
  type: 'string',
398
493
  description: '节点 ID'
494
+ },
495
+ operator_id: {
496
+ type: 'string',
497
+ description: '操作者 unionid(不传则使用默认用户)'
399
498
  }
400
499
  },
401
500
  required: ['node_id']
@@ -403,7 +502,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
403
502
  },
404
503
  {
405
504
  name: 'search_wiki',
406
- description: '搜索知识库内容',
505
+ description: '搜索知识库(POST /v2.0/doc/search)',
407
506
  inputSchema: {
408
507
  type: 'object',
409
508
  properties: {
@@ -414,6 +513,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
414
513
  workspace_id: {
415
514
  type: 'string',
416
515
  description: '指定知识库 ID(可选)'
516
+ },
517
+ max_results: {
518
+ type: 'number',
519
+ description: '返回条数上限(默认 10,最大 20)'
520
+ },
521
+ next_token: {
522
+ type: 'string',
523
+ description: '分页游标(上次返回的 nextToken)'
524
+ },
525
+ operator_id: {
526
+ type: 'string',
527
+ description: '操作者 unionid(不传则使用默认用户)'
417
528
  }
418
529
  },
419
530
  required: ['keyword']
@@ -476,6 +587,255 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
476
587
  required: ['userid']
477
588
  }
478
589
  },
590
+ {
591
+ name: 'get_wiki_doc_content',
592
+ description: '读取文档正文内容(返回 Block 结构,含标题、段落、表格等)',
593
+ inputSchema: {
594
+ type: 'object',
595
+ properties: {
596
+ doc_key: {
597
+ type: 'string',
598
+ description: '文档 docKey。wiki nodes 返回的 nodeId 本质是 dentryUuid,可直接用于此处'
599
+ },
600
+ operator_id: {
601
+ type: 'string',
602
+ description: '操作者 unionid(不传则使用默认用户)'
603
+ }
604
+ },
605
+ required: ['doc_key']
606
+ }
607
+ },
608
+ {
609
+ name: 'update_wiki_doc_content',
610
+ description: '覆写文档正文内容(⚠️ 全量覆盖,不可撤销)',
611
+ inputSchema: {
612
+ type: 'object',
613
+ properties: {
614
+ doc_key: {
615
+ type: 'string',
616
+ description: '文档 docKey。wiki nodes 返回的 nodeId 本质是 dentryUuid,可直接用于此处'
617
+ },
618
+ content: {
619
+ type: 'string',
620
+ description: '要写入的 Markdown 内容'
621
+ },
622
+ operator_id: {
623
+ type: 'string',
624
+ description: '操作者 unionid(不传则使用默认用户)'
625
+ }
626
+ },
627
+ required: ['doc_key', 'content']
628
+ }
629
+ },
630
+ {
631
+ name: 'rename_wiki_doc',
632
+ description: '重命名文档',
633
+ inputSchema: {
634
+ type: 'object',
635
+ properties: {
636
+ workspace_id: {
637
+ type: 'string',
638
+ description: '知识库工作空间 ID'
639
+ },
640
+ node_id: {
641
+ type: 'string',
642
+ description: '节点 ID(重命名的目标文档)'
643
+ },
644
+ name: {
645
+ type: 'string',
646
+ description: '新的文档名称'
647
+ },
648
+ operator_id: {
649
+ type: 'string',
650
+ description: '操作者 unionid(不传则使用默认用户)'
651
+ }
652
+ },
653
+ required: ['workspace_id', 'node_id', 'name']
654
+ }
655
+ },
656
+ {
657
+ name: 'delete_wiki_doc',
658
+ description: '删除文档节点',
659
+ inputSchema: {
660
+ type: 'object',
661
+ properties: {
662
+ workspace_id: {
663
+ type: 'string',
664
+ description: '知识库工作空间 ID'
665
+ },
666
+ node_id: {
667
+ type: 'string',
668
+ description: '节点 ID(要删除的目标文档)'
669
+ },
670
+ operator_id: {
671
+ type: 'string',
672
+ description: '操作者 unionid(不传则使用默认用户)'
673
+ }
674
+ },
675
+ required: ['workspace_id', 'node_id']
676
+ }
677
+ },
678
+ {
679
+ name: 'create_notable_record',
680
+ description: '在 AI 表格数据表中创建一条或多条记录',
681
+ inputSchema: {
682
+ type: 'object',
683
+ properties: {
684
+ base_id: {
685
+ type: 'string',
686
+ description: 'Notable baseId'
687
+ },
688
+ sheet_id: {
689
+ type: 'string',
690
+ description: '数据表 ID 或名称,可通过 list_notable_sheets 获取'
691
+ },
692
+ records: {
693
+ type: 'array',
694
+ description: '要创建的记录数组。每条记录为 { fields: { 字段名: 值 } }',
695
+ items: {
696
+ type: 'object',
697
+ properties: {
698
+ fields: {
699
+ type: 'object',
700
+ description: '字段名到字段值的映射'
701
+ }
702
+ },
703
+ required: ['fields']
704
+ }
705
+ },
706
+ operator_id: {
707
+ type: 'string',
708
+ description: '操作者 unionid(不传则使用默认用户)'
709
+ }
710
+ },
711
+ required: ['base_id', 'sheet_id', 'records']
712
+ }
713
+ },
714
+ {
715
+ name: 'update_notable_record',
716
+ description: '更新 AI 表格数据表中的多条记录',
717
+ inputSchema: {
718
+ type: 'object',
719
+ properties: {
720
+ base_id: {
721
+ type: 'string',
722
+ description: 'Notable baseId'
723
+ },
724
+ sheet_id: {
725
+ type: 'string',
726
+ description: '数据表 ID 或名称'
727
+ },
728
+ records: {
729
+ type: 'array',
730
+ description: '要更新的记录数组。每条记录格式为 { id: "recordId", fields: { 字段名: 值 } }',
731
+ items: {
732
+ type: 'object',
733
+ properties: {
734
+ id: {
735
+ type: 'string',
736
+ description: '记录 ID'
737
+ },
738
+ fields: {
739
+ type: 'object',
740
+ description: '要更新的字段'
741
+ }
742
+ },
743
+ required: ['id', 'fields']
744
+ }
745
+ },
746
+ operator_id: {
747
+ type: 'string',
748
+ description: '操作者 unionid(不传则使用默认用户)'
749
+ }
750
+ },
751
+ required: ['base_id', 'sheet_id', 'records']
752
+ }
753
+ },
754
+ {
755
+ name: 'delete_notable_record',
756
+ description: '删除 AI 表格数据表中的多条记录',
757
+ inputSchema: {
758
+ type: 'object',
759
+ properties: {
760
+ base_id: {
761
+ type: 'string',
762
+ description: 'Notable baseId'
763
+ },
764
+ sheet_id: {
765
+ type: 'string',
766
+ description: '数据表 ID 或名称'
767
+ },
768
+ record_ids: {
769
+ type: 'array',
770
+ description: '要删除的记录 ID 列表',
771
+ items: {
772
+ type: 'string'
773
+ }
774
+ },
775
+ operator_id: {
776
+ type: 'string',
777
+ description: '操作者 unionid(不传则使用默认用户)'
778
+ }
779
+ },
780
+ required: ['base_id', 'sheet_id', 'record_ids']
781
+ }
782
+ },
783
+ {
784
+ name: 'create_notable_sheet',
785
+ description: '在 AI 表格中创建一个新的数据表',
786
+ inputSchema: {
787
+ type: 'object',
788
+ properties: {
789
+ base_id: {
790
+ type: 'string',
791
+ description: 'Notable baseId'
792
+ },
793
+ name: {
794
+ type: 'string',
795
+ description: '数据表名称'
796
+ },
797
+ fields: {
798
+ type: 'array',
799
+ description: '数据表字段配置(可选),格式: [{ name: "字段名", type: "字段类型", property: {} }]',
800
+ items: {
801
+ type: 'object',
802
+ properties: {
803
+ name: { type: 'string', description: '字段名' },
804
+ type: { type: 'string', description: '字段类型,如 Text, Number, Select 等' }
805
+ },
806
+ required: ['name', 'type']
807
+ }
808
+ },
809
+ operator_id: {
810
+ type: 'string',
811
+ description: '操作者 unionid(不传则使用默认用户)'
812
+ }
813
+ },
814
+ required: ['base_id', 'name']
815
+ }
816
+ },
817
+ {
818
+ name: 'delete_notable_sheet',
819
+ description: '删除 AI 表格中的一个数据表',
820
+ inputSchema: {
821
+ type: 'object',
822
+ properties: {
823
+ base_id: {
824
+ type: 'string',
825
+ description: 'Notable baseId'
826
+ },
827
+ sheet_id: {
828
+ type: 'string',
829
+ description: '数据表 ID 或名称'
830
+ },
831
+ operator_id: {
832
+ type: 'string',
833
+ description: '操作者 unionid(不传则使用默认用户)'
834
+ }
835
+ },
836
+ required: ['base_id', 'sheet_id']
837
+ }
838
+ },
479
839
  {
480
840
  name: 'list_notable_sheets',
481
841
  description: '读取 AI 表格 / Notable 的所有数据表。对于 .able 节点,直接使用 nodeId 作为 base_id。',
@@ -560,8 +920,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
560
920
  }
561
921
 
562
922
  case 'get_wiki_workspace': {
563
- const { workspace_id } = args;
564
- // 通过列表获取详情
923
+ const { workspace_id, operator_id } = args;
924
+ if (operator_id) {
925
+ dingtalk.setOperatorId(operator_id);
926
+ }
565
927
  const result = await dingtalk.wikiRequest('workspaces');
566
928
  const workspaces = result.workspaces || [];
567
929
  const workspace = workspaces.find(ws => ws.workspaceId === workspace_id);
@@ -732,24 +1094,65 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
732
1094
  }
733
1095
 
734
1096
  case 'get_wiki_node': {
735
- const { node_id } = args;
736
- // 通过搜索或其他方式获取节点详情
1097
+ const { node_id, operator_id } = args;
1098
+ if (operator_id) {
1099
+ dingtalk.setOperatorId(operator_id);
1100
+ }
1101
+ const result = await dingtalk.docRequest('GET', `/v2.0/wiki/nodes/${node_id}`, {
1102
+ operatorId: operator_id || null
1103
+ });
1104
+ const node = result;
1105
+ let output = `📄 节点详情\n\n`;
1106
+ output += `名称: ${node.name || '-'}\n`;
1107
+ output += `ID: ${node.id || node.nodeId || '-'}\n`;
1108
+ output += `类型: ${node.type || '-'}\n`;
1109
+ output += `知识库 ID: ${node.workspaceId || '-'}\n`;
1110
+ output += `父节点: ${node.parentNodeId || '-'}\n`;
1111
+ output += `创建时间: ${node.createdTime || '-'}\n`;
1112
+ output += `修改时间: ${node.modifiedTime || '-'}\n`;
1113
+ output += `URL: ${node.url || '-'}\n`;
1114
+ if (node.document) {
1115
+ output += `文档信息: ${JSON.stringify(node.document)}\n`;
1116
+ }
737
1117
  return {
738
- content: [{
739
- type: 'text',
740
- text: `📄 节点 ID: ${node_id}\n\n请使用 list_wiki_nodes 获取节点列表,然后通过节点链接访问详情。`
741
- }]
1118
+ content: [{ type: 'text', text: output }]
742
1119
  };
743
1120
  }
744
1121
 
745
1122
  case 'search_wiki': {
746
- const { keyword, workspace_id } = args;
747
- // Wiki 搜索 API 需要额外权限
1123
+ const { keyword, workspace_id, max_results = 10, next_token, operator_id } = args;
1124
+ if (operator_id) {
1125
+ dingtalk.setOperatorId(operator_id);
1126
+ }
1127
+ const body = {
1128
+ keyword,
1129
+ maxResults: Math.min(max_results, 20)
1130
+ };
1131
+ if (next_token) {
1132
+ body.nextToken = next_token;
1133
+ }
1134
+ if (workspace_id) {
1135
+ body.option = { workspaceIds: [workspace_id] };
1136
+ }
1137
+ const result = await dingtalk.docRequest('POST', '/v2.0/doc/search', {
1138
+ operatorId: operator_id || null,
1139
+ data: body
1140
+ });
1141
+ const items = result.items || [];
1142
+ let output = `🔍 搜索 "${keyword}" (${items.length}条)\n\n`;
1143
+ items.forEach((item, i) => {
1144
+ output += `${i + 1}. ${item.name}\n`;
1145
+ output += ` 知识库: ${item.workspaceId}\n`;
1146
+ output += ` 链接: ${item.url}\n\n`;
1147
+ });
1148
+ if (!items.length) {
1149
+ output += '没有找到匹配的知识库。\n';
1150
+ }
1151
+ if (result.nextToken) {
1152
+ output += `--- 更多结果, nextToken: ${result.nextToken} ---\n`;
1153
+ }
748
1154
  return {
749
- content: [{
750
- type: 'text',
751
- text: `🔍 搜索知识库: ${keyword}\n\n搜索功能需要 Wiki.Search 权限。\n\n请直接访问知识库网页版进行搜索:\nhttps://alidocs.dingtalk.com/i/spaces/${workspace_id || ''}/search?keyword=${encodeURIComponent(keyword)}`
752
- }]
1155
+ content: [{ type: 'text', text: output }]
753
1156
  };
754
1157
  }
755
1158
 
@@ -803,6 +1206,73 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
803
1206
  };
804
1207
  }
805
1208
 
1209
+ case 'get_wiki_doc_content': {
1210
+ const { doc_key: docKey, operator_id } = args;
1211
+ if (operator_id) {
1212
+ dingtalk.setOperatorId(operator_id);
1213
+ }
1214
+ const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${docKey}/blocks`, { operatorId: operator_id || null });
1215
+ const blocks = result.result?.data || [];
1216
+ let output = '';
1217
+ blocks.forEach((block) => {
1218
+ const text = extractBlockText(block);
1219
+ output += text + '\n\n';
1220
+ });
1221
+ if (!blocks.length) {
1222
+ output = '(文档为空或无可读内容)';
1223
+ }
1224
+ return {
1225
+ content: [{
1226
+ type: 'text',
1227
+ text: output.trim()
1228
+ }]
1229
+ };
1230
+ }
1231
+
1232
+ case 'update_wiki_doc_content': {
1233
+ const { doc_key: docKey, content, operator_id } = args;
1234
+ if (operator_id) {
1235
+ dingtalk.setOperatorId(operator_id);
1236
+ }
1237
+ await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${docKey}/overwriteContent`, {
1238
+ operatorId: operator_id || null,
1239
+ data: { content, contentType: 'markdown' }
1240
+ });
1241
+ return {
1242
+ content: [{ type: 'text', text: '✅ 文档内容已更新' }]
1243
+ };
1244
+ }
1245
+
1246
+ case 'rename_wiki_doc': {
1247
+ const { workspace_id, node_id, name, operator_id } = args;
1248
+ if (operator_id) {
1249
+ dingtalk.setOperatorId(operator_id);
1250
+ }
1251
+ await dingtalk.docRequest('PATCH', `/v1.0/doc/workspaces/${workspace_id}/docs/${node_id}`, {
1252
+ operatorId: operator_id || null,
1253
+ data: { name }
1254
+ });
1255
+ return {
1256
+ content: [{ type: 'text', text: `✅ 文档已重命名为: ${name}` }]
1257
+ };
1258
+ }
1259
+
1260
+ case 'delete_wiki_doc': {
1261
+ const { workspace_id, node_id, operator_id } = args;
1262
+ if (operator_id) {
1263
+ dingtalk.setOperatorId(operator_id);
1264
+ }
1265
+ await dingtalk.docRequest('DELETE', `/v1.0/doc/workspaces/${workspace_id}/docs/${node_id}`, {
1266
+ operatorId: operator_id || null
1267
+ });
1268
+ return {
1269
+ content: [{
1270
+ type: 'text',
1271
+ text: `✅ 文档已删除 (nodeId: ${node_id})`
1272
+ }]
1273
+ };
1274
+ }
1275
+
806
1276
  case 'list_notable_sheets': {
807
1277
  const { base_id, operator_id } = args;
808
1278
  const result = await dingtalk.notableRequest('GET', `/v1.0/notable/bases/${base_id}/sheets`, {
@@ -853,6 +1323,79 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
853
1323
  };
854
1324
  }
855
1325
 
1326
+ case 'create_notable_record': {
1327
+ const { base_id, sheet_id, records, operator_id } = args;
1328
+ const result = await dingtalk.notableRequest('POST', `/v1.0/notable/bases/${base_id}/sheets/${sheet_id}/records`, {
1329
+ operatorId: operator_id || null,
1330
+ data: { records }
1331
+ });
1332
+ return {
1333
+ content: [{
1334
+ type: 'text',
1335
+ text: `✅ 已创建 ${records.length} 条记录\n\n${JSON.stringify(result, null, 2)}`
1336
+ }]
1337
+ };
1338
+ }
1339
+
1340
+ case 'update_notable_record': {
1341
+ const { base_id, sheet_id, records, operator_id } = args;
1342
+ const result = await dingtalk.notableRequest('PUT', `/v1.0/notable/bases/${base_id}/sheets/${sheet_id}/records`, {
1343
+ operatorId: operator_id || null,
1344
+ data: { records }
1345
+ });
1346
+ return {
1347
+ content: [{
1348
+ type: 'text',
1349
+ text: `✅ 已更新 ${records.length} 条记录\n\n${JSON.stringify(result, null, 2)}`
1350
+ }]
1351
+ };
1352
+ }
1353
+
1354
+ case 'delete_notable_record': {
1355
+ const { base_id, sheet_id, record_ids, operator_id } = args;
1356
+ const result = await dingtalk.notableRequest('DELETE', `/v1.0/notable/bases/${base_id}/sheets/${sheet_id}/records`, {
1357
+ operatorId: operator_id || null,
1358
+ data: { recordIds: record_ids }
1359
+ });
1360
+ return {
1361
+ content: [{
1362
+ type: 'text',
1363
+ text: `✅ 已删除 ${record_ids.length} 条记录`
1364
+ }]
1365
+ };
1366
+ }
1367
+
1368
+ case 'create_notable_sheet': {
1369
+ const { base_id, name, fields, operator_id } = args;
1370
+ const data = { name };
1371
+ if (fields) {
1372
+ data.fields = fields;
1373
+ }
1374
+ const result = await dingtalk.notableRequest('POST', `/v1.0/notable/bases/${base_id}/sheets`, {
1375
+ operatorId: operator_id || null,
1376
+ data
1377
+ });
1378
+ return {
1379
+ content: [{
1380
+ type: 'text',
1381
+ text: `✅ 数据表已创建\n\n${JSON.stringify(result, null, 2)}`
1382
+ }]
1383
+ };
1384
+ }
1385
+
1386
+ case 'delete_notable_sheet': {
1387
+ const { base_id, sheet_id, operator_id } = args;
1388
+ await dingtalk.notableRequest('DELETE', `/v1.0/notable/bases/${base_id}/sheets/${sheet_id}`, {
1389
+ operatorId: operator_id || null
1390
+ });
1391
+ return {
1392
+ content: [{
1393
+ type: 'text',
1394
+ text: `✅ 数据表已删除 (sheetId: ${sheet_id})`
1395
+ }]
1396
+ };
1397
+ }
1398
+
856
1399
  default:
857
1400
  throw new Error(`未知工具: ${name}`);
858
1401
  }
@@ -872,7 +1415,7 @@ async function main() {
872
1415
  const transport = new StdioServerTransport();
873
1416
  await server.connect(transport);
874
1417
  console.error('钉钉 Wiki MCP Server 已启动 v2.0');
875
- console.error(`Config path: ${CONFIG_PATH}`);
1418
+ console.error(`Config via DINGTALK_WIKI_CONFIG env var`);
876
1419
  }
877
1420
 
878
1421
  main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dingtalk-wiki",
3
- "version": "1.1.3",
3
+ "version": "1.1.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": {
package/skill/SKILL.md CHANGED
@@ -7,10 +7,19 @@
7
7
  - `get_wiki_workspace` - 获取知识库详情
8
8
  - `list_wiki_nodes` - 列出知识库节点(文档 / 目录)
9
9
  - `get_wiki_node` - 获取节点详情
10
+ - `get_wiki_doc_content` - 读取文档正文内容(Block 结构)
10
11
  - `create_wiki_doc` - 创建文档(支持 `DOC` / `WORKBOOK` / `MIND` / `FOLDER`)
12
+ - `update_wiki_doc_content` - 覆写文档内容(Markdown,⚠️ 全量覆盖)
13
+ - `rename_wiki_doc` - 重命名文档
14
+ - `delete_wiki_doc` - 删除文档节点
11
15
  - `search_wiki` - 搜索知识库内容
12
16
  - `list_notable_sheets` - 获取 `.able` / AI 表格中的所有数据表
13
17
  - `list_notable_records` - 获取指定数据表中的 records
18
+ - `create_notable_record` - 创建记录(单条或多条)
19
+ - `update_notable_record` - 更新记录
20
+ - `delete_notable_record` - 删除记录
21
+ - `create_notable_sheet` - 创建数据表
22
+ - `delete_notable_sheet` - 删除数据表
14
23
 
15
24
  ### 组织架构
16
25
  - `list_departments` - 列出部门列表
@@ -72,11 +81,36 @@ mcporter call dingtalk-wiki.create_wiki_doc \
72
81
  # 获取节点详情
73
82
  mcporter call dingtalk-wiki.get_wiki_node node_id="your_node_id"
74
83
 
75
- # 搜索知识库内容
84
+ # 读取文档正文内容
85
+ mcporter call dingtalk-wiki.get_wiki_doc_content doc_key="your_doc_key"
86
+
87
+ # 更新文档内容(Markdown,⚠️ 全量覆盖)
88
+ mcporter call dingtalk-wiki.update_wiki_doc_content \
89
+ doc_key="your_doc_key" \
90
+ content="# 新标题\n\n新正文"
91
+
92
+ # 重命名文档
93
+ mcporter call dingtalk-wiki.rename_wiki_doc \
94
+ workspace_id="your_workspace_id" \
95
+ node_id="your_node_id" \
96
+ name="新文档名称"
97
+
98
+ # 删除文档
99
+ mcporter call dingtalk-wiki.delete_wiki_doc \
100
+ workspace_id="your_workspace_id" \
101
+ node_id="your_node_id"
102
+
103
+ # 搜索知识库
76
104
  mcporter call dingtalk-wiki.search_wiki keyword="项目规划"
77
105
 
78
106
  # 在指定知识库内搜索
79
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"
80
114
  ```
81
115
 
82
116
  ### AI 表格(Notable)
@@ -101,6 +135,40 @@ mcporter call dingtalk-wiki.list_notable_records \
101
135
  base_id="your_base_id" \
102
136
  sheet_id="your_sheet_id" \
103
137
  next_token="your_next_token"
138
+
139
+ # 创建记录(单条或多条)
140
+ mcporter call dingtalk-wiki.create_notable_record \
141
+ base_id="your_base_id" \
142
+ sheet_id="your_sheet_id" \
143
+ records='[{"fields":{"姓名":"张三","年龄":25}}]'
144
+
145
+ # 更新记录
146
+ mcporter call dingtalk-wiki.update_notable_record \
147
+ base_id="your_base_id" \
148
+ sheet_id="your_sheet_id" \
149
+ records='[{"id":"record_id","fields":{"姓名":"李四","年龄":26}}]'
150
+
151
+ # 删除记录
152
+ mcporter call dingtalk-wiki.delete_notable_record \
153
+ base_id="your_base_id" \
154
+ sheet_id="your_sheet_id" \
155
+ record_ids='["record_id1","record_id2"]'
156
+
157
+ # 创建数据表
158
+ mcporter call dingtalk-wiki.create_notable_sheet \
159
+ base_id="your_base_id" \
160
+ name="新数据表"
161
+
162
+ # 创建带字段的数据表
163
+ mcporter call dingtalk-wiki.create_notable_sheet \
164
+ base_id="your_base_id" \
165
+ name="员工表" \
166
+ fields='[{"name":"姓名","type":"Text"},{"name":"年龄","type":"Number"}]'
167
+
168
+ # 删除数据表
169
+ mcporter call dingtalk-wiki.delete_notable_sheet \
170
+ base_id="your_base_id" \
171
+ sheet_id="your_sheet_id"
104
172
  ```
105
173
 
106
174
  ### 组织架构