@vrs-soft/wecom-aibot-mcp 3.2.2 → 3.2.4
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 +86 -0
- package/package.json +1 -1
package/dist/channel-server.js
CHANGED
|
@@ -72,6 +72,50 @@ const MIME_BY_EXT = {
|
|
|
72
72
|
'.webp': 'image/webp', '.svg': 'image/svg+xml',
|
|
73
73
|
'.pdf': 'application/pdf', '.zip': 'application/zip',
|
|
74
74
|
};
|
|
75
|
+
async function sendWecomMediaFromFile(args) {
|
|
76
|
+
const abs = path.resolve(args.file_path);
|
|
77
|
+
if (!fs.existsSync(abs)) {
|
|
78
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'file_not_found', detail: abs }) }] };
|
|
79
|
+
}
|
|
80
|
+
let stat;
|
|
81
|
+
try {
|
|
82
|
+
stat = fs.statSync(abs);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'file_unreadable', detail: String(e) }) }] };
|
|
86
|
+
}
|
|
87
|
+
if (!stat.isFile()) {
|
|
88
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'not_a_file' }) }] };
|
|
89
|
+
}
|
|
90
|
+
const filename = args.filename || path.basename(abs);
|
|
91
|
+
const buf = fs.readFileSync(abs);
|
|
92
|
+
const headers = {
|
|
93
|
+
'Content-Type': 'application/octet-stream',
|
|
94
|
+
'Content-Length': String(buf.length),
|
|
95
|
+
'X-Target-User': args.target_user,
|
|
96
|
+
'X-Media-Type': args.media_type,
|
|
97
|
+
'X-Filename': encodeURIComponent(filename),
|
|
98
|
+
};
|
|
99
|
+
if (MCP_AUTH_TOKEN)
|
|
100
|
+
headers['Authorization'] = `Bearer ${MCP_AUTH_TOKEN}`;
|
|
101
|
+
if (args.robot_name)
|
|
102
|
+
headers['X-Robot'] = args.robot_name;
|
|
103
|
+
if (args.video_title)
|
|
104
|
+
headers['X-Video-Title'] = encodeURIComponent(args.video_title);
|
|
105
|
+
if (args.video_description)
|
|
106
|
+
headers['X-Video-Description'] = encodeURIComponent(args.video_description);
|
|
107
|
+
try {
|
|
108
|
+
const res = await fetch(`${MCP_URL}/api/v1/wecom/send_media`, { method: 'POST', headers, body: buf });
|
|
109
|
+
const text = await res.text();
|
|
110
|
+
if (!res.ok) {
|
|
111
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'http_failed', status: res.status, body: text.slice(0, 500) }) }] };
|
|
112
|
+
}
|
|
113
|
+
return { content: [{ type: 'text', text }] };
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'network_failed', detail: String(e) }) }] };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
75
119
|
async function uploadFileToHttp(args) {
|
|
76
120
|
const abs = path.resolve(args.file_path);
|
|
77
121
|
if (!fs.existsSync(abs)) {
|
|
@@ -657,6 +701,14 @@ function registerChannelTools(server) {
|
|
|
657
701
|
cc_id: z.string().describe('自己的 CC 标识(必须 = 文档的 to_cc)'),
|
|
658
702
|
doc_id: z.string().describe('文档 ID'),
|
|
659
703
|
}, async (params) => forwardToHttpMcp('get_document_info', params));
|
|
704
|
+
server.tool('delete_shared_file', '删除共享文件。仅 owner 可删。', {
|
|
705
|
+
cc_id: z.string().describe('自己的 CC 标识(必须 = owner)'),
|
|
706
|
+
file_id: z.string().describe('共享文件 ID'),
|
|
707
|
+
}, async (params) => forwardToHttpMcp('delete_shared_file', params));
|
|
708
|
+
server.tool('delete_document', '删除点对点文档。fromCc 或 toCc 任一方可删。', {
|
|
709
|
+
cc_id: z.string().describe('自己的 CC 标识(必须 = fromCc 或 toCc)'),
|
|
710
|
+
doc_id: z.string().describe('文档 ID'),
|
|
711
|
+
}, async (params) => forwardToHttpMcp('delete_document', params));
|
|
660
712
|
server.tool('get_shared_file_info', '查询单个共享文件的元数据(不返回 content)。共享池无权限校验,任何 CC 都可查。', {
|
|
661
713
|
cc_id: z.string().describe('自己的 CC 标识'),
|
|
662
714
|
file_id: z.string().describe('共享文件 ID'),
|
|
@@ -667,6 +719,40 @@ function registerChannelTools(server) {
|
|
|
667
719
|
save_as: z.string().optional().describe('可选:自定义文件名'),
|
|
668
720
|
}, async (params) => forwardToHttpMcp('accept_shared_file', params));
|
|
669
721
|
// ============================================
|
|
722
|
+
// 发媒体给 wecom 用户(v3.2.4)
|
|
723
|
+
// channel-server 本地读文件 + HTTP POST 字节流 → daemon uploadMedia + sendMediaMessage
|
|
724
|
+
// ============================================
|
|
725
|
+
server.tool('send_image_to_wecom_user', '把本地图片发送给 wecom 用户。channel-server 读文件 + POST 字节流 → daemon 调用 wecom uploadMedia + sendMediaMessage。文件字节不经 LLM context。', {
|
|
726
|
+
cc_id: z.string().describe('自己的 CC 标识(仅日志)'),
|
|
727
|
+
target_user: z.string().describe('目标 wecom userid(单聊用户ID,群聊用群ID/chatid)'),
|
|
728
|
+
file_path: z.string().describe('本地图片绝对路径(PNG/JPEG/GIF/WebP 等)'),
|
|
729
|
+
filename: z.string().optional().describe('可选:发到 wecom 端显示的文件名'),
|
|
730
|
+
robot_name: z.string().optional().describe('可选:指定通过哪个机器人发送'),
|
|
731
|
+
}, async (params) => sendWecomMediaFromFile({ ...params, media_type: 'image' }));
|
|
732
|
+
server.tool('send_file_to_wecom_user', '把本地任意文件(PDF/zip/docx 等)发送给 wecom 用户。', {
|
|
733
|
+
cc_id: z.string().describe('自己的 CC 标识'),
|
|
734
|
+
target_user: z.string().describe('目标 wecom userid 或 chatid'),
|
|
735
|
+
file_path: z.string().describe('本地文件绝对路径'),
|
|
736
|
+
filename: z.string().optional().describe('可选:wecom 显示的文件名(默认用 basename)'),
|
|
737
|
+
robot_name: z.string().optional().describe('可选:指定机器人'),
|
|
738
|
+
}, async (params) => sendWecomMediaFromFile({ ...params, media_type: 'file' }));
|
|
739
|
+
server.tool('send_video_to_wecom_user', '把本地视频文件发送给 wecom 用户。', {
|
|
740
|
+
cc_id: z.string().describe('自己的 CC 标识'),
|
|
741
|
+
target_user: z.string().describe('目标 wecom userid 或 chatid'),
|
|
742
|
+
file_path: z.string().describe('本地视频路径(MP4 等)'),
|
|
743
|
+
filename: z.string().optional(),
|
|
744
|
+
title: z.string().optional().describe('视频标题'),
|
|
745
|
+
description: z.string().optional().describe('视频描述'),
|
|
746
|
+
robot_name: z.string().optional(),
|
|
747
|
+
}, async ({ title, description, ...params }) => sendWecomMediaFromFile({ ...params, media_type: 'video', video_title: title, video_description: description }));
|
|
748
|
+
server.tool('send_voice_to_wecom_user', '把本地语音文件发送给 wecom 用户(amr 格式优先;wecom 对其他格式可能拒绝)。', {
|
|
749
|
+
cc_id: z.string().describe('自己的 CC 标识'),
|
|
750
|
+
target_user: z.string().describe('目标 wecom userid 或 chatid'),
|
|
751
|
+
file_path: z.string().describe('本地语音文件路径'),
|
|
752
|
+
filename: z.string().optional(),
|
|
753
|
+
robot_name: z.string().optional(),
|
|
754
|
+
}, async (params) => sendWecomMediaFromFile({ ...params, media_type: 'voice' }));
|
|
755
|
+
// ============================================
|
|
670
756
|
// 文件直传工具(v3.2.0)—— channel-server 本地读写 + HTTP 直传
|
|
671
757
|
// 解决远端 daemon 场景下 file_path/accept_* 不可用的 bug
|
|
672
758
|
// 文件字节全程不经 LLM context
|