@vrs-soft/wecom-aibot-mcp 2.3.2 → 2.4.0
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 +51 -46
- package/dist/bin.js +154 -22
- package/dist/channel-server.js +125 -6
- package/dist/client-pool.d.ts +1 -0
- package/dist/config-wizard.d.ts +16 -2
- package/dist/config-wizard.js +283 -115
- package/dist/connection-manager.js +2 -2
- package/dist/daemon.js +1 -17
- package/dist/doc-proxy.d.ts +21 -0
- package/dist/doc-proxy.js +46 -0
- package/dist/http-server.js +11 -2
- package/dist/tools/index.js +212 -8
- package/package.json +1 -1
package/dist/daemon.js
CHANGED
|
@@ -47,23 +47,7 @@ class ConnectionDaemon {
|
|
|
47
47
|
}
|
|
48
48
|
loadAllRobots() {
|
|
49
49
|
const robots = [];
|
|
50
|
-
//
|
|
51
|
-
const mainConfigPath = path.join(CONFIG_DIR, 'config.json');
|
|
52
|
-
if (fs.existsSync(mainConfigPath)) {
|
|
53
|
-
try {
|
|
54
|
-
const config = JSON.parse(fs.readFileSync(mainConfigPath, 'utf-8'));
|
|
55
|
-
robots.push({
|
|
56
|
-
name: config.nameTag || 'default',
|
|
57
|
-
botId: config.botId,
|
|
58
|
-
secret: config.secret,
|
|
59
|
-
targetUserId: config.targetUserId,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
catch (err) {
|
|
63
|
-
this.log(`加载主配置失败: ${err}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// 机器人配置文件 (robot-*.json)
|
|
50
|
+
// 所有机器人配置(统一 robot-*.json 格式)
|
|
67
51
|
const files = fs.readdirSync(CONFIG_DIR).filter(f => f.startsWith('robot-') && f.endsWith('.json'));
|
|
68
52
|
for (const file of files) {
|
|
69
53
|
try {
|
|
@@ -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.js
CHANGED
|
@@ -23,7 +23,7 @@ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
|
23
23
|
import { registerTools } from './tools/index.js';
|
|
24
24
|
import { getClient, getConnectionState, getAllConnectionStates, connectAllRobots } from './connection-manager.js';
|
|
25
25
|
import { subscribeWecomMessage, getSubscriberCount } from './message-bus.js';
|
|
26
|
-
import { listAllRobots } from './config-wizard.js';
|
|
26
|
+
import { listAllRobots, VERSION, getAuthToken } from './config-wizard.js';
|
|
27
27
|
import { logger } from './logger.js';
|
|
28
28
|
// ESM 兼容的 __dirname
|
|
29
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -185,7 +185,6 @@ export function getOnlineCcIds() {
|
|
|
185
185
|
}
|
|
186
186
|
// 使用 Map 存储多个待处理审批(按 taskId 索引)
|
|
187
187
|
const pendingApprovals = new Map();
|
|
188
|
-
const VERSION = '2.0.0';
|
|
189
188
|
const transports = new Map();
|
|
190
189
|
const sseClients = new Map(); // clientId -> SSEClient
|
|
191
190
|
// 初始化 MCP Server(不再全局连接)
|
|
@@ -479,6 +478,16 @@ export async function startHttpServer(_server, port = HTTP_PORT) {
|
|
|
479
478
|
return;
|
|
480
479
|
}
|
|
481
480
|
const url = req.url || '/';
|
|
481
|
+
// Auth token 校验(排除 /health)
|
|
482
|
+
const authToken = getAuthToken();
|
|
483
|
+
if (authToken && url !== '/health') {
|
|
484
|
+
const authHeader = req.headers['authorization'];
|
|
485
|
+
if (authHeader !== `Bearer ${authToken}`) {
|
|
486
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
487
|
+
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
482
491
|
// MCP endpoint - 每个客户端一个独立的 server 和 transport
|
|
483
492
|
// POST /mcp: 初始化或调用工具
|
|
484
493
|
// GET /mcp: 建立 SSE 流
|
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, VERSION } 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';
|
|
@@ -249,7 +250,7 @@ npx @vrs-soft/wecom-aibot-mcp
|
|
|
249
250
|
content: [{
|
|
250
251
|
type: 'text',
|
|
251
252
|
text: JSON.stringify({
|
|
252
|
-
version:
|
|
253
|
+
version: VERSION,
|
|
253
254
|
requirements: {
|
|
254
255
|
// 权限配置需求
|
|
255
256
|
permissions: {
|
|
@@ -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
|
}
|