@vrs-soft/wecom-aibot-mcp 2.3.1 → 2.3.3
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/dist/channel-server.js +111 -6
- package/dist/client-pool.d.ts +1 -0
- package/dist/config-wizard.d.ts +6 -0
- package/dist/config-wizard.js +56 -4
- package/dist/doc-proxy.d.ts +21 -0
- package/dist/doc-proxy.js +46 -0
- package/dist/http-server.d.ts +1 -0
- package/dist/http-server.js +6 -0
- package/dist/tools/index.js +211 -7
- package/package.json +1 -1
package/dist/channel-server.js
CHANGED
|
@@ -38,6 +38,7 @@ function logChannel(message, data) {
|
|
|
38
38
|
let sseConnected = false;
|
|
39
39
|
let sseAbortController = null;
|
|
40
40
|
let mcpServer = null;
|
|
41
|
+
let sseCurrentCcId = undefined;
|
|
41
42
|
// HTTP MCP session ID(需要在转发请求前初始化)
|
|
42
43
|
let httpSessionId = null;
|
|
43
44
|
/**
|
|
@@ -172,6 +173,7 @@ function connectSSE(ccId) {
|
|
|
172
173
|
return;
|
|
173
174
|
}
|
|
174
175
|
sseConnected = true;
|
|
176
|
+
sseCurrentCcId = ccId;
|
|
175
177
|
const sseUrl = ccId ? `${MCP_URL}/sse/${ccId}` : `${MCP_URL}/sse`;
|
|
176
178
|
logChannel('Connecting to SSE', { url: sseUrl, ccId, mcpServerReady: mcpServer ? 'yes' : 'no' });
|
|
177
179
|
sseAbortController = new AbortController();
|
|
@@ -210,6 +212,11 @@ function connectSSE(ccId) {
|
|
|
210
212
|
logChannel('SSE stream ended');
|
|
211
213
|
clearInterval(heartbeatInterval);
|
|
212
214
|
sseConnected = false;
|
|
215
|
+
// 非主动断开时自动重连
|
|
216
|
+
if (!sseAbortController?.signal.aborted) {
|
|
217
|
+
logChannel('SSE 断线,3 秒后重连', { ccId });
|
|
218
|
+
setTimeout(() => connectSSE(ccId), 3000);
|
|
219
|
+
}
|
|
213
220
|
break;
|
|
214
221
|
}
|
|
215
222
|
const chunk = decoder.decode(value, { stream: true });
|
|
@@ -240,6 +247,7 @@ function connectSSE(ccId) {
|
|
|
240
247
|
chatid: message.chatid || '',
|
|
241
248
|
chattype: message.chattype || 'single',
|
|
242
249
|
cc_id: msg.ccId || '',
|
|
250
|
+
quote_content: message.quoteContent || '',
|
|
243
251
|
},
|
|
244
252
|
},
|
|
245
253
|
};
|
|
@@ -279,6 +287,11 @@ function connectSSE(ccId) {
|
|
|
279
287
|
}).catch((err) => {
|
|
280
288
|
logChannel('SSE error', { error: String(err) });
|
|
281
289
|
sseConnected = false;
|
|
290
|
+
// 非主动断开时自动重连
|
|
291
|
+
if (!sseAbortController?.signal.aborted) {
|
|
292
|
+
logChannel('SSE 出错,3 秒后重连', { ccId });
|
|
293
|
+
setTimeout(() => connectSSE(ccId), 3000);
|
|
294
|
+
}
|
|
282
295
|
});
|
|
283
296
|
}
|
|
284
297
|
/**
|
|
@@ -351,8 +364,9 @@ function registerChannelTools(server) {
|
|
|
351
364
|
bot_id: z.string().describe('企业微信 Bot ID'),
|
|
352
365
|
secret: z.string().describe('机器人密钥'),
|
|
353
366
|
default_user: z.string().optional().describe('默认目标用户'),
|
|
354
|
-
|
|
355
|
-
|
|
367
|
+
doc_mcp_url: z.string().optional().describe('机器人文档 MCP URL(企业微信文档能力)'),
|
|
368
|
+
}, async ({ name, bot_id, secret, default_user, doc_mcp_url }) => {
|
|
369
|
+
return forwardToHttpMcp('add_robot_config', { name, bot_id, secret, default_user, doc_mcp_url });
|
|
356
370
|
});
|
|
357
371
|
// ============================================
|
|
358
372
|
// 工具 8: 列出所有机器人
|
|
@@ -378,7 +392,7 @@ function registerChannelTools(server) {
|
|
|
378
392
|
agent_name,
|
|
379
393
|
cc_id,
|
|
380
394
|
robot_id,
|
|
381
|
-
project_dir,
|
|
395
|
+
project_dir: project_dir || process.cwd(),
|
|
382
396
|
mode,
|
|
383
397
|
auto_approve,
|
|
384
398
|
auto_approve_timeout,
|
|
@@ -415,14 +429,15 @@ function registerChannelTools(server) {
|
|
|
415
429
|
cc_id: z.string().describe('CC 唯一标识(enter_headless_mode 返回的 ccId)'),
|
|
416
430
|
project_dir: z.string().optional().describe('项目目录路径(用于更新配置文件)'),
|
|
417
431
|
}, async ({ cc_id, project_dir }) => {
|
|
418
|
-
// 断开 SSE
|
|
432
|
+
// 断开 SSE 连接(abort 后重连逻辑不会触发)
|
|
419
433
|
if (sseAbortController) {
|
|
420
434
|
sseAbortController.abort();
|
|
421
435
|
sseAbortController = null;
|
|
422
436
|
sseConnected = false;
|
|
437
|
+
sseCurrentCcId = undefined;
|
|
423
438
|
logChannel('SSE disconnected', { cc_id });
|
|
424
439
|
}
|
|
425
|
-
return forwardToHttpMcp('exit_headless_mode', { cc_id, project_dir });
|
|
440
|
+
return forwardToHttpMcp('exit_headless_mode', { cc_id, project_dir: project_dir || process.cwd() });
|
|
426
441
|
});
|
|
427
442
|
// ============================================
|
|
428
443
|
// 工具 11: 从消息识别用户
|
|
@@ -461,7 +476,97 @@ function registerChannelTools(server) {
|
|
|
461
476
|
}],
|
|
462
477
|
};
|
|
463
478
|
});
|
|
464
|
-
|
|
479
|
+
// ============================================
|
|
480
|
+
// 文档代理工具(转发到 HTTP MCP)
|
|
481
|
+
// ============================================
|
|
482
|
+
const docTools = [
|
|
483
|
+
['create_doc', '新建文档或智能表格', {
|
|
484
|
+
doc_type: z.number().int().describe('文档类型:3=文档,10=智能表格'),
|
|
485
|
+
doc_name: z.string().describe('文档名称'),
|
|
486
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
487
|
+
}],
|
|
488
|
+
['get_doc_content', '获取文档内容(Markdown 格式)', {
|
|
489
|
+
type: z.number().int().describe('内容格式:2=Markdown'),
|
|
490
|
+
url: z.string().optional().describe('文档链接'),
|
|
491
|
+
docid: z.string().optional().describe('文档 docid'),
|
|
492
|
+
task_id: z.string().optional().describe('任务 ID(轮询时填写)'),
|
|
493
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
494
|
+
}],
|
|
495
|
+
['edit_doc_content', '编辑文档内容(Markdown 格式覆写)', {
|
|
496
|
+
content: z.string().describe('覆写的文档内容'),
|
|
497
|
+
content_type: z.number().int().describe('内容类型:1=Markdown'),
|
|
498
|
+
url: z.string().optional().describe('文档链接'),
|
|
499
|
+
docid: z.string().optional().describe('文档 docid'),
|
|
500
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
501
|
+
}],
|
|
502
|
+
['smartsheet_get_sheet', '查询智能表格子表信息', {
|
|
503
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
504
|
+
robot_name: z.string().optional(),
|
|
505
|
+
}],
|
|
506
|
+
['smartsheet_add_sheet', '添加智能表格子表', {
|
|
507
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
508
|
+
properties: z.object({ title: z.string().optional() }).optional(),
|
|
509
|
+
robot_name: z.string().optional(),
|
|
510
|
+
}],
|
|
511
|
+
['smartsheet_update_sheet', '更新智能表格子表标题', {
|
|
512
|
+
properties: z.object({ sheet_id: z.string(), title: z.string() }),
|
|
513
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
514
|
+
robot_name: z.string().optional(),
|
|
515
|
+
}],
|
|
516
|
+
['smartsheet_delete_sheet', '删除智能表格子表', {
|
|
517
|
+
sheet_id: z.string(), url: z.string().optional(), docid: z.string().optional(),
|
|
518
|
+
robot_name: z.string().optional(),
|
|
519
|
+
}],
|
|
520
|
+
['smartsheet_get_fields', '查询智能表格字段', {
|
|
521
|
+
sheet_id: z.string(), url: z.string().optional(), docid: z.string().optional(),
|
|
522
|
+
robot_name: z.string().optional(),
|
|
523
|
+
}],
|
|
524
|
+
['smartsheet_add_fields', '添加智能表格字段', {
|
|
525
|
+
sheet_id: z.string(),
|
|
526
|
+
fields: z.array(z.object({ field_title: z.string(), field_type: z.string() })),
|
|
527
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
528
|
+
robot_name: z.string().optional(),
|
|
529
|
+
}],
|
|
530
|
+
['smartsheet_update_fields', '更新智能表格字段标题', {
|
|
531
|
+
sheet_id: z.string(),
|
|
532
|
+
fields: z.array(z.object({ field_id: z.string(), field_title: z.string(), field_type: z.string() })),
|
|
533
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
534
|
+
robot_name: z.string().optional(),
|
|
535
|
+
}],
|
|
536
|
+
['smartsheet_delete_fields', '删除智能表格字段', {
|
|
537
|
+
sheet_id: z.string(), field_ids: z.array(z.string()),
|
|
538
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
539
|
+
robot_name: z.string().optional(),
|
|
540
|
+
}],
|
|
541
|
+
['smartsheet_get_records', '查询智能表格记录', {
|
|
542
|
+
sheet_id: z.string(), url: z.string().optional(), docid: z.string().optional(),
|
|
543
|
+
robot_name: z.string().optional(),
|
|
544
|
+
}],
|
|
545
|
+
['smartsheet_add_records', '添加智能表格记录', {
|
|
546
|
+
sheet_id: z.string(),
|
|
547
|
+
records: z.array(z.object({ values: z.record(z.string(), z.unknown()) })),
|
|
548
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
549
|
+
robot_name: z.string().optional(),
|
|
550
|
+
}],
|
|
551
|
+
['smartsheet_update_records', '更新智能表格记录', {
|
|
552
|
+
sheet_id: z.string(),
|
|
553
|
+
records: z.array(z.object({ record_id: z.string(), values: z.record(z.string(), z.unknown()) })),
|
|
554
|
+
key_type: z.enum(['CELL_VALUE_KEY_TYPE_FIELD_TITLE', 'CELL_VALUE_KEY_TYPE_FIELD_ID']),
|
|
555
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
556
|
+
robot_name: z.string().optional(),
|
|
557
|
+
}],
|
|
558
|
+
['smartsheet_delete_records', '删除智能表格记录', {
|
|
559
|
+
sheet_id: z.string(), record_ids: z.array(z.string()),
|
|
560
|
+
url: z.string().optional(), docid: z.string().optional(),
|
|
561
|
+
robot_name: z.string().optional(),
|
|
562
|
+
}],
|
|
563
|
+
];
|
|
564
|
+
for (const [toolName, description, schema] of docTools) {
|
|
565
|
+
server.tool(toolName, description, schema, async (args) => {
|
|
566
|
+
return forwardToHttpMcp(toolName, args);
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
logChannel('Registered 28 tools (13 core + 15 doc proxy)');
|
|
465
570
|
}
|
|
466
571
|
/**
|
|
467
572
|
* 启动 Channel MCP Server
|
package/dist/client-pool.d.ts
CHANGED
package/dist/config-wizard.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface WecomConfig {
|
|
|
4
4
|
targetUserId: string;
|
|
5
5
|
targetUserName?: string;
|
|
6
6
|
nameTag?: string;
|
|
7
|
+
doc_mcp_url?: string;
|
|
7
8
|
}
|
|
8
9
|
export declare function loadConfig(): WecomConfig | null;
|
|
9
10
|
export declare function listAllMcpInstances(): Array<{
|
|
@@ -23,7 +24,12 @@ export declare function listAllRobots(): Array<{
|
|
|
23
24
|
name: string;
|
|
24
25
|
botId: string;
|
|
25
26
|
targetUserId: string;
|
|
27
|
+
doc_mcp_url?: string;
|
|
26
28
|
}>;
|
|
29
|
+
export declare function getDocMcpUrl(robotName?: string): {
|
|
30
|
+
url: string | null;
|
|
31
|
+
error?: string;
|
|
32
|
+
};
|
|
27
33
|
export declare function ensureHookInstalled(): void;
|
|
28
34
|
export declare function ensureGlobalConfigs(mode?: 'full' | 'http-only' | 'channel-only'): {
|
|
29
35
|
upgraded: boolean;
|
package/dist/config-wizard.js
CHANGED
|
@@ -45,11 +45,16 @@ export function loadConfig() {
|
|
|
45
45
|
const content = fs.readFileSync(BOT_CONFIG_FILE, 'utf-8');
|
|
46
46
|
const config = JSON.parse(content);
|
|
47
47
|
if (config.botId && config.secret && config.targetUserId) {
|
|
48
|
-
|
|
48
|
+
const result = {
|
|
49
49
|
botId: config.botId,
|
|
50
50
|
secret: config.secret,
|
|
51
51
|
targetUserId: config.targetUserId,
|
|
52
52
|
};
|
|
53
|
+
if (config.nameTag)
|
|
54
|
+
result.nameTag = config.nameTag;
|
|
55
|
+
if (config.doc_mcp_url)
|
|
56
|
+
result.doc_mcp_url = config.doc_mcp_url;
|
|
57
|
+
return result;
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
60
|
}
|
|
@@ -650,6 +655,9 @@ function writeMcpServerConfig(config, instanceName) {
|
|
|
650
655
|
if (config.nameTag) {
|
|
651
656
|
botConfig.nameTag = config.nameTag;
|
|
652
657
|
}
|
|
658
|
+
if (config.doc_mcp_url) {
|
|
659
|
+
botConfig.doc_mcp_url = config.doc_mcp_url;
|
|
660
|
+
}
|
|
653
661
|
// 检查名称唯一性(如果设置了新名称)
|
|
654
662
|
if (config.nameTag && isRobotNameExists(config.nameTag, config.botId)) {
|
|
655
663
|
console.log(`[config] ❌ 机器人名称 "${config.nameTag}" 已被其他机器人使用`);
|
|
@@ -843,6 +851,7 @@ export function listAllRobots() {
|
|
|
843
851
|
name,
|
|
844
852
|
botId: config.botId,
|
|
845
853
|
targetUserId: config.targetUserId,
|
|
854
|
+
...(config.doc_mcp_url ? { doc_mcp_url: config.doc_mcp_url } : {}),
|
|
846
855
|
});
|
|
847
856
|
}
|
|
848
857
|
catch {
|
|
@@ -860,6 +869,7 @@ export function listAllRobots() {
|
|
|
860
869
|
name,
|
|
861
870
|
botId: config.botId,
|
|
862
871
|
targetUserId: config.targetUserId,
|
|
872
|
+
...(config.doc_mcp_url ? { doc_mcp_url: config.doc_mcp_url } : {}),
|
|
863
873
|
});
|
|
864
874
|
}
|
|
865
875
|
catch {
|
|
@@ -869,6 +879,35 @@ export function listAllRobots() {
|
|
|
869
879
|
}
|
|
870
880
|
return robots;
|
|
871
881
|
}
|
|
882
|
+
// 获取指定机器人(或唯一机器人)的文档 MCP URL
|
|
883
|
+
export function getDocMcpUrl(robotName) {
|
|
884
|
+
const robots = listAllRobots();
|
|
885
|
+
const robotsWithDoc = robots.filter(r => r.doc_mcp_url);
|
|
886
|
+
if (robotsWithDoc.length === 0) {
|
|
887
|
+
return {
|
|
888
|
+
url: null,
|
|
889
|
+
error: '未配置文档 MCP URL。请运行 `npx @vrs-soft/wecom-aibot-mcp --add` 添加机器人时填写文档 MCP URL,或通过 `add_robot_config` 工具设置。',
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
if (robotName) {
|
|
893
|
+
const robot = robotsWithDoc.find(r => r.name === robotName);
|
|
894
|
+
if (!robot) {
|
|
895
|
+
return {
|
|
896
|
+
url: null,
|
|
897
|
+
error: `未找到名为 "${robotName}" 的机器人,或该机器人未配置文档 MCP URL。已配置文档能力的机器人: ${robotsWithDoc.map(r => r.name).join(', ')}`,
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
return { url: robot.doc_mcp_url };
|
|
901
|
+
}
|
|
902
|
+
if (robotsWithDoc.length === 1) {
|
|
903
|
+
return { url: robotsWithDoc[0].doc_mcp_url };
|
|
904
|
+
}
|
|
905
|
+
// 多个机器人有 doc_mcp_url,需要用户指定
|
|
906
|
+
return {
|
|
907
|
+
url: null,
|
|
908
|
+
error: `有多个机器人配置了文档 MCP URL,请通过 robot_name 参数指定使用哪个机器人。已配置文档能力的机器人: ${robotsWithDoc.map(r => r.name).join(', ')}`,
|
|
909
|
+
};
|
|
910
|
+
}
|
|
872
911
|
// 写入 MCP 工具权限 + 注册 PermissionRequest hook 到 Claude settings
|
|
873
912
|
function writeMcpPermissions() {
|
|
874
913
|
try {
|
|
@@ -1075,7 +1114,8 @@ export async function runConfigWizard() {
|
|
|
1075
1114
|
else {
|
|
1076
1115
|
console.log('\n请选择要操作的机器人:\n');
|
|
1077
1116
|
robots.forEach((robot, idx) => {
|
|
1078
|
-
|
|
1117
|
+
const docTag = robot.doc_mcp_url ? ' [文档✅]' : '';
|
|
1118
|
+
console.log(` ${idx + 1}. ${robot.name} (Bot ID: ${robot.botId.slice(0, 12)}...)${docTag}`);
|
|
1079
1119
|
});
|
|
1080
1120
|
console.log(` ${robots.length + 1}. 添加新机器人\n`);
|
|
1081
1121
|
const choice = await question(rl, '请输入序号: ');
|
|
@@ -1150,12 +1190,23 @@ export async function runConfigWizard() {
|
|
|
1150
1190
|
}
|
|
1151
1191
|
}
|
|
1152
1192
|
}
|
|
1153
|
-
//
|
|
1193
|
+
// 第五步:文档 MCP URL(可选)
|
|
1194
|
+
const currentDocUrl = targetRobot?.doc_mcp_url ?? '';
|
|
1195
|
+
const docUrlPrompt = currentDocUrl
|
|
1196
|
+
? `文档 MCP URL(当前: ${currentDocUrl.slice(0, 40)}...,留空保持不变): `
|
|
1197
|
+
: '文档 MCP URL(可选,企业微信管理后台获取,留空跳过): ';
|
|
1198
|
+
let docMcpUrl = await question(rl, docUrlPrompt);
|
|
1199
|
+
if (!docMcpUrl && currentDocUrl) {
|
|
1200
|
+
docMcpUrl = currentDocUrl;
|
|
1201
|
+
console.log('[config] 保持原文档 MCP URL');
|
|
1202
|
+
}
|
|
1203
|
+
// 第六步:目标用户(稍后通过消息自动识别)
|
|
1154
1204
|
console.log('\n─────────────────────────────────────');
|
|
1155
1205
|
console.log('配置确认:');
|
|
1156
1206
|
console.log(` 机器人名称: ${robotName}`);
|
|
1157
1207
|
console.log(` Bot ID: ${botId}`);
|
|
1158
1208
|
console.log(` Secret: ${secret.slice(0, 8)}...${secret.slice(-4)}`);
|
|
1209
|
+
console.log(` 文档 MCP: ${docMcpUrl ? '✅ 已配置' : '(未配置)'}`);
|
|
1159
1210
|
console.log(` 目标用户: (将通过消息自动识别)`);
|
|
1160
1211
|
console.log('─────────────────────────────────────\n');
|
|
1161
1212
|
const confirm = await question(rl, '确认配置?(Y/n): ');
|
|
@@ -1167,8 +1218,9 @@ export async function runConfigWizard() {
|
|
|
1167
1218
|
const config = {
|
|
1168
1219
|
botId,
|
|
1169
1220
|
secret,
|
|
1170
|
-
targetUserId: '', //
|
|
1221
|
+
targetUserId: targetRobot?.targetUserId || '', // 修改时保留原值,新建时稍后识别
|
|
1171
1222
|
nameTag: robotName,
|
|
1223
|
+
...(docMcpUrl ? { doc_mcp_url: docMcpUrl } : {}),
|
|
1172
1224
|
};
|
|
1173
1225
|
// 如果是修改现有机器人,返回其 instanceName(用于删除旧配置)
|
|
1174
1226
|
const instanceName = targetRobot ? targetRobot.name : 'wecom-aibot';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文档 MCP 代理模块
|
|
3
|
+
*
|
|
4
|
+
* 将文档工具调用代理转发到机器人专属的企业微信文档 MCP 服务(StreamableHTTP)。
|
|
5
|
+
* 每次调用建立独立连接,无需维护长连接状态。
|
|
6
|
+
*/
|
|
7
|
+
export interface DocToolResult {
|
|
8
|
+
content: Array<{
|
|
9
|
+
type: string;
|
|
10
|
+
text: string;
|
|
11
|
+
}>;
|
|
12
|
+
isError?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 调用机器人文档 MCP 工具
|
|
16
|
+
*
|
|
17
|
+
* @param docMcpUrl 机器人专属的文档 MCP URL(含 uaKey)
|
|
18
|
+
* @param toolName 工具名称,如 create_doc、smartsheet_add_records 等
|
|
19
|
+
* @param args 工具参数
|
|
20
|
+
*/
|
|
21
|
+
export declare function callDocTool(docMcpUrl: string, toolName: string, args: Record<string, unknown>): Promise<DocToolResult>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文档 MCP 代理模块
|
|
3
|
+
*
|
|
4
|
+
* 将文档工具调用代理转发到机器人专属的企业微信文档 MCP 服务(StreamableHTTP)。
|
|
5
|
+
* 每次调用建立独立连接,无需维护长连接状态。
|
|
6
|
+
*/
|
|
7
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
8
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
9
|
+
import { logger } from './logger.js';
|
|
10
|
+
/**
|
|
11
|
+
* 调用机器人文档 MCP 工具
|
|
12
|
+
*
|
|
13
|
+
* @param docMcpUrl 机器人专属的文档 MCP URL(含 uaKey)
|
|
14
|
+
* @param toolName 工具名称,如 create_doc、smartsheet_add_records 等
|
|
15
|
+
* @param args 工具参数
|
|
16
|
+
*/
|
|
17
|
+
export async function callDocTool(docMcpUrl, toolName, args) {
|
|
18
|
+
const client = new Client({ name: 'wecom-aibot-doc-proxy', version: '1.0.0' }, { capabilities: {} });
|
|
19
|
+
try {
|
|
20
|
+
const transport = new StreamableHTTPClientTransport(new URL(docMcpUrl));
|
|
21
|
+
await client.connect(transport);
|
|
22
|
+
const result = await client.callTool({ name: toolName, arguments: args });
|
|
23
|
+
return {
|
|
24
|
+
content: result.content ?? [
|
|
25
|
+
{ type: 'text', text: JSON.stringify(result) },
|
|
26
|
+
],
|
|
27
|
+
isError: result.isError,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
logger.error(`[doc-proxy] 调用 ${toolName} 失败:`, err);
|
|
32
|
+
const message = err?.message ?? String(err);
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: 'text', text: `文档工具调用失败: ${message}\n\n请检查机器人的文档 MCP URL 是否正确,或该机器人是否已授权文档能力。` }],
|
|
35
|
+
isError: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
try {
|
|
40
|
+
await client.close();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// 关闭时忽略错误
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/http-server.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export declare function pushMessageToSSEClient(robotName: string, message: {
|
|
|
31
31
|
chatid: string;
|
|
32
32
|
chattype: 'single' | 'group';
|
|
33
33
|
timestamp: number;
|
|
34
|
+
quoteContent?: string;
|
|
34
35
|
}, targetCcId?: string): Promise<void>;
|
|
35
36
|
export interface ApprovalRequest {
|
|
36
37
|
tool_name: string;
|
package/dist/http-server.js
CHANGED
|
@@ -109,6 +109,7 @@ export async function pushMessageToSSEClient(robotName, message, targetCcId) {
|
|
|
109
109
|
chatid: message.chatid,
|
|
110
110
|
chattype: message.chattype,
|
|
111
111
|
time: new Date(message.timestamp).toISOString(),
|
|
112
|
+
quoteContent: message.quoteContent,
|
|
112
113
|
},
|
|
113
114
|
});
|
|
114
115
|
client.res.write(`event: message\ndata: ${data}\n\n`);
|
|
@@ -227,6 +228,7 @@ async function handleWecomMessage(msg) {
|
|
|
227
228
|
chatid: msg.chatid,
|
|
228
229
|
chattype: msg.chattype,
|
|
229
230
|
timestamp: msg.timestamp,
|
|
231
|
+
quoteContent: msg.quoteContent,
|
|
230
232
|
}, targetCcId);
|
|
231
233
|
return;
|
|
232
234
|
}
|
|
@@ -251,6 +253,7 @@ async function handleWecomMessage(msg) {
|
|
|
251
253
|
chatid: msg.chatid,
|
|
252
254
|
chattype: msg.chattype,
|
|
253
255
|
timestamp: msg.timestamp,
|
|
256
|
+
quoteContent: msg.quoteContent,
|
|
254
257
|
}, ccId);
|
|
255
258
|
return;
|
|
256
259
|
}
|
|
@@ -267,6 +270,7 @@ async function handleWecomMessage(msg) {
|
|
|
267
270
|
chatid: msg.chatid,
|
|
268
271
|
chattype: msg.chattype,
|
|
269
272
|
timestamp: msg.timestamp,
|
|
273
|
+
quoteContent: msg.quoteContent,
|
|
270
274
|
}, targetCcId);
|
|
271
275
|
return;
|
|
272
276
|
}
|
|
@@ -284,6 +288,7 @@ async function handleWecomMessage(msg) {
|
|
|
284
288
|
chatid: msg.chatid,
|
|
285
289
|
chattype: msg.chattype,
|
|
286
290
|
timestamp: msg.timestamp,
|
|
291
|
+
quoteContent: msg.quoteContent,
|
|
287
292
|
}, matchedCcId);
|
|
288
293
|
return;
|
|
289
294
|
}
|
|
@@ -383,6 +388,7 @@ async function handleWecomMessage(msg) {
|
|
|
383
388
|
chatid: msg.chatid,
|
|
384
389
|
chattype: msg.chattype,
|
|
385
390
|
timestamp: msg.timestamp,
|
|
391
|
+
quoteContent: msg.quoteContent,
|
|
386
392
|
}, matchedCcId);
|
|
387
393
|
}
|
|
388
394
|
else {
|
package/dist/tools/index.js
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* - 从 Session 自动获取 robotName
|
|
21
21
|
*/
|
|
22
22
|
import { z } from 'zod';
|
|
23
|
-
import { listAllRobots, installSkill } from '../config-wizard.js';
|
|
23
|
+
import { listAllRobots, getDocMcpUrl, installSkill } from '../config-wizard.js';
|
|
24
|
+
import { callDocTool } from '../doc-proxy.js';
|
|
24
25
|
import { connectRobot, disconnectRobot, getClient, getConnectionState, } from '../connection-manager.js';
|
|
25
26
|
import { registerCcId, unregisterCcId, getRobotByCcId, getProjectDirByCcId, generateCcId, } from '../http-server.js';
|
|
26
27
|
import { subscribeWecomMessageByCcId } from '../message-bus.js';
|
|
@@ -312,13 +313,15 @@ npx @vrs-soft/wecom-aibot-mcp
|
|
|
312
313
|
bot_id: z.string().describe('企业微信 Bot ID'),
|
|
313
314
|
secret: z.string().describe('机器人密钥'),
|
|
314
315
|
default_user: z.string().optional().describe('默认目标用户'),
|
|
315
|
-
|
|
316
|
+
doc_mcp_url: z.string().optional().describe('机器人文档 MCP URL(企业微信文档能力)'),
|
|
317
|
+
}, async ({ name, bot_id, secret, default_user, doc_mcp_url }) => {
|
|
316
318
|
return {
|
|
317
319
|
content: [{
|
|
318
320
|
type: 'text',
|
|
319
321
|
text: JSON.stringify({
|
|
320
322
|
message: '请使用 --add 命令添加机器人配置',
|
|
321
323
|
command: `npx @vrs-soft/wecom-aibot-mcp --add`,
|
|
324
|
+
tip: doc_mcp_url ? '运行向导时在"文档 MCP URL"提示处粘贴您的 URL' : undefined,
|
|
322
325
|
}, null, 2),
|
|
323
326
|
}],
|
|
324
327
|
};
|
|
@@ -334,6 +337,7 @@ npx @vrs-soft/wecom-aibot-mcp
|
|
|
334
337
|
name: robot.name,
|
|
335
338
|
botId: robot.botId?.slice(0, 12) + '...', // 只显示前12位
|
|
336
339
|
targetUser: robot.targetUserId,
|
|
340
|
+
hasDocMcp: !!robot.doc_mcp_url, // 是否配置了文档 MCP 能力
|
|
337
341
|
}));
|
|
338
342
|
return {
|
|
339
343
|
content: [{
|
|
@@ -413,11 +417,11 @@ npx @vrs-soft/wecom-aibot-mcp
|
|
|
413
417
|
}],
|
|
414
418
|
};
|
|
415
419
|
}
|
|
416
|
-
//
|
|
417
|
-
const finalCcId = cc_id || generateCcId(effectiveAgentName);
|
|
418
|
-
// 检查 wecom-aibot.json 是否已有匹配的 ccId(重连场景)
|
|
420
|
+
// 检查 wecom-aibot.json 是否已有 ccId(重连场景)
|
|
419
421
|
const existingConfig = loadWechatModeConfig(projectDir);
|
|
420
|
-
|
|
422
|
+
// 优先级:cc_id 参数 > (非 agent_name 指定时) 配置文件已有 ccId > 自动生成
|
|
423
|
+
const finalCcId = cc_id || (!agent_name && existingConfig?.ccId) || generateCcId(effectiveAgentName);
|
|
424
|
+
const isReconnect = !!existingConfig?.ccId && existingConfig.ccId === finalCcId;
|
|
421
425
|
// 注册 ccId 到 CC 注册表(重连直接覆盖,首次注册清理超时条目)
|
|
422
426
|
registerCcId(finalCcId, selectedRobot.name, effectiveAgentName, mode, projectDir, isReconnect);
|
|
423
427
|
// 更新项目配置文件中的 wechatMode 为 true
|
|
@@ -598,5 +602,205 @@ npx @vrs-soft/wecom-aibot-mcp
|
|
|
598
602
|
}],
|
|
599
603
|
};
|
|
600
604
|
});
|
|
601
|
-
|
|
605
|
+
// ============================================
|
|
606
|
+
// 文档代理工具(企业微信文档 MCP 能力)
|
|
607
|
+
// 代理转发到机器人专属的 doc_mcp_url
|
|
608
|
+
// ============================================
|
|
609
|
+
// 公共辅助:解析 doc MCP URL
|
|
610
|
+
function resolveDocUrl(robotName) {
|
|
611
|
+
const { url, error } = getDocMcpUrl(robotName);
|
|
612
|
+
if (!url) {
|
|
613
|
+
return {
|
|
614
|
+
url: null,
|
|
615
|
+
errorContent: { content: [{ type: 'text', text: error ?? '未配置文档 MCP URL' }] },
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
return { url };
|
|
619
|
+
}
|
|
620
|
+
server.tool('create_doc', '新建文档或智能表格。doc_type: 3=文档, 10=智能表格。创建成功后返回文档链接。', {
|
|
621
|
+
doc_type: z.number().int().describe('文档类型:3=文档,10=智能表格'),
|
|
622
|
+
doc_name: z.string().describe('文档名称,最多 255 个字符'),
|
|
623
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
624
|
+
}, async ({ doc_type, doc_name, robot_name }) => {
|
|
625
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
626
|
+
if (!url)
|
|
627
|
+
return errorContent;
|
|
628
|
+
return callDocTool(url, 'create_doc', { doc_type, doc_name });
|
|
629
|
+
});
|
|
630
|
+
server.tool('get_doc_content', '获取企业微信文档内容(Markdown 格式)。支持异步轮询:首次调用无需 task_id,若 task_done 为 false 则带 task_id 继续轮询。', {
|
|
631
|
+
type: z.number().int().describe('内容格式:2=Markdown'),
|
|
632
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
633
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
634
|
+
task_id: z.string().optional().describe('任务 ID(轮询时填写)'),
|
|
635
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
636
|
+
}, async ({ type, url: docUrl, docid, task_id, robot_name }) => {
|
|
637
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
638
|
+
if (!url)
|
|
639
|
+
return errorContent;
|
|
640
|
+
return callDocTool(url, 'get_doc_content', { type, url: docUrl, docid, task_id });
|
|
641
|
+
});
|
|
642
|
+
server.tool('edit_doc_content', '编辑企业微信文档内容,支持 Markdown 格式覆写。', {
|
|
643
|
+
content: z.string().describe('覆写的文档内容'),
|
|
644
|
+
content_type: z.number().int().describe('内容类型:1=Markdown'),
|
|
645
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
646
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
647
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
648
|
+
}, async ({ content, content_type, url: docUrl, docid, robot_name }) => {
|
|
649
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
650
|
+
if (!url)
|
|
651
|
+
return errorContent;
|
|
652
|
+
return callDocTool(url, 'edit_doc_content', { content, content_type, url: docUrl, docid });
|
|
653
|
+
});
|
|
654
|
+
server.tool('smartsheet_get_sheet', '查询智能表格中的子表信息。', {
|
|
655
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
656
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
657
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
658
|
+
}, async ({ url: docUrl, docid, robot_name }) => {
|
|
659
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
660
|
+
if (!url)
|
|
661
|
+
return errorContent;
|
|
662
|
+
return callDocTool(url, 'smartsheet_get_sheet', { url: docUrl, docid });
|
|
663
|
+
});
|
|
664
|
+
server.tool('smartsheet_add_sheet', '在智能表格内添加新子表。', {
|
|
665
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
666
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
667
|
+
properties: z.object({ title: z.string().optional() }).optional().describe('子表属性,可设置 title'),
|
|
668
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
669
|
+
}, async ({ url: docUrl, docid, properties, robot_name }) => {
|
|
670
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
671
|
+
if (!url)
|
|
672
|
+
return errorContent;
|
|
673
|
+
return callDocTool(url, 'smartsheet_add_sheet', { url: docUrl, docid, properties });
|
|
674
|
+
});
|
|
675
|
+
server.tool('smartsheet_update_sheet', '修改智能表格子表的标题。', {
|
|
676
|
+
properties: z.object({
|
|
677
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
678
|
+
title: z.string().describe('新标题'),
|
|
679
|
+
}).describe('子表属性'),
|
|
680
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
681
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
682
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
683
|
+
}, async ({ properties, url: docUrl, docid, robot_name }) => {
|
|
684
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
685
|
+
if (!url)
|
|
686
|
+
return errorContent;
|
|
687
|
+
return callDocTool(url, 'smartsheet_update_sheet', { properties, url: docUrl, docid });
|
|
688
|
+
});
|
|
689
|
+
server.tool('smartsheet_delete_sheet', '删除智能表格中的子表(不可逆)。', {
|
|
690
|
+
sheet_id: z.string().describe('要删除的子表 ID'),
|
|
691
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
692
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
693
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
694
|
+
}, async ({ sheet_id, url: docUrl, docid, robot_name }) => {
|
|
695
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
696
|
+
if (!url)
|
|
697
|
+
return errorContent;
|
|
698
|
+
return callDocTool(url, 'smartsheet_delete_sheet', { sheet_id, url: docUrl, docid });
|
|
699
|
+
});
|
|
700
|
+
server.tool('smartsheet_get_fields', '获取智能表格子表的字段(列)信息。', {
|
|
701
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
702
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
703
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
704
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
705
|
+
}, async ({ sheet_id, url: docUrl, docid, robot_name }) => {
|
|
706
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
707
|
+
if (!url)
|
|
708
|
+
return errorContent;
|
|
709
|
+
return callDocTool(url, 'smartsheet_get_fields', { sheet_id, url: docUrl, docid });
|
|
710
|
+
});
|
|
711
|
+
server.tool('smartsheet_add_fields', '向智能表格子表添加字段(列)。调用前必须先用 smartsheet_get_fields 查看现有字段并用 smartsheet_update_fields 重命名默认字段,否则会多出无用列。', {
|
|
712
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
713
|
+
fields: z.array(z.object({
|
|
714
|
+
field_title: z.string(),
|
|
715
|
+
field_type: z.string(),
|
|
716
|
+
})).describe('要添加的字段列表'),
|
|
717
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
718
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
719
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
720
|
+
}, async ({ sheet_id, fields, url: docUrl, docid, robot_name }) => {
|
|
721
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
722
|
+
if (!url)
|
|
723
|
+
return errorContent;
|
|
724
|
+
return callDocTool(url, 'smartsheet_add_fields', { sheet_id, fields, url: docUrl, docid });
|
|
725
|
+
});
|
|
726
|
+
server.tool('smartsheet_update_fields', '更新智能表格子表字段的标题(不能更改字段类型)。', {
|
|
727
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
728
|
+
fields: z.array(z.object({
|
|
729
|
+
field_id: z.string(),
|
|
730
|
+
field_title: z.string(),
|
|
731
|
+
field_type: z.string(),
|
|
732
|
+
})).describe('要更新的字段列表'),
|
|
733
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
734
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
735
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
736
|
+
}, async ({ sheet_id, fields, url: docUrl, docid, robot_name }) => {
|
|
737
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
738
|
+
if (!url)
|
|
739
|
+
return errorContent;
|
|
740
|
+
return callDocTool(url, 'smartsheet_update_fields', { sheet_id, fields, url: docUrl, docid });
|
|
741
|
+
});
|
|
742
|
+
server.tool('smartsheet_delete_fields', '删除智能表格子表中的字段(列)(不可逆)。', {
|
|
743
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
744
|
+
field_ids: z.array(z.string()).describe('要删除的字段 ID 列表'),
|
|
745
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
746
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
747
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
748
|
+
}, async ({ sheet_id, field_ids, url: docUrl, docid, robot_name }) => {
|
|
749
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
750
|
+
if (!url)
|
|
751
|
+
return errorContent;
|
|
752
|
+
return callDocTool(url, 'smartsheet_delete_fields', { sheet_id, field_ids, url: docUrl, docid });
|
|
753
|
+
});
|
|
754
|
+
server.tool('smartsheet_get_records', '查询智能表格子表的所有记录。', {
|
|
755
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
756
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
757
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
758
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
759
|
+
}, async ({ sheet_id, url: docUrl, docid, robot_name }) => {
|
|
760
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
761
|
+
if (!url)
|
|
762
|
+
return errorContent;
|
|
763
|
+
return callDocTool(url, 'smartsheet_get_records', { sheet_id, url: docUrl, docid });
|
|
764
|
+
});
|
|
765
|
+
server.tool('smartsheet_add_records', '向智能表格子表添加记录(行)。单次建议不超过 500 行。values 的 key 必须是字段标题(field_title),不能用 field_id。', {
|
|
766
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
767
|
+
records: z.array(z.object({ values: z.record(z.string(), z.unknown()) })).describe('记录列表,values key 为字段标题'),
|
|
768
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
769
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
770
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
771
|
+
}, async ({ sheet_id, records, url: docUrl, docid, robot_name }) => {
|
|
772
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
773
|
+
if (!url)
|
|
774
|
+
return errorContent;
|
|
775
|
+
return callDocTool(url, 'smartsheet_add_records', { sheet_id, records, url: docUrl, docid });
|
|
776
|
+
});
|
|
777
|
+
server.tool('smartsheet_update_records', '更新智能表格子表中的记录(行)。单次建议不超过 500 行。', {
|
|
778
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
779
|
+
records: z.array(z.object({
|
|
780
|
+
record_id: z.string(),
|
|
781
|
+
values: z.record(z.string(), z.unknown()),
|
|
782
|
+
})).describe('要更新的记录列表,含 record_id 和 values'),
|
|
783
|
+
key_type: z.enum(['CELL_VALUE_KEY_TYPE_FIELD_TITLE', 'CELL_VALUE_KEY_TYPE_FIELD_ID']).describe('values 的 key 类型'),
|
|
784
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
785
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
786
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
787
|
+
}, async ({ sheet_id, records, key_type, url: docUrl, docid, robot_name }) => {
|
|
788
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
789
|
+
if (!url)
|
|
790
|
+
return errorContent;
|
|
791
|
+
return callDocTool(url, 'smartsheet_update_records', { sheet_id, records, key_type, url: docUrl, docid });
|
|
792
|
+
});
|
|
793
|
+
server.tool('smartsheet_delete_records', '删除智能表格子表中的记录(行)(不可逆)。单次建议不超过 500 行。', {
|
|
794
|
+
sheet_id: z.string().describe('子表 ID'),
|
|
795
|
+
record_ids: z.array(z.string()).describe('要删除的记录 ID 列表'),
|
|
796
|
+
url: z.string().optional().describe('文档访问链接,与 docid 二选一'),
|
|
797
|
+
docid: z.string().optional().describe('文档 docid,与 url 二选一'),
|
|
798
|
+
robot_name: z.string().optional().describe('指定机器人名称(多机器人时必填)'),
|
|
799
|
+
}, async ({ sheet_id, record_ids, url: docUrl, docid, robot_name }) => {
|
|
800
|
+
const { url, errorContent } = resolveDocUrl(robot_name);
|
|
801
|
+
if (!url)
|
|
802
|
+
return errorContent;
|
|
803
|
+
return callDocTool(url, 'smartsheet_delete_records', { sheet_id, record_ids, url: docUrl, docid });
|
|
804
|
+
});
|
|
805
|
+
logger.log('[mcp] 已注册 29 个工具(含 15 个文档代理工具)');
|
|
602
806
|
}
|