dingtalk-mcp 1.1.9 → 1.1.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.
- package/README.md +12 -18
- package/dingtalk_contacts_mcp_server.yaml +3 -0
- package/dingtalk_notable_mcp_server.yaml +404 -0
- package/dingtalk_report_mcp_server.yaml +1 -1
- package/dist/DingTalkMCPServer.js +79 -55
- package/dist/resources/notable-field-property.md +17 -0
- package/dist/resources/notable-record-values.md +18 -0
- package/dist/utils/DateUtils.js +29 -0
- package/dist/utils/file.js +14 -0
- package/dist/utils/localTools.js +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# 钉钉MCP Server
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
## 🚀 功能特性
|
|
6
5
|
- 钉钉通讯录
|
|
7
6
|
- 钉钉部门管理
|
|
@@ -13,8 +12,6 @@
|
|
|
13
12
|
- 钉钉工作通知
|
|
14
13
|
- 钉钉应用管理
|
|
15
14
|
- 钉钉服务窗
|
|
16
|
-
- 钉钉项目管理
|
|
17
|
-
- 钉钉日志
|
|
18
15
|
|
|
19
16
|
## 如何使用
|
|
20
17
|
```json
|
|
@@ -40,20 +37,18 @@
|
|
|
40
37
|
2. DINGTALK_Client_Secret
|
|
41
38
|
3. ACTIVE_PROFILES,激活哪些钉钉MCP服务,逗号风格,如果是ALL则激活全部。可选集合
|
|
42
39
|
|
|
43
|
-
| ProfileId
|
|
44
|
-
|
|
45
|
-
| dingtalk-contacts
|
|
46
|
-
| dingtalk-department
|
|
47
|
-
| dingtalk-robot-send-message | 钉钉机器人发消息/DING,默认激活 | 需要企业内机器人发送消息权限
|
|
48
|
-
| dingtalk-honor
|
|
49
|
-
| dingtalk-tasks
|
|
50
|
-
| dingtalk-calendar
|
|
51
|
-
| dingtalk-checkin
|
|
52
|
-
| dingtalk-notice
|
|
53
|
-
| dingtalk-app-manage
|
|
54
|
-
| dingtalk-service-window
|
|
55
|
-
| dingtalk-teambition | 钉钉项目管理 | Project.Project.Write.All Project.Project.Read.All Project.Task.Write.All Project.Task.Read.All |
|
|
56
|
-
| dingtalk-report | 钉钉日志 | qyapi_report_statistics qyapi_report_manage qyapi_report_query|
|
|
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.WriteTodo.Todo.Read |
|
|
47
|
+
| dingtalk-calendar | 钉钉日程 |
|
|
48
|
+
| dingtalk-checkin | 钉钉签到 |
|
|
49
|
+
| dingtalk-notice | 钉钉工作通知 |
|
|
50
|
+
| dingtalk-app-manage | 钉钉应用管理 | qyapi_microapp_manageqyapi_get_microapp_list |
|
|
51
|
+
| dingtalk-service-window | 钉钉服务窗 | |
|
|
57
52
|
|
|
58
53
|
4. ROBOT_CODE,用于发消息/DING的机器人Code
|
|
59
54
|
5. ROBOT_ACCESS_TOKEN,群自定义机器人ACCESS_TOKEN,用于自定义机器人发消息
|
|
@@ -69,7 +64,6 @@
|
|
|
69
64
|
|
|
70
65
|
## 📞 支持
|
|
71
66
|
|
|
72
|
-
- 帮助文档: https://open.dingtalk.com/document/ai-dev/dingtalk-server-api-mcp-overview
|
|
73
67
|
- 钉钉开放平台: https://open.dingtalk.com
|
|
74
68
|
- MCP协议: https://modelcontextprotocol.io
|
|
75
69
|
- 欢迎加入钉钉MCP交流群
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
server:
|
|
2
|
+
name: dingtalk-notable
|
|
3
|
+
description: 钉钉AI表格/多维表
|
|
4
|
+
tools:
|
|
5
|
+
- name: notableSupportedFieldInfo
|
|
6
|
+
description: AI表格/多维表支持的字段类型和额外属性
|
|
7
|
+
requestTemplate:
|
|
8
|
+
type: file
|
|
9
|
+
url: /resources/notable-field-property.md
|
|
10
|
+
|
|
11
|
+
- name: notableRecordValuesFormat
|
|
12
|
+
description: AI表格/多维表记录值格式
|
|
13
|
+
requestTemplate:
|
|
14
|
+
type: file
|
|
15
|
+
url: /resources/notable-record-values.md
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
- name: queryNotables
|
|
19
|
+
description: 根据名称查询AI表格/多维表
|
|
20
|
+
requestTemplate:
|
|
21
|
+
method: POST
|
|
22
|
+
url: https://api.dingtalk.com/v2.0/storage/dentries/search?operatorId=String
|
|
23
|
+
args:
|
|
24
|
+
- name: operatorId
|
|
25
|
+
description: 操作人的unionId
|
|
26
|
+
type: string
|
|
27
|
+
required: true
|
|
28
|
+
position: query
|
|
29
|
+
- name: keyword
|
|
30
|
+
description: AI表格/多维表名称
|
|
31
|
+
type: string
|
|
32
|
+
required: true
|
|
33
|
+
position: body
|
|
34
|
+
- name: option
|
|
35
|
+
description: 查询参数
|
|
36
|
+
type: object
|
|
37
|
+
required: true
|
|
38
|
+
position: body
|
|
39
|
+
items:
|
|
40
|
+
type: object
|
|
41
|
+
required:
|
|
42
|
+
- dentryCategories
|
|
43
|
+
- creatorIds
|
|
44
|
+
properties:
|
|
45
|
+
dentryCategories:
|
|
46
|
+
type: array
|
|
47
|
+
enum: [ "alidoc" ]
|
|
48
|
+
description: 文件类别
|
|
49
|
+
items:
|
|
50
|
+
type: string
|
|
51
|
+
creatorIds:
|
|
52
|
+
type: array
|
|
53
|
+
description: 创建者的userId
|
|
54
|
+
items:
|
|
55
|
+
type: string
|
|
56
|
+
nextToken:
|
|
57
|
+
type: string
|
|
58
|
+
description: 分页游标,第一次查询不需要此值,第二次查询需要,从本接口的返回值nextToken获取,如果返回值为空则说明没有更多数据
|
|
59
|
+
maxResults:
|
|
60
|
+
type: string
|
|
61
|
+
description: 分页大小,默认值10
|
|
62
|
+
# 数据表
|
|
63
|
+
- name: getNotableSheet
|
|
64
|
+
description: 获取AI表格/多维表的单个数据表
|
|
65
|
+
requestTemplate:
|
|
66
|
+
method: GET
|
|
67
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}?operatorId=String
|
|
68
|
+
args:
|
|
69
|
+
- name: operatorId
|
|
70
|
+
description: 操作人的unionId
|
|
71
|
+
type: string
|
|
72
|
+
required: true
|
|
73
|
+
position: query
|
|
74
|
+
- name: baseId
|
|
75
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
76
|
+
type: string
|
|
77
|
+
required: true
|
|
78
|
+
position: query
|
|
79
|
+
- name: sheetIdOrName
|
|
80
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
81
|
+
type: string
|
|
82
|
+
required: true
|
|
83
|
+
position: path
|
|
84
|
+
|
|
85
|
+
- name: getNotableAllSheets
|
|
86
|
+
description: 获取AI表格/多维表的所有数据表
|
|
87
|
+
requestTemplate:
|
|
88
|
+
method: GET
|
|
89
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets?operatorId=String
|
|
90
|
+
args:
|
|
91
|
+
- name: operatorId
|
|
92
|
+
description: 操作人的unionId
|
|
93
|
+
type: string
|
|
94
|
+
required: true
|
|
95
|
+
position: query
|
|
96
|
+
- name: baseId
|
|
97
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
98
|
+
type: string
|
|
99
|
+
required: true
|
|
100
|
+
position: query
|
|
101
|
+
|
|
102
|
+
- name: updateNotableSheetName
|
|
103
|
+
description: 更新AI表格/多维表的单个数据表的名称
|
|
104
|
+
requestTemplate:
|
|
105
|
+
method: PUT
|
|
106
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}?operatorId=String
|
|
107
|
+
args:
|
|
108
|
+
- name: operatorId
|
|
109
|
+
description: 操作人的unionId
|
|
110
|
+
type: string
|
|
111
|
+
required: true
|
|
112
|
+
position: query
|
|
113
|
+
- name: baseId
|
|
114
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
115
|
+
type: string
|
|
116
|
+
required: true
|
|
117
|
+
position: query
|
|
118
|
+
- name: sheetIdOrName
|
|
119
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
120
|
+
type: string
|
|
121
|
+
required: true
|
|
122
|
+
position: path
|
|
123
|
+
- name: name
|
|
124
|
+
description: 数据表名称
|
|
125
|
+
type: string
|
|
126
|
+
required: true
|
|
127
|
+
position: body
|
|
128
|
+
|
|
129
|
+
- name: createNotableSheet
|
|
130
|
+
description: 创建AI表格/多维表的数据表
|
|
131
|
+
requestTemplate:
|
|
132
|
+
method: POST
|
|
133
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets?operatorId=String
|
|
134
|
+
args:
|
|
135
|
+
- name: operatorId
|
|
136
|
+
description: 操作人的unionId
|
|
137
|
+
type: string
|
|
138
|
+
required: true
|
|
139
|
+
position: query
|
|
140
|
+
- name: baseId
|
|
141
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
142
|
+
type: string
|
|
143
|
+
required: true
|
|
144
|
+
position: query
|
|
145
|
+
- name: name
|
|
146
|
+
description: 数据表ID或数据表名称。
|
|
147
|
+
type: string
|
|
148
|
+
required: true
|
|
149
|
+
position: body
|
|
150
|
+
- name: fields
|
|
151
|
+
type: array
|
|
152
|
+
required: true
|
|
153
|
+
position: body
|
|
154
|
+
items:
|
|
155
|
+
type: object
|
|
156
|
+
properties:
|
|
157
|
+
name:
|
|
158
|
+
type: string
|
|
159
|
+
description: 字段名称
|
|
160
|
+
type:
|
|
161
|
+
type: string
|
|
162
|
+
description: 字段类型,参考「notableSupportedFieldInfo」Tool返回结果中的[类型]。
|
|
163
|
+
property:
|
|
164
|
+
type: Map<String, Any>
|
|
165
|
+
description: 字段属性,具体格式请参考「notableSupportedFieldInfo」Tool返回结果中的[属性]。
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
- name: deleteNotableSheet
|
|
169
|
+
description: 删除AI表格/多维表的单个数据表
|
|
170
|
+
requestTemplate:
|
|
171
|
+
method: DELETE
|
|
172
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}?operatorId=String
|
|
173
|
+
args:
|
|
174
|
+
- name: operatorId
|
|
175
|
+
description: 操作人的unionId
|
|
176
|
+
type: string
|
|
177
|
+
required: true
|
|
178
|
+
position: query
|
|
179
|
+
- name: baseId
|
|
180
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
181
|
+
type: string
|
|
182
|
+
required: true
|
|
183
|
+
position: query
|
|
184
|
+
- name: sheetIdOrName
|
|
185
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
186
|
+
type: string
|
|
187
|
+
required: true
|
|
188
|
+
position: path
|
|
189
|
+
|
|
190
|
+
# 记录
|
|
191
|
+
- name: listNotableRecords
|
|
192
|
+
description: 获取AI表格/多维表里指定数据表的多行记录
|
|
193
|
+
requestTemplate:
|
|
194
|
+
method: POST
|
|
195
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}/records/list?operatorId=String
|
|
196
|
+
args:
|
|
197
|
+
- name: operatorId
|
|
198
|
+
description: 操作人的unionId
|
|
199
|
+
type: string
|
|
200
|
+
required: true
|
|
201
|
+
position: query
|
|
202
|
+
- name: baseId
|
|
203
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
204
|
+
type: string
|
|
205
|
+
required: true
|
|
206
|
+
position: path
|
|
207
|
+
- name: sheetIdOrName
|
|
208
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
209
|
+
type: string
|
|
210
|
+
required: true
|
|
211
|
+
position: path
|
|
212
|
+
- name: maxResults
|
|
213
|
+
description: 每页记录数,最大100,默认100
|
|
214
|
+
type: integer
|
|
215
|
+
required: false
|
|
216
|
+
position: body
|
|
217
|
+
- name: nextToken
|
|
218
|
+
description: 分页游标
|
|
219
|
+
type: string
|
|
220
|
+
required: false
|
|
221
|
+
position: body
|
|
222
|
+
- name: filter
|
|
223
|
+
description: 过滤条件
|
|
224
|
+
type: object
|
|
225
|
+
required: true
|
|
226
|
+
position: body
|
|
227
|
+
items:
|
|
228
|
+
type: object
|
|
229
|
+
properties:
|
|
230
|
+
combination:
|
|
231
|
+
type: string
|
|
232
|
+
description: 条件组合方式,可选值:
|
|
233
|
+
|| and:同时满足所有条件
|
|
234
|
+
|| or:满足任一条件
|
|
235
|
+
conditions:
|
|
236
|
+
type: array
|
|
237
|
+
items:
|
|
238
|
+
type: object
|
|
239
|
+
properties:
|
|
240
|
+
field:
|
|
241
|
+
type: string
|
|
242
|
+
description: 字段ID或字段名。
|
|
243
|
+
operator:
|
|
244
|
+
type: string
|
|
245
|
+
description: 条件类型:equal | notEqual | greater | greaterEqual | less | lessEqual | contain | notContain | empty | notEmpty。
|
|
246
|
+
value:
|
|
247
|
+
type: string
|
|
248
|
+
description: 字段值
|
|
249
|
+
required:
|
|
250
|
+
- field
|
|
251
|
+
- operator
|
|
252
|
+
- value
|
|
253
|
+
|
|
254
|
+
- name: getNotableRecord
|
|
255
|
+
description: 获取AI表格/多维表里指定数据表的单行记录
|
|
256
|
+
requestTemplate:
|
|
257
|
+
method: GET
|
|
258
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}/records/{recordId}?operatorId=String
|
|
259
|
+
args:
|
|
260
|
+
- name: operatorId
|
|
261
|
+
description: 操作人的unionId
|
|
262
|
+
type: string
|
|
263
|
+
required: true
|
|
264
|
+
position: query
|
|
265
|
+
- name: baseId
|
|
266
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
267
|
+
type: string
|
|
268
|
+
required: true
|
|
269
|
+
position: path
|
|
270
|
+
- name: sheetIdOrName
|
|
271
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
272
|
+
type: string
|
|
273
|
+
required: true
|
|
274
|
+
position: path
|
|
275
|
+
- name: recordId
|
|
276
|
+
description: 记录ID
|
|
277
|
+
type: string
|
|
278
|
+
required: true
|
|
279
|
+
position: path
|
|
280
|
+
|
|
281
|
+
- name: deleteNotableRecords
|
|
282
|
+
description: 删除AI表格/多维表里指定数据表的多行记录
|
|
283
|
+
requestTemplate:
|
|
284
|
+
method: POST
|
|
285
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}/records/delete?operatorId=String
|
|
286
|
+
args:
|
|
287
|
+
- name: operatorId
|
|
288
|
+
description: 操作人的unionId
|
|
289
|
+
type: string
|
|
290
|
+
required: true
|
|
291
|
+
position: query
|
|
292
|
+
- name: baseId
|
|
293
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
294
|
+
type: string
|
|
295
|
+
required: true
|
|
296
|
+
position: path
|
|
297
|
+
- name: sheetIdOrName
|
|
298
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
299
|
+
type: string
|
|
300
|
+
required: true
|
|
301
|
+
position: path
|
|
302
|
+
- name: recordIds
|
|
303
|
+
description: 记录ID数组
|
|
304
|
+
type: array
|
|
305
|
+
required: true
|
|
306
|
+
position: body
|
|
307
|
+
items:
|
|
308
|
+
type: string
|
|
309
|
+
|
|
310
|
+
- name: insertNotableRecords
|
|
311
|
+
description: 在AI表格/多维表里指定数据表中新增行记录
|
|
312
|
+
requestTemplate:
|
|
313
|
+
method: POST
|
|
314
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}/records?operatorId=String
|
|
315
|
+
args:
|
|
316
|
+
- name: operatorId
|
|
317
|
+
description: 操作人的unionId
|
|
318
|
+
type: string
|
|
319
|
+
required: true
|
|
320
|
+
position: query
|
|
321
|
+
- name: baseId
|
|
322
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
323
|
+
type: string
|
|
324
|
+
required: true
|
|
325
|
+
position: path
|
|
326
|
+
- name: sheetIdOrName
|
|
327
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
328
|
+
type: string
|
|
329
|
+
required: true
|
|
330
|
+
position: path
|
|
331
|
+
- name: records
|
|
332
|
+
description: 记录数组
|
|
333
|
+
type: array
|
|
334
|
+
required: true
|
|
335
|
+
position: body
|
|
336
|
+
items:
|
|
337
|
+
type: object
|
|
338
|
+
description: 该行记录中各字段的值,具体格式请参考「notableRecordValuesFormat」Tool的结果。
|
|
339
|
+
properties:
|
|
340
|
+
fields:
|
|
341
|
+
type: object
|
|
342
|
+
description: 字段键值对,key为字段ID,value为字段设置值,格式需参考「notableRecordValuesFormat」Tool的结果。
|
|
343
|
+
additionalProperties:
|
|
344
|
+
type: [ string, number, boolean, object, array] # 值可以是这些类型中的任意一种
|
|
345
|
+
|
|
346
|
+
# 字段
|
|
347
|
+
- name: getNotableAllFields
|
|
348
|
+
description: 获取AI表格/多维表里指定数据表的所有字段
|
|
349
|
+
requestTemplate:
|
|
350
|
+
method: GET
|
|
351
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}/fields?operatorId=String
|
|
352
|
+
args:
|
|
353
|
+
- name: operatorId
|
|
354
|
+
description: 操作人的unionId
|
|
355
|
+
type: string
|
|
356
|
+
required: true
|
|
357
|
+
position: query
|
|
358
|
+
- name: baseId
|
|
359
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
360
|
+
type: string
|
|
361
|
+
required: true
|
|
362
|
+
position: path
|
|
363
|
+
- name: sheetIdOrName
|
|
364
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
365
|
+
type: string
|
|
366
|
+
required: true
|
|
367
|
+
position: path
|
|
368
|
+
|
|
369
|
+
- name: createNotableField
|
|
370
|
+
description: 在AI表格/多维表里指定数据表中创建字段
|
|
371
|
+
requestTemplate:
|
|
372
|
+
method: POST
|
|
373
|
+
url: https://api.dingtalk.com/v1.0/notable/bases/{baseId}/sheets/{sheetIdOrName}/fields?operatorId=String
|
|
374
|
+
args:
|
|
375
|
+
- name: operatorId
|
|
376
|
+
description: 操作人的unionId
|
|
377
|
+
type: string
|
|
378
|
+
required: true
|
|
379
|
+
position: query
|
|
380
|
+
- name: baseId
|
|
381
|
+
description: AI表格/多维表ID,从「根据名称查询AI表格/多维表」Tool中的结果字段「dentryUuid」获取。
|
|
382
|
+
type: string
|
|
383
|
+
required: true
|
|
384
|
+
position: path
|
|
385
|
+
- name: sheetIdOrName
|
|
386
|
+
description: 数据表ID或数据表名称。数据表ID可以通过调用「getNotableAllSheets」获取
|
|
387
|
+
type: string
|
|
388
|
+
required: true
|
|
389
|
+
position: path
|
|
390
|
+
- name: name
|
|
391
|
+
description: 字段名称
|
|
392
|
+
type: string
|
|
393
|
+
required: true
|
|
394
|
+
position: body
|
|
395
|
+
- name: type
|
|
396
|
+
description: 字段类型,需要参考「notableSupportedFieldInfo」Tool的内容
|
|
397
|
+
type: string
|
|
398
|
+
required: true
|
|
399
|
+
position: body
|
|
400
|
+
- name: property
|
|
401
|
+
description: 字段属性,,需要参考「notableSupportedFieldInfo」Tool的对不同类型字段的属性描述。
|
|
402
|
+
type: object
|
|
403
|
+
required: false
|
|
404
|
+
position: body
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import {Server} from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import {CallToolRequestSchema, ListToolsRequestSchema} from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import {ServiceWindowMessageBuilder} from './utils/messageBuilder.js'
|
|
5
|
+
import {LogMessageBuilder} from './utils/logBuilder.js'
|
|
6
|
+
import {LocalTools} from './utils/localTools.js'
|
|
6
7
|
import axios from 'axios';
|
|
7
8
|
import * as yaml from 'js-yaml';
|
|
8
9
|
import fs from 'fs';
|
|
9
10
|
import path from 'path';
|
|
10
|
-
import {
|
|
11
|
+
import {fileURLToPath} from 'url';
|
|
11
12
|
import {ifError} from "node:assert";
|
|
12
13
|
import {resolveObjectURL} from "node:buffer";
|
|
14
|
+
|
|
13
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
16
|
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
15
20
|
export class DingTalkMCPServer {
|
|
16
21
|
constructor() {
|
|
17
22
|
this.accessToken = null;
|
|
@@ -26,7 +31,7 @@ export class DingTalkMCPServer {
|
|
|
26
31
|
this.tokenCacheFile = path.join(__dirname, '..', '.dingtalk_token_cache.json');
|
|
27
32
|
this.loadConfig(path.join(__dirname, '..'));
|
|
28
33
|
// 测试状态
|
|
29
|
-
if (process.env.STAGING){
|
|
34
|
+
if (process.env.STAGING) {
|
|
30
35
|
this.loadConfig(path.join(__dirname, '../staging'));
|
|
31
36
|
}
|
|
32
37
|
this.server = new Server({
|
|
@@ -40,12 +45,13 @@ export class DingTalkMCPServer {
|
|
|
40
45
|
this.loadTokenCache();
|
|
41
46
|
this.setupHandlers();
|
|
42
47
|
}
|
|
48
|
+
|
|
43
49
|
loadConfig(dirname) {
|
|
44
50
|
try {
|
|
45
51
|
//激活的profile
|
|
46
52
|
const profiles = process.env.ACTIVE_PROFILES;
|
|
47
53
|
let profiles_list = [];
|
|
48
|
-
if (profiles){
|
|
54
|
+
if (profiles) {
|
|
49
55
|
profiles_list = profiles.split(',');
|
|
50
56
|
}
|
|
51
57
|
|
|
@@ -64,7 +70,7 @@ export class DingTalkMCPServer {
|
|
|
64
70
|
|
|
65
71
|
(config.tools || []).forEach(tool => {
|
|
66
72
|
if (this.tools.includes(tool.name)) {
|
|
67
|
-
throw new Error('Dulipict tool name: '+ tool.name);
|
|
73
|
+
throw new Error('Dulipict tool name: ' + tool.name);
|
|
68
74
|
}
|
|
69
75
|
this.tools.push(tool);
|
|
70
76
|
});
|
|
@@ -75,18 +81,19 @@ export class DingTalkMCPServer {
|
|
|
75
81
|
|
|
76
82
|
}
|
|
77
83
|
);
|
|
84
|
+
|
|
78
85
|
if (this.tools && this.tools.length > 0) {
|
|
79
86
|
console.error(`Loaded ${this.tools.length} tools from config`);
|
|
80
|
-
console.error(`Tools:\r\n${this.tools.map(t => t.name + ', '
|
|
87
|
+
console.error(`Tools:\r\n${this.tools.map(t => t.name + ', ' + t.description).join('\r\n')}`);
|
|
81
88
|
}
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
89
|
+
} catch (error) {
|
|
84
90
|
const err = error;
|
|
85
91
|
console.error('Failed to load config:', err.message);
|
|
86
92
|
this.tools = [];
|
|
87
93
|
throw error;
|
|
88
94
|
}
|
|
89
95
|
}
|
|
96
|
+
|
|
90
97
|
/**
|
|
91
98
|
* 加载本地缓存的access_token
|
|
92
99
|
*/
|
|
@@ -101,22 +108,20 @@ export class DingTalkMCPServer {
|
|
|
101
108
|
this.accessToken = cacheData.access_token;
|
|
102
109
|
console.error('Loaded valid access token from cache');
|
|
103
110
|
console.error(`Token expires at: ${new Date(cacheData.expires_at).toISOString()}`);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
111
|
+
} else {
|
|
106
112
|
console.error('Cached token expired, will refresh when needed');
|
|
107
113
|
this.clearTokenCache();
|
|
108
114
|
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
115
|
+
} else {
|
|
111
116
|
console.error('No token cache found');
|
|
112
117
|
}
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
118
|
+
} catch (error) {
|
|
115
119
|
const err = error;
|
|
116
120
|
console.error('Failed to load token cache:', err.message);
|
|
117
121
|
this.clearTokenCache();
|
|
118
122
|
}
|
|
119
123
|
}
|
|
124
|
+
|
|
120
125
|
/**
|
|
121
126
|
* 检查token缓存是否有效(未过期)
|
|
122
127
|
*/
|
|
@@ -129,6 +134,7 @@ export class DingTalkMCPServer {
|
|
|
129
134
|
const now = Date.now();
|
|
130
135
|
return now < (cacheData.expires_at - bufferTime);
|
|
131
136
|
}
|
|
137
|
+
|
|
132
138
|
/**
|
|
133
139
|
* 保存access_token到本地缓存
|
|
134
140
|
*/
|
|
@@ -146,12 +152,12 @@ export class DingTalkMCPServer {
|
|
|
146
152
|
fs.writeFileSync(this.tokenCacheFile, JSON.stringify(this.tokenCacheData, null, 2));
|
|
147
153
|
console.error('Access token saved to cache');
|
|
148
154
|
console.error(`Token expires at: ${new Date(expiresAt).toISOString()}`);
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
155
|
+
} catch (error) {
|
|
151
156
|
const err = error;
|
|
152
157
|
console.error('Failed to save token cache:', err.message);
|
|
153
158
|
}
|
|
154
159
|
}
|
|
160
|
+
|
|
155
161
|
/**
|
|
156
162
|
* 清除token缓存
|
|
157
163
|
*/
|
|
@@ -162,12 +168,12 @@ export class DingTalkMCPServer {
|
|
|
162
168
|
console.error('Token cache cleared');
|
|
163
169
|
}
|
|
164
170
|
this.tokenCacheData = null;
|
|
165
|
-
}
|
|
166
|
-
catch (error) {
|
|
171
|
+
} catch (error) {
|
|
167
172
|
const err = error;
|
|
168
173
|
console.error('Failed to clear token cache:', err.message);
|
|
169
174
|
}
|
|
170
175
|
}
|
|
176
|
+
|
|
171
177
|
/**
|
|
172
178
|
* 获取有效的access_token(优先使用缓存)
|
|
173
179
|
*/
|
|
@@ -185,6 +191,7 @@ export class DingTalkMCPServer {
|
|
|
185
191
|
console.error('Token cache invalid or missing, refreshing...');
|
|
186
192
|
return await this.refreshAccessToken();
|
|
187
193
|
}
|
|
194
|
+
|
|
188
195
|
async refreshAccessToken() {
|
|
189
196
|
if (!this.appId || !this.appSecret) {
|
|
190
197
|
throw new Error('DINGTALK_APP_ID and DINGTALK_APP_SECRET are required');
|
|
@@ -205,26 +212,24 @@ export class DingTalkMCPServer {
|
|
|
205
212
|
this.saveTokenCache(this.accessToken, expiresIn);
|
|
206
213
|
console.error('Access token refreshed successfully');
|
|
207
214
|
return this.accessToken;
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
215
|
+
} else {
|
|
210
216
|
throw new Error(`Token refresh failed: ${response.data.errmsg} (errcode: ${response.data.errcode})`);
|
|
211
217
|
}
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
218
|
+
} catch (error) {
|
|
214
219
|
// 刷新失败时清除缓存
|
|
215
220
|
this.clearTokenCache();
|
|
216
221
|
if (axios.isAxiosError(error) && error.response) {
|
|
217
222
|
throw new Error(`Failed to refresh access token: HTTP ${error.response.status} - ${JSON.stringify(error.response.data)}`);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
223
|
+
} else {
|
|
220
224
|
const err = error;
|
|
221
225
|
throw new Error(`Failed to refresh access token: ${err.message}`);
|
|
222
226
|
}
|
|
223
227
|
}
|
|
224
228
|
}
|
|
229
|
+
|
|
225
230
|
setupHandlers() {
|
|
226
231
|
// 列出可用工具
|
|
227
|
-
if (this.debug){
|
|
232
|
+
if (this.debug) {
|
|
228
233
|
const tools = this.tools.map(tool => ({
|
|
229
234
|
name: tool.name,
|
|
230
235
|
description: tool.description,
|
|
@@ -248,19 +253,22 @@ export class DingTalkMCPServer {
|
|
|
248
253
|
}
|
|
249
254
|
}));
|
|
250
255
|
|
|
251
|
-
return {
|
|
256
|
+
return {tools};
|
|
252
257
|
});
|
|
253
258
|
// 执行工具调用
|
|
254
259
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
255
|
-
const {
|
|
260
|
+
const {name, arguments: args} = request.params;
|
|
261
|
+
|
|
256
262
|
const result = await this.executeTool(name, args || {});
|
|
257
263
|
// 转换为 MCP SDK 期望的格式
|
|
258
264
|
return {
|
|
259
265
|
content: result.content,
|
|
260
266
|
isError: result.isError
|
|
261
267
|
};
|
|
268
|
+
|
|
262
269
|
});
|
|
263
270
|
}
|
|
271
|
+
|
|
264
272
|
generateSchema(args) {
|
|
265
273
|
const schema = {};
|
|
266
274
|
args.forEach(arg => {
|
|
@@ -276,12 +284,13 @@ export class DingTalkMCPServer {
|
|
|
276
284
|
type: arg.type,
|
|
277
285
|
description: arg.description
|
|
278
286
|
};
|
|
279
|
-
if ((arg.type === 'array' || arg.type === 'object'
|
|
287
|
+
if ((arg.type === 'array' || arg.type === 'object') && arg.items) {
|
|
280
288
|
schema[arg.name].items = arg.items;
|
|
281
289
|
}
|
|
282
290
|
});
|
|
283
291
|
return schema;
|
|
284
292
|
}
|
|
293
|
+
|
|
285
294
|
async executeTool(toolName, args) {
|
|
286
295
|
const tool = this.tools.find(t => t.name === toolName);
|
|
287
296
|
if (!tool) {
|
|
@@ -294,6 +303,15 @@ export class DingTalkMCPServer {
|
|
|
294
303
|
};
|
|
295
304
|
}
|
|
296
305
|
try {
|
|
306
|
+
if (LocalTools.isLocalTool(toolName)){
|
|
307
|
+
return {
|
|
308
|
+
content: [{
|
|
309
|
+
type: 'text',
|
|
310
|
+
text: JSON.stringify(LocalTools.callTool(tool), null, 2)
|
|
311
|
+
}]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
297
315
|
// 获取有效的访问令牌(使用缓存机制)
|
|
298
316
|
const accessToken = await this.getValidAccessToken();
|
|
299
317
|
if (!accessToken) {
|
|
@@ -317,18 +335,18 @@ export class DingTalkMCPServer {
|
|
|
317
335
|
data: body,
|
|
318
336
|
timeout: 30000
|
|
319
337
|
});
|
|
320
|
-
if (this.debug){
|
|
338
|
+
if (this.debug) {
|
|
321
339
|
console.log(response)
|
|
322
340
|
}
|
|
341
|
+
|
|
323
342
|
return {
|
|
324
343
|
content: [{
|
|
325
344
|
type: 'text',
|
|
326
345
|
text: JSON.stringify(response.data, null, 2)
|
|
327
346
|
}]
|
|
328
347
|
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (this.debug){
|
|
348
|
+
} catch (error) {
|
|
349
|
+
if (this.debug) {
|
|
332
350
|
console.log(error)
|
|
333
351
|
}
|
|
334
352
|
let errorMessage = error.message;
|
|
@@ -352,6 +370,7 @@ export class DingTalkMCPServer {
|
|
|
352
370
|
};
|
|
353
371
|
}
|
|
354
372
|
}
|
|
373
|
+
|
|
355
374
|
buildUrl(template, args) {
|
|
356
375
|
let url = template;
|
|
357
376
|
// 替换路径参数
|
|
@@ -373,12 +392,11 @@ export class DingTalkMCPServer {
|
|
|
373
392
|
if (args[key] !== undefined) {
|
|
374
393
|
queryParams.set(key, String(args[key]));
|
|
375
394
|
}
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
395
|
+
} else {
|
|
378
396
|
// 处理系统参数,如自动化机器人access_token
|
|
379
|
-
if (process.env[value]){
|
|
397
|
+
if (process.env[value]) {
|
|
380
398
|
queryParams.set(key, process.env[value]);
|
|
381
|
-
}else {
|
|
399
|
+
} else {
|
|
382
400
|
queryParams.set(key, value);
|
|
383
401
|
}
|
|
384
402
|
}
|
|
@@ -393,12 +411,17 @@ export class DingTalkMCPServer {
|
|
|
393
411
|
}
|
|
394
412
|
return url;
|
|
395
413
|
}
|
|
414
|
+
|
|
396
415
|
buildHeaders(tool) {
|
|
416
|
+
if (tool.requestTemplate.url && !tool.requestTemplate.url.includes('dingtalk.com')) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
397
420
|
const headers = {
|
|
398
421
|
'Content-Type': 'application/json'
|
|
399
422
|
};
|
|
400
423
|
// 只有对新版API (api.dingtalk.com) 才添加token到header
|
|
401
|
-
if (tool.requestTemplate.url &&
|
|
424
|
+
if (tool.requestTemplate.url && tool.requestTemplate.url.includes('https://api.dingtalk.com') && this.accessToken) {
|
|
402
425
|
headers['x-acs-dingtalk-access-token'] = this.accessToken;
|
|
403
426
|
}
|
|
404
427
|
if (tool.requestTemplate.headers) {
|
|
@@ -409,20 +432,20 @@ export class DingTalkMCPServer {
|
|
|
409
432
|
return headers;
|
|
410
433
|
}
|
|
411
434
|
|
|
412
|
-
processMultiParam(body, name, value){
|
|
435
|
+
processMultiParam(body, name, value) {
|
|
413
436
|
let objParmas = name.split('.');
|
|
414
437
|
if (objParmas && objParmas.length > 1) {
|
|
415
|
-
if (objParmas.length == 2){
|
|
416
|
-
if (!body[objParmas[0]]){
|
|
438
|
+
if (objParmas.length == 2) {
|
|
439
|
+
if (!body[objParmas[0]]) {
|
|
417
440
|
body[objParmas[0]] = {};
|
|
418
441
|
}
|
|
419
442
|
body[objParmas[0]][objParmas[1]] = value;
|
|
420
443
|
}
|
|
421
|
-
if (objParmas.length == 3){
|
|
422
|
-
if (!body[objParmas[0]]){
|
|
444
|
+
if (objParmas.length == 3) {
|
|
445
|
+
if (!body[objParmas[0]]) {
|
|
423
446
|
body[objParmas[0]] = {};
|
|
424
447
|
}
|
|
425
|
-
if (!body[objParmas[0]][objParmas[1]]){
|
|
448
|
+
if (!body[objParmas[0]][objParmas[1]]) {
|
|
426
449
|
body[objParmas[0]][objParmas[1]] = {};
|
|
427
450
|
}
|
|
428
451
|
body[objParmas[0]][objParmas[1]][objParmas[2]] = value;
|
|
@@ -439,11 +462,11 @@ export class DingTalkMCPServer {
|
|
|
439
462
|
return undefined;
|
|
440
463
|
}
|
|
441
464
|
|
|
442
|
-
if (tool.name === 'sendServiceWindowMessage'){
|
|
465
|
+
if (tool.name === 'sendServiceWindowMessage') {
|
|
443
466
|
return ServiceWindowMessageBuilder.buildSendServiceWindowMarkdownBody(args);
|
|
444
|
-
}else if (tool.name === 'batchSendServiceWindowMessage'){
|
|
467
|
+
} else if (tool.name === 'batchSendServiceWindowMessage') {
|
|
445
468
|
return ServiceWindowMessageBuilder.buildBatchSendServiceWindowMarkdownBody(args);
|
|
446
|
-
}else if (tool.name === '
|
|
469
|
+
} else if (tool.name === 'createReport' || tool.name === 'saveReportDraft') {
|
|
447
470
|
return LogMessageBuilder.buildBody(args);
|
|
448
471
|
}
|
|
449
472
|
|
|
@@ -453,24 +476,24 @@ export class DingTalkMCPServer {
|
|
|
453
476
|
if (arg.position === 'body') {
|
|
454
477
|
// 系统参数,从环境变量读取
|
|
455
478
|
if (arg.system) {
|
|
456
|
-
if (!process.env[arg.system]){
|
|
479
|
+
if (!process.env[arg.system]) {
|
|
457
480
|
throw new Error(`System parameter ${arg.system} is required, please check your environment variables`);
|
|
458
481
|
}
|
|
459
482
|
body[arg.name] = process.env[arg.system];
|
|
460
483
|
return;
|
|
461
484
|
}
|
|
462
485
|
// 不需要模型处理,直接取默认值赋值
|
|
463
|
-
if (arg.not_need_model_transform){
|
|
486
|
+
if (arg.not_need_model_transform) {
|
|
464
487
|
this.processMultiParam(body, arg.name, arg.default);
|
|
465
488
|
return;
|
|
466
489
|
}
|
|
467
490
|
|
|
468
491
|
// 如果模型没有对应提参,则不赋值给API对应的入参
|
|
469
|
-
if (
|
|
492
|
+
if (args[arg.name] == null) {
|
|
470
493
|
return;
|
|
471
494
|
}
|
|
472
495
|
|
|
473
|
-
if (arg.extendType === 'json'){
|
|
496
|
+
if (arg.extendType === 'json') {
|
|
474
497
|
body[arg.name] = JSON.stringify(args[arg.name]);
|
|
475
498
|
return;
|
|
476
499
|
}
|
|
@@ -496,7 +519,7 @@ export class DingTalkMCPServer {
|
|
|
496
519
|
// 为searchUser/searchDepartment添加默认的分页参数
|
|
497
520
|
|
|
498
521
|
// 设置默认的offset为0
|
|
499
|
-
if (tool.name === 'searchDepartment' || tool.name === 'searchUser'){
|
|
522
|
+
if (tool.name === 'searchDepartment' || tool.name === 'searchUser') {
|
|
500
523
|
if (body.offset === undefined) {
|
|
501
524
|
body.offset = 0;
|
|
502
525
|
}
|
|
@@ -516,4 +539,5 @@ export class DingTalkMCPServer {
|
|
|
516
539
|
console.error('DingTalk MCP server running on stdio');
|
|
517
540
|
}
|
|
518
541
|
}
|
|
542
|
+
|
|
519
543
|
//# sourceMappingURL=DingTalkMCPServer.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 字段属性
|
|
2
|
+
|
|
3
|
+
该文档介绍了AI表格中字段属性的相关配置。
|
|
4
|
+
|
|
5
|
+
| 字段名 | 类型 (type) | 属性 (property) |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| 文本 | text | 无 |
|
|
8
|
+
| 数字 | number | ``` { formatter: "INT" // 整数 \| "FLOAT_1" // 保留1位小数 \| "FLOAT_2" // 保留2位小数 \| "FLOAT_3" // 保留3位小数 \| "FLOAT_4" // 保留4位小数 \| "THOUSAND" // 千分位 \| "THOUSAND_FLOAT" // 千分位(小数点) \| "PRESENT" // 百分比 \| "PRESENT_FLOAT" // 百分比(小数点) \| "CNY" // 人民币 \| "CNY_FLOAT" // 人民币(小数点) \| "HKD" // 港元 \| "HKD_FLOAT" // 港元(小数点) \| "USD" // 美元 \| "USD_FLOAT" // 美元(小数点) \| "EUR" // 欧元 \| "EUR_FLOAT" // 欧元(小数点) \| "JPY" // 日元 \| "JPY_FLOAT"; // 日元(小数点) } ``` |
|
|
9
|
+
| 单选 | singleSelect | ``` { choices: [{ name: "optionName1" // 配置选项名 }, { name: "optionName2" }]; } ``` |
|
|
10
|
+
| 多选 | multipleSelect | 同「单选」 |
|
|
11
|
+
| 日期 | date | ``` { formatter: "YYYY-MM-DD" // 显示格式: 2023-12-31 \| "YYYY-MM-DD HH:mm" // 显示格式: 2023-12-31 09:00 \| "YYYY/MM/DD" // 显示格式: 2023/12/31 \| "YYYY/MM/DD HH:mm"; // 显示格式: 2023/12/31 09:00 } ``` |
|
|
12
|
+
| 人员 | user | ``` { multiple: boolean; // 支持多选,默认为true } ``` |
|
|
13
|
+
| 部门 | department | ``` { multiple: boolean; // 支持多选,默认为true } ``` |
|
|
14
|
+
| 附件 | attachment | 无 |
|
|
15
|
+
| 单向关联 | unidirectionalLink | ``` { multiple: boolean; // 支持多选,默认为true linkedSheetId: "xxx" // 关联的数据表ID } ``` |
|
|
16
|
+
| 双向关联 | bidirectionalLink | ``` { multiple: boolean; // 支持多选,默认为true linkedSheetId: "xxx", // 关联的数据表ID linkedFieldId: "yyy" // 关联的数据表上的字段ID,创建字段时不传 } ``` |
|
|
17
|
+
| 链接 | url | 无 |
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 记录值格式
|
|
2
|
+
该文档介绍AI表格中记录值的读写格式。
|
|
3
|
+
|
|
4
|
+
不同字段类型所使用的格式请参考下表:
|
|
5
|
+
|
|
6
|
+
| 字段名 | 类型 (type) | 设置值(新增/更新记录时使用的格式) | 返回值(返回记录值时返回的格式) |
|
|
7
|
+
| --- | --- | --- | --- |
|
|
8
|
+
| 文本 | text | `"TextString" // 字符串` | `"TextString" // 字符串` |
|
|
9
|
+
| 数字 | number | `123 // 支持整数/浮点数/字符串` | `"123" // 数字值,以字符串形式返回` |
|
|
10
|
+
| 单选 | singleSelect | `"optionName1" // 单选选项名` | `{ "id": "id", // 选项ID "name": "optionName1" // 选项名 }` |
|
|
11
|
+
| 多选 | multipleSelect | `["optionName1", "optionName2"] // 多选选项名` | `[ { "id": "id1", // 选项ID "name": "optionName1" // 选项名 }, { "id": "id2", // 选项ID "name": "optionName2" // 选项名 } ]` |
|
|
12
|
+
| 日期 | date | `1688601600000 // 时间戳 "2023-12-20 03:00" // 或者 ISO 8601字符串` | `1688601600000 // 时间戳` |
|
|
13
|
+
| 人员 | user | `[ { unionId: "xxx" } ]` | `[ { unionId: "xxx" } ]` |
|
|
14
|
+
| 部门 | department | `[ { deptId: "xxx" } ]` | `[ { deptId: "xxx" } ]` |
|
|
15
|
+
| 附件 | attachment | 具体请参考[上传附件](https://open.dingtalk.com/document/orgapp/notable-upload-attachment) 。 | `[ { "filename": "image.xlsx", "size": 92250, "type": "xls", "url": "xxx" } ]`<br> <br><br>**说明**<br><br>url是附件访问链接。<br><br>* 当附件是在线文档时,其是在线文档链接,该链接没有访问时效。<br> <br>* 当附件是其它文件时,是一个有**访问时效** 的下载链接,一段时间后该链接将无法访问。 |
|
|
16
|
+
| 单向关联 | unidirectionalLink | `{ "linkedRecordIds": [ "xxx", "yyy" ] }` | `{ "linkedRecordIds": [ "xxx", "yyy" ] }`<br> <br><br>**说明**<br><br>field property中包含关联的sheetId,配合这里返回的recordId,可以通过调用[获取记录](/document/orgapp/api-getrecord#) 接口去获取关联记录的值。 |
|
|
17
|
+
| 双向关联 | bidirectionalLink | `{ "linkedRecordIds": [ "xxx", "yyy" ] }` | `{ "linkedRecordIds": [ "xxx", "yyy" ] }` |
|
|
18
|
+
| 链接 | url | `{ "text": "Dingtalk", "link": "https://dingtalk.com" }` | `{ "text": "Dingtalk", "link": "https://dingtalk.com" }` |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* 日期和时间处理的工具类 (适用于 Node.js 环境)
|
|
4
|
+
*/
|
|
5
|
+
export class DateUtils {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 异步获取当前时间,并根据运行环境的区域和时区设置进行格式化。
|
|
9
|
+
* @returns {Promise<string>} 一个 Promise,解析为格式化后的本地时间字符串, e.g., "2023-10-27 16:45:30"
|
|
10
|
+
*/
|
|
11
|
+
static getFormattedLocalNow() {
|
|
12
|
+
// 1. 异步、安全地获取操作系统的区域设置,提供一个后备值
|
|
13
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale || 'zh-CN';
|
|
14
|
+
|
|
15
|
+
// 2. Intl.DateTimeFormat 默认就会使用环境时区
|
|
16
|
+
const formatter = new Intl.DateTimeFormat(locale, {
|
|
17
|
+
year: 'numeric',
|
|
18
|
+
month: '2-digit',
|
|
19
|
+
day: '2-digit',
|
|
20
|
+
hour: '2-digit',
|
|
21
|
+
minute: '2-digit',
|
|
22
|
+
second: '2-digit',
|
|
23
|
+
hour12: false // 使用24小时制
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 3. 格式化并统一分隔符
|
|
27
|
+
return formatter.format(new Date()).replace(/\//g, '-');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path, {normalize} from 'path';
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import {fileURLToPath} from 'url';
|
|
4
|
+
|
|
5
|
+
export class FileUtils {
|
|
6
|
+
|
|
7
|
+
static readLocalFileContent(tool){
|
|
8
|
+
if (tool.requestTemplate.type === "file"){
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
return fs.readFileSync(path.join(path.join(__dirname, '..'), tool.requestTemplate.url), 'utf8');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {DateUtils} from './DateUtils.js'
|
|
2
|
+
import {FileUtils} from './file.js'
|
|
3
|
+
|
|
4
|
+
const localTools = ["currentDateTime", "notableSupportedFieldInfo", "notableRecordValuesFormat"]
|
|
5
|
+
|
|
6
|
+
export class LocalTools{
|
|
7
|
+
|
|
8
|
+
static isLocalTool(toolName){
|
|
9
|
+
return localTools.includes(toolName)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static callTool(tool){
|
|
13
|
+
if (tool.name === "currentDateTime"){
|
|
14
|
+
return DateUtils.getFormattedLocalNow();
|
|
15
|
+
}else if (tool.name === "notableSupportedFieldInfo" || tool.name === "notableRecordValuesFormat"){
|
|
16
|
+
return FileUtils.readLocalFileContent(tool);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|