dingtalk-mcp 1.0.15 → 1.1.1

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.
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  - 钉钉待办
10
10
  - 钉钉日程
11
11
  - 钉钉签到
12
- - 钉钉服务窗
13
12
  - 钉钉工作通知
14
13
  - 钉钉应用管理
14
+ - 钉钉服务窗
15
15
 
16
16
  ## 如何使用
17
17
  ```json
@@ -25,7 +25,8 @@
25
25
  ],
26
26
  "env": {
27
27
  "DINGTALK_Client_ID": "your dingtalk client id",
28
- "DINGTALK_Client_Secret": "your dingtalk client secret"
28
+ "DINGTALK_Client_Secret": "your dingtalk client secret",
29
+ "ACTIVE_PROFILES": "dingtalk-contacts,dingtalk-calendar"
29
30
  }
30
31
  }
31
32
  }
@@ -36,21 +37,22 @@
36
37
  2. DINGTALK_Client_Secret
37
38
  3. ACTIVE_PROFILES,激活哪些钉钉MCP服务,逗号风格,如果是ALL则激活全部。可选集合
38
39
 
39
- | ProfileId | Description | Permission |
40
- |-----------------------------|---------------|---------------------------------|
41
- | dingtalk-contacts | 钉钉通讯录 |
42
- | dingtalk-department | 钉钉部门管理 |
43
- | dingtalk-robot-send-message | 钉钉机器人发消息/DING |
44
- | dingtalk-honor | 钉钉企业文化荣誉 |
45
- | dingtalk-tasks | 钉钉待办 | Todo.Todo.Write Todo.Todo.Read |
46
- | dingtalk-calendar | 钉钉日程 |
47
- | dingtalk-checkin | 钉钉签到 |
48
- | dingtalk-servicewindow | 钉钉服务窗 |
49
- | dingtalk-notice | 钉钉工作通知 |
50
- | dingtalk-app-manage | 钉钉应用管理 |
51
- 4. ROBOT_CODE,用于发消息/DING的机器人Code
52
- 5. ROBOT_ACCESS_TOKEN,群自定义机器人ACCESS_TOEKN,用于自定义机器人发消息
40
+ | ProfileId | Description | Permission |
41
+ |--------------------------|--------------------|-------------------------------------------------|
42
+ | dingtalk-contacts | 钉钉通讯录,默认激活 |
43
+ | dingtalk-department | 钉钉部门管理 |
44
+ | dingtalk-robot-send-message | 钉钉机器人发消息/DING,默认激活 | 需要企业内机器人发送消息权限 |
45
+ | dingtalk-honor | 钉钉企业文化荣誉 |
46
+ | dingtalk-tasks | 钉钉待办 | Todo.Todo.Write<br>Todo.Todo.Read |
47
+ | dingtalk-calendar | 钉钉日程 |
48
+ | dingtalk-checkin | 钉钉签到 |
49
+ | dingtalk-notice | 钉钉工作通知 |
50
+ | dingtalk-app-manage | 钉钉应用管理 | qyapi_microapp_manage<br>qyapi_get_microapp_list |
51
+ | dingtalk-service-window | 钉钉服务窗 | |
53
52
 
53
+ 4. ROBOT_CODE,用于发消息/DING的机器人Code
54
+ 5. ROBOT_ACCESS_TOKEN,群自定义机器人ACCESS_TOKEN,用于自定义机器人发消息
55
+ 6. DINGTALK_AGENT_ID 用于发送工作通知
54
56
  ### 如何获取钉钉Client ID和Client Secret
55
57
  1. [成为钉钉开发者](https://open.dingtalk.com/document/orgapp/obtain-developer-permissions)
56
58
  2. [创建应用](https://open.dingtalk.com/document/orgapp/create-an-application)
@@ -1,5 +1,6 @@
1
1
  server:
2
2
  name: dingtalk-calendar
3
+ description: 钉钉日历
3
4
  securitySchemes:
4
5
  - id: DingTalkAuth
5
6
  type: apiKey
@@ -1,16 +1,22 @@
1
1
  server:
2
2
  name: dingtalk-notice
3
3
  version: 1.0.0
4
- description: DingTalk Work Notice MCP Server - 钉钉工作通知
4
+ description: DingTalk Work Notice MCP Server - 钉钉工作通知MCP服务器
5
5
 
6
6
  tools:
7
7
  # 发送工作通知消息
8
8
  - name: sendNotice
9
- description: 发送工作通知消息,支持3种消息类型:text、link、markdown
9
+ description: 发送工作通知消息,支持 markdown 消息类型
10
10
  requestTemplate:
11
11
  method: POST
12
12
  url: https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2
13
13
  args:
14
+ - name: agent_id
15
+ type: number
16
+ required: true
17
+ position: body
18
+ description: 发送消息时使用的微应用的AgentID
19
+ system: DINGTALK_AGENT_ID
14
20
  - name: userid_list
15
21
  type: string
16
22
  required: false
@@ -26,11 +32,23 @@ tools:
26
32
  required: false
27
33
  position: body
28
34
  description: 是否发送给全员(可选,默认false)
29
- - name: msg
30
- type: object
35
+ - name: msg.msgtype
36
+ not_need_model_transform: true
37
+ type: string
38
+ required: true
39
+ position: body
40
+ description: markdown消息格式
41
+ default: markdown
42
+ - name: msg.markdown.title
43
+ type: string
44
+ required: true
45
+ position: body
46
+ description: markdown消息标题
47
+ - name: msg.markdown.text
48
+ type: string
31
49
  required: true
32
50
  position: body
33
- description: 消息内容,支持text、link、markdown三种类型
51
+ description: markdown消息内容
34
52
 
35
53
  # 获取工作通知消息的发送结果
36
54
  - name: getSendResult
@@ -39,6 +57,12 @@ tools:
39
57
  method: POST
40
58
  url: https://oapi.dingtalk.com/topapi/message/corpconversation/getsendresult
41
59
  args:
60
+ - name: agent_id
61
+ type: number
62
+ required: true
63
+ position: body
64
+ description: 发送消息时使用的微应用的AgentID
65
+ system: DINGTALK_AGENT_ID
42
66
  - name: task_id
43
67
  type: number
44
68
  required: true
@@ -52,6 +76,12 @@ tools:
52
76
  method: POST
53
77
  url: https://oapi.dingtalk.com/topapi/message/corpconversation/getsendprogress
54
78
  args:
79
+ - name: agent_id
80
+ type: number
81
+ required: true
82
+ position: body
83
+ description: 发送消息时使用的微应用的AgentID
84
+ system: DINGTALK_AGENT_ID
55
85
  - name: task_id
56
86
  type: number
57
87
  required: true
@@ -65,21 +95,15 @@ tools:
65
95
  method: POST
66
96
  url: https://oapi.dingtalk.com/topapi/message/corpconversation/recall
67
97
  args:
98
+ - name: agent_id
99
+ type: number
100
+ required: true
101
+ position: body
102
+ description: 发送消息时使用的微应用的AgentID
103
+ system: DINGTALK_AGENT_ID
68
104
  - name: msg_task_id
69
105
  type: number
70
106
  required: true
71
107
  position: body
72
108
  description: 要撤回的消息任务ID
73
109
 
74
- # 获取消息模板(内置功能)
75
- - name: getMessageTemplates
76
- description: 获取所有支持的消息类型模板和示例,帮助用户快速了解消息格式
77
- requestTemplate:
78
- method: LOCAL
79
- url: local://templates
80
- args:
81
- - name: msgtype
82
- type: string
83
- required: false
84
- position: query
85
- description: 指定消息类型(text、link、markdown),不指定则返回所有类型
@@ -79,11 +79,6 @@ tools:
79
79
  description: 会话ID。
80
80
  position: body
81
81
  required: true
82
- - name: coolAppCode
83
- type: string
84
- description: 群聊酷应用编码。
85
- system: COOL_APP_CODE
86
- position: body
87
82
  - name: msgKey
88
83
  type: string
89
84
  description: 消息模板key,默认为"sampleMarkdown"。
@@ -93,10 +88,11 @@ tools:
93
88
  not_need_model_transform: true
94
89
  - name: msgParam
95
90
  type: string
96
- description: 消息内容,为markdown格式。
91
+ description: 消息内容,格式为JSON字符串,必须遵循此格式 {"title":"title", "text":"markdown内容"}
97
92
  position: body
98
93
  required: true
99
94
 
95
+
100
96
  requestTemplate:
101
97
  url: https://api.dingtalk.com/v1.0/robot/groupMessages/send
102
98
  method: POST
@@ -162,7 +158,7 @@ tools:
162
158
  not_need_model_transform: true
163
159
  - name: msgParam
164
160
  type: string
165
- description: 消息内容,为markdown格式。
161
+ description: 消息内容,格式为JSON字符串,必须遵循此格式 {"title":"title", "text":"markdown内容"}
166
162
  position: body
167
163
  required: true
168
164
 
@@ -1,7 +1,7 @@
1
1
  server:
2
- name: dingtalk-servicewindow
2
+ name: dingtalk-service-window
3
3
  version: 1.0.0
4
- description: 钉钉服务窗
4
+ description: DingTalk ServiceWindow MCP Server with Message Builder
5
5
  securitySchemes:
6
6
  - id: DingTalkAuth
7
7
  type: apiKey
@@ -10,11 +10,6 @@ server:
10
10
 
11
11
  # 注意:当前版本的工具定义在代码中,此配置文件为预留扩展
12
12
  tools:
13
- - name: buildServiceWindowMessage
14
- description: "构建服务窗消息体,支持文本、链接、markdown、卡片等多种类型"
15
- requestTemplate:
16
- method: POST
17
- url: "internal://message-builder"
18
13
 
19
14
  - name: sendServiceWindowMessage
20
15
  description: "发送服务窗单人消息"
@@ -23,7 +18,30 @@ tools:
23
18
  url: "https://api.dingtalk.com/v1.0/crm/officialAccounts/oToMessages/send"
24
19
  security:
25
20
  id: DingTalkAuth
26
-
21
+ args:
22
+ - name: userId
23
+ description: 用户userId
24
+ type: string
25
+ required: true
26
+ position: body
27
+ - name: accountId
28
+ description: 服务窗帐号ID
29
+ type: string
30
+ required: true
31
+ position: body
32
+ - name: messageTitle
33
+ description: 消息标题
34
+ type: string
35
+ required: true
36
+ position: body
37
+ - name: messageContent
38
+ description: markdown格式的消息内容
39
+ type: string
40
+ required: true
41
+ position: body
42
+
43
+
44
+
27
45
  - name: batchSendServiceWindowMessage
28
46
  description: "批量发送服务窗消息"
29
47
  requestTemplate:
@@ -31,79 +49,98 @@ tools:
31
49
  url: "https://api.dingtalk.com/v1.0/crm/officialAccounts/oToMessages/batchSend"
32
50
  security:
33
51
  id: DingTalkAuth
52
+ args:
53
+ - name: userIdList
54
+ description: 用户userId
55
+ type: array
56
+ required: false
57
+ position: body
58
+ items:
59
+ type: string
60
+ - name: accountId
61
+ description: 服务窗帐号ID
62
+ type: string
63
+ required: true
64
+ position: body
65
+ - name: messageTitle
66
+ description: 消息标题
67
+ type: string
68
+ required: true
69
+ position: body
70
+ - name: messageContent
71
+ description: markdown格式的消息内容
72
+ type: string
73
+ required: true
74
+ position: body
34
75
 
35
- - name: getServiceWindowFollower
76
+ - name: getServiceWindowFollowerInfo
36
77
  description: "获取关注服务窗的单个用户信息"
37
78
  requestTemplate:
38
79
  method: GET
39
- url: "https://api.dingtalk.com/v1.0/link/followers/infos"
80
+ url: "https://api.dingtalk.com/v1.0/link/followers/infos?userId={String}&accountId={String}"
40
81
  security:
41
82
  id: DingTalkAuth
83
+ args:
84
+ - name: accountId
85
+ description: 服务窗帐号ID
86
+ type: string
87
+ required: true
88
+ position: query
89
+ - name: userId
90
+ description: 关注服务窗用户的userId
91
+ type: string
92
+ required: true
93
+ position: query
42
94
 
43
- - name: listServiceWindowFollowers
44
- description: "分页获取关注服务窗的用户列表"
95
+ - name: listServiceWindowFollowersInfo
96
+ description: "批量获取关注服务窗用户信息"
45
97
  requestTemplate:
46
98
  method: GET
47
- url: "https://api.dingtalk.com/v1.0/link/followers"
99
+ url: "https://api.dingtalk.com/v1.0/link/followers?accountId={String}&nextToken={String}&maxResults={String}"
48
100
  security:
49
101
  id: DingTalkAuth
102
+ args:
103
+ - name: accountId
104
+ description: 服务窗帐号ID
105
+ type: string
106
+ required: true
107
+ position: query
108
+ - name: nextToken
109
+ description: 分页游标
110
+ type: string
111
+ required: false
112
+ position: query
113
+ - name: maxResults
114
+ description: 每页最大条目数,最大值100
115
+ type: string
116
+ required: false
117
+ position: query
118
+
50
119
 
51
120
  - name: getUserFollowStatus
52
121
  description: "获取用户的服务窗关注状态"
53
122
  requestTemplate:
54
123
  method: GET
55
- url: "https://api.dingtalk.com/v1.0/link/followers/status"
124
+ url: "https://api.dingtalk.com/v1.0/link/followers/statuses?accountId={String}&userId={String}"
56
125
  security:
57
126
  id: DingTalkAuth
127
+ args:
128
+ - name: accountId
129
+ description: 服务窗帐号ID
130
+ type: string
131
+ required: true
132
+ position: query
133
+ - name: userId
134
+ description: 关注服务窗用户的userId
135
+ type: string
136
+ required: true
137
+ position: query
138
+
58
139
 
59
140
  - name: listServiceWindows
60
- description: "获取企业下的服务窗列表"
141
+ description: "获取企业下的服务窗列表,以获取服务窗accountId"
61
142
  requestTemplate:
62
143
  method: GET
63
144
  url: "https://api.dingtalk.com/v1.0/link/accounts"
64
145
  security:
65
- id: DingTalkAuth
66
-
67
- # 消息类型定义
68
- messageTypes:
69
- text:
70
- description: "纯文本消息"
71
- maxLength: 500
72
-
73
- link:
74
- description: "链接消息"
75
- fields:
76
- title:
77
- maxLength: 128
78
- required: true
79
- text:
80
- maxLength: 500
81
- required: true
82
- messageUrl:
83
- pattern: "^https?://.+"
84
- required: true
85
- picUrl:
86
- required: true
87
-
88
- markdown:
89
- description: "Markdown格式消息"
90
- fields:
91
- title:
92
- maxLength: 30
93
- required: true
94
- text:
95
- maxLength: 500
96
- required: true
97
-
98
- actionCard:
99
- description: "卡片消息"
100
- fields:
101
- title:
102
- maxLength: 30
103
- required: true
104
- markdown:
105
- maxLength: 1000
106
- required: true
107
- cardType:
108
- enum: ["single", "independent"]
109
- required: true
146
+ id: DingTalkAuth
@@ -34,6 +34,7 @@ export declare class DingTalkMCPServer {
34
34
  private executeTool;
35
35
  private buildUrl;
36
36
  private buildHeaders;
37
+ private processMultiParam;
37
38
  private buildBody;
38
39
  run(): Promise<void>;
39
40
  }
@@ -1,11 +1,14 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { ServiceWindowMessageBuilder } from './utils/messageBuilder.js'
4
5
  import axios from 'axios';
5
6
  import * as yaml from 'js-yaml';
6
7
  import fs from 'fs';
7
8
  import path from 'path';
8
9
  import { fileURLToPath } from 'url';
10
+ import {ifError} from "node:assert";
11
+ import {resolveObjectURL} from "node:buffer";
9
12
  const __filename = fileURLToPath(import.meta.url);
10
13
  const __dirname = path.dirname(__filename);
11
14
  export class DingTalkMCPServer {
@@ -319,6 +322,9 @@ export class DingTalkMCPServer {
319
322
  };
320
323
  }
321
324
  catch (error) {
325
+ if (this.debug){
326
+ console.log(error)
327
+ }
322
328
  let errorMessage = error.message;
323
329
  if (axios.isAxiosError(error) && error.response) {
324
330
  // 如果是token相关错误,清除缓存
@@ -375,7 +381,7 @@ export class DingTalkMCPServer {
375
381
  return finalQuery ? `${baseUrl}?${finalQuery}` : baseUrl;
376
382
  }
377
383
  // 特殊处理oapi.dingtalk.com接口且配置了security(自定义机器人发消息不使用这个),将access_token作为URL参数传递
378
- if (url.includes('oapi.dingtalk.com') && tool.requestTemplate.security && this.accessToken) {
384
+ if (url.includes('oapi.dingtalk.com') && this.accessToken) {
379
385
  const separator = url.includes('?') ? '&' : '?';
380
386
  return `${url}${separator}access_token=${encodeURIComponent(this.accessToken)}`;
381
387
  }
@@ -396,10 +402,44 @@ export class DingTalkMCPServer {
396
402
  }
397
403
  return headers;
398
404
  }
405
+
406
+ processMultiParam(body, name, value){
407
+ let objParmas = name.split('.');
408
+ if (objParmas && objParmas.length > 1) {
409
+ if (objParmas.length == 2){
410
+ if (!body[objParmas[0]]){
411
+ body[objParmas[0]] = {};
412
+ }
413
+ body[objParmas[0]][objParmas[1]] = value;
414
+ }
415
+ if (objParmas.length == 3){
416
+ if (!body[objParmas[0]]){
417
+ body[objParmas[0]] = {};
418
+ }
419
+ if (!body[objParmas[0]][objParmas[1]]){
420
+ body[objParmas[0]][objParmas[1]] = {};
421
+ }
422
+ body[objParmas[0]][objParmas[1]][objParmas[2]] = value;
423
+ }
424
+ return true;
425
+ } else {
426
+ body[name] = value;
427
+ }
428
+ return false;
429
+ }
430
+
399
431
  buildBody(tool, args) {
400
432
  if (tool.requestTemplate.method === 'GET' || tool.requestTemplate.method === 'DELETE') {
401
433
  return undefined;
402
434
  }
435
+
436
+ if (tool.name === 'sendServiceWindowMessage'){
437
+ return ServiceWindowMessageBuilder.buildSendServiceWindowMarkdownBody(args);
438
+ }else if (tool.name === 'batchSendServiceWindowMessage'){
439
+ return ServiceWindowMessageBuilder.buildBatchSendServiceWindowMarkdownBody(args);
440
+ }
441
+
442
+
403
443
  const body = {};
404
444
  (tool.args || []).forEach(arg => {
405
445
  if (arg.position === 'body') {
@@ -413,7 +453,7 @@ export class DingTalkMCPServer {
413
453
  }
414
454
  // 不需要模型处理,直接取默认值赋值
415
455
  if (arg.not_need_model_transform){
416
- body[arg.name] = arg.default
456
+ this.processMultiParam(body, arg.name, arg.default);
417
457
  return;
418
458
  }
419
459
 
@@ -422,56 +462,40 @@ export class DingTalkMCPServer {
422
462
  return;
423
463
  }
424
464
  //打平Object类型参数,递归处理
425
- let objParmas = arg.name.split('.');
426
- if (objParmas && objParmas.length > 1) {
427
- if (objParmas.length == 2){
428
- if (!body[objParmas[0]]){
429
- body[objParmas[0]] = {};
430
- }
431
- body[objParmas[0]][objParmas[1]] = args[arg.name];
432
- }
433
- if (objParmas.length == 3){
434
- if (!body[objParmas[0]]){
435
- body[objParmas[0]] = {};
436
- }
437
- if (!body[objParmas[1]]){
438
- body[objParmas[1]] = {};
439
- }
440
- body[objParmas[0]][objParmas[1]][objParmas[2]] = args[arg.name];
441
- }
442
- return;
443
- }
465
+ this.processMultiParam(body, arg.name, args[arg.name]);
444
466
 
445
-
446
- // userIds参数直接传递为数组类型,保证请求体为正确的JSON对象
447
- if (arg.name === 'userIds' && Array.isArray(args[arg.name])) {
448
- body[arg.name] = args[arg.name];
449
- }
450
- // 搜索用户API参数名称转换,从snake_case转为camelCase
451
- else if (tool.name === 'searchUser') {
452
- // Convert snake_case to camelCase for the search user API
453
- const camelCaseName = arg.name.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
454
- body[camelCaseName] = args[arg.name];
455
- }
456
- else {
457
- body[arg.name] = args[arg.name];
458
- }
467
+ // // userIds参数直接传递为数组类型,保证请求体为正确的JSON对象
468
+ // if (arg.name === 'userIds' && Array.isArray(args[arg.name])) {
469
+ // body[arg.name] = args[arg.name];
470
+ // }
471
+ // // 搜索用户API参数名称转换,从snake_case转为camelCase
472
+ // else if (tool.name === 'searchUser') {
473
+ // // Convert snake_case to camelCase for the search user API
474
+ // const camelCaseName = arg.name.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
475
+ // body[camelCaseName] = args[arg.name];
476
+ // }
477
+ // else {
478
+ // body[arg.name] = args[arg.name];
479
+ // }
459
480
  }
460
481
  });
461
482
  // 为searchUser/searchDepartment添加默认的分页参数
462
483
 
463
484
  // 设置默认的offset为0
464
- if (body.offset === undefined) {
465
- body.offset = 0;
466
- }
467
- // 设置默认的size为20
468
- if (body.size === undefined) {
469
- body.size = 20;
485
+ if (tool.name === 'searchDepartment' || tool.name === 'searchUser'){
486
+ if (body.offset === undefined) {
487
+ body.offset = 0;
488
+ }
489
+ // 设置默认的size为20
490
+ if (body.size === undefined) {
491
+ body.size = 20;
492
+ }
470
493
  }
471
494
  console.error(`SearchUser API with params: offset=${body.offset}, size=${body.size}`);
472
495
 
473
496
  return Object.keys(body).length > 0 ? body : undefined;
474
497
  }
498
+
475
499
  async run() {
476
500
  const transport = new StdioServerTransport();
477
501
  await this.server.connect(transport);
package/dist/cli.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { DingTalkMCPServer } from './DingTalkMCPServer.js';
3
+ import {json} from "node:stream/consumers";
3
4
  async function main() {
4
5
  // 检查环境变量
5
6
  const appId = process.env.DINGTALK_Client_ID;
6
7
  const appSecret = process.env.DINGTALK_Client_Secret;
7
8
  const accessToken = process.env.DINGTALK_ACCESS_TOKEN;
8
- process.env.ACTIVE_PROFILES = 'dingtalk-contacts,dingtalk-department'
9
+
9
10
  // 如果既没有AppKey/Secret也没有AccessToken,显示帮助信息
10
11
  if (!accessToken && (!appId || !appSecret)) {
11
12
  console.error('🔧 DingTalk MCP Server 配置指南');
@@ -50,6 +51,8 @@ async function main() {
50
51
  console.error('');
51
52
  process.exit(1);
52
53
  }
54
+
55
+
53
56
  }
54
57
  // 处理未捕获的异常
55
58
  process.on('uncaughtException', (error) => {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DingTalk Calendar MCP Server
3
- *
4
- * A TypeScript-based MCP server for DingTalk Calendar integration
3
+
4
+ * A TypeScript-based MCP server for DingTalk integration
5
5
  */
6
6
  export { DingTalkMCPServer } from './DingTalkMCPServer.js';
7
7
  export * from './types.js';
package/dist/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * DingTalk Calendar MCP Server Type Definitions
2
+ * DingTalk MCP Server Type Definitions
3
3
  */
4
4
  export {};
5
5
  //# sourceMappingURL=types.js.map