@vrs-soft/wecom-aibot-mcp 1.0.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 ADDED
@@ -0,0 +1,426 @@
1
+ # @vrs-soft/wecom-aibot-mcp
2
+
3
+ 企业微信智能机器人 MCP 服务 - Claude Code 远程审批通道
4
+
5
+ > 通过企业微信智能机器人实现 Claude Code 的远程审批和消息推送,离开电脑也能处理决策请求。
6
+
7
+ ## 功能特性
8
+
9
+ - 🔐 **远程审批**:敏感操作通过微信卡片审批,支持"允许一次/永久允许/拒绝"
10
+ - 💬 **消息推送**:任务进度、完成通知实时推送到微信
11
+ - 📱 **Headless 模式**:离开电脑时切换到微信交互
12
+ - 🔄 **多实例支持**:多个 Claude Code 可同时运行,自动分配端口
13
+ - 🧹 **自动清理**:进程退出后自动清理残留文件
14
+
15
+ ## 架构
16
+
17
+ ```
18
+ ┌─────────────────┐ MCP (stdio) ┌──────────────────┐
19
+ │ Claude Code │ ──────────────────▶ │ wecom-aibot-mcp │
20
+ │ (MCP Client) │ │ MCP Server │
21
+ └─────────────────┘ └──────────────────┘
22
+
23
+ WebSocket 长连接
24
+
25
+ ┌───────────────────┐
26
+ │ 企业微信服务器 │
27
+ │ wss://openws... │
28
+ └───────────────────┘
29
+
30
+
31
+ ┌───────────────────┐
32
+ │ 用户企业微信客户端 │
33
+ │ (手机/桌面) │
34
+ └───────────────────┘
35
+ ```
36
+
37
+ ### 审批流程
38
+
39
+ ```
40
+ Claude 请求执行敏感操作(Bash/Write/Edit 等)
41
+
42
+ PermissionRequest Hook 拦截
43
+
44
+ ┌────────────────────────┐
45
+ │ 检查 headless 模式状态 │
46
+ └────────────────────────┘
47
+
48
+ ┌───────┴───────┐
49
+ ↓ ↓
50
+ 非 headless headless
51
+ ↓ ↓
52
+ 终端确认框 发送微信审批卡片
53
+ │ │
54
+ │ 用户点击按钮
55
+ │ │
56
+ └───────┬───────┘
57
+
58
+ 执行或拒绝操作
59
+ ```
60
+
61
+ ## 安装
62
+
63
+ ### 前置要求
64
+
65
+ - Node.js >= 18
66
+ - 企业微信账号(有创建机器人权限)
67
+ - Claude Code 已安装
68
+
69
+ ### 方式一:npx 直接运行(推荐)
70
+
71
+ ```bash
72
+ npx @vrs-soft/wecom-aibot-mcp
73
+ ```
74
+
75
+ ### 方式二:全局安装
76
+
77
+ ```bash
78
+ npm install -g @vrs-soft/wecom-aibot-mcp
79
+ wecom-aibot-mcp
80
+ ```
81
+
82
+ ### 方式三:从源码安装
83
+
84
+ ```bash
85
+ git clone https://github.com/eric2877/wecom-aibot-mcp.git
86
+ cd wecom-aibot-mcp
87
+ npm install
88
+ npm run build
89
+ npm link
90
+ ```
91
+
92
+ ## 快速开始
93
+
94
+ ### 第一步:创建企业微信机器人
95
+
96
+ 1. 登录企业微信管理后台:https://work.weixin.qq.com
97
+ 2. 进入「管理工具」→「智能机器人」
98
+ 3. 点击「创建机器人」→「手动创建」
99
+ 4. 填写机器人名称(如"Claude 审批助手")
100
+ 5. 在「API 配置」区域:
101
+ - 连接方式选择「**使用长连接**」
102
+ - 点击「获取 Secret」
103
+ 6. 记录以下信息:
104
+ - **Bot ID**:机器人唯一标识
105
+ - **Secret**:长连接密钥
106
+
107
+ > ⚠️ 每个机器人同时只能保持一个 WebSocket 长连接
108
+
109
+ ### 第二步:获取目标用户 ID
110
+
111
+ 目标用户 ID 是审批消息发送给谁。获取方式:
112
+
113
+ 1. 在企业微信通讯录中找到目标用户
114
+ 2. 查看用户详情,**账号**字段即为 User ID
115
+ 3. 例如:`zhangsan`、`lisi`
116
+
117
+ ### 第三步:配置 Claude Code
118
+
119
+ 编辑 `~/.claude.json`(不存在则创建):
120
+
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "wecom-aibot": {
125
+ "command": "npx",
126
+ "args": ["@vrs-soft/wecom-aibot-mcp"],
127
+ "env": {
128
+ "WECOM_BOT_ID": "你的Bot ID",
129
+ "WECOM_SECRET": "你的Secret",
130
+ "WECOM_TARGET_USER": "目标用户ID"
131
+ }
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### 第四步:重启 Claude Code
138
+
139
+ 1. 运行 `/mcp` 命令
140
+ 2. 选择「Reconnect」重新连接 MCP 服务
141
+ 3. 首次连接时会自动:
142
+ - 检查配置完整性
143
+ - 注册权限预授权
144
+ - 生成审批 Hook 脚本
145
+
146
+ ### 第五步:验证连接
147
+
148
+ ```
149
+ 你:检查微信连接状态
150
+ Claude:(调用 check_connection)
151
+ 返回:{"connected":true,"defaultTargetUser":"zhangsan"}
152
+ ```
153
+
154
+ ## 使用示例
155
+
156
+ ### 场景 1:发送任务完成通知
157
+
158
+ ```
159
+ 你:帮我重构这个函数,完成后微信通知我
160
+
161
+ Claude:[执行重构操作...]
162
+
163
+ Claude:(调用 send_message)
164
+ 微信收到:
165
+ ┌─────────────────────────┐
166
+ │ 【完成】函数重构完成 │
167
+ │ │
168
+ │ 已将 parseData 函数拆分 │
169
+ │ 为 3 个独立函数,提高可读性│
170
+ └─────────────────────────┘
171
+ ```
172
+
173
+ ### 场景 2:远程审批(Headless 模式)
174
+
175
+ ```
176
+ 你:我要离开电脑前了,现在开始通过微信联系
177
+
178
+ Claude:(调用 enter_headless_mode)
179
+ 微信收到:【进度】已进入微信模式,所有交互将通过企业微信进行。
180
+
181
+ [你离开电脑,Claude 需要执行删除文件操作]
182
+
183
+ 微信收到审批卡片:
184
+ ┌─────────────────────────┐
185
+ │ 【待审批】Bash │
186
+ │ │
187
+ │ 执行命令: rm -rf dist │
188
+ │ │
189
+ │ [允许一次] [永久允许] [拒绝]│
190
+ └─────────────────────────┘
191
+
192
+ [你在手机点击"允许一次"]
193
+
194
+ Claude 继续执行...
195
+
196
+ 你:我回来了
197
+
198
+ Claude:(调用 exit_headless_mode)
199
+ 微信收到:【进度】已退出微信模式,恢复终端交互。
200
+ ```
201
+
202
+ ### 场景 3:群聊机器人
203
+
204
+ 将机器人拉入群聊,@机器人 即可触发:
205
+
206
+ ```
207
+ 群聊中:
208
+ 张三:@Claude助手 帮我查看服务器日志
209
+
210
+ Claude:(执行命令,通过 send_message 发送结果到群聊)
211
+ 微信收到:
212
+ ┌─────────────────────────┐
213
+ │ 📋 服务器日志摘要 │
214
+ │ │
215
+ │ 最近 10 条错误日志... │
216
+ └─────────────────────────┘
217
+ ```
218
+
219
+ ### 场景 4:长时间任务监控
220
+
221
+ ```
222
+ 你:帮我跑一遍测试套件,有失败的通知我
223
+
224
+ Claude:(调用 send_message)
225
+ 微信收到:【进度】开始运行测试套件,共 128 个用例...
226
+
227
+ [测试运行中...]
228
+
229
+ Claude:(调用 send_message)
230
+ 微信收到:
231
+ ┌─────────────────────────┐
232
+ │ ⚠️ 测试失败 │
233
+ │ │
234
+ │ 失败用例:test/auth.spec.ts│
235
+ │ 原因:登录超时 │
236
+ │ │
237
+ │ 总计:125 通过,3 失败 │
238
+ └─────────────────────────┘
239
+ ```
240
+
241
+ ### 场景 5:定时任务提醒
242
+
243
+ 结合 Cron 工具实现定时提醒:
244
+
245
+ ```
246
+ 你:每天早上 9 点提醒我开站会
247
+
248
+ Claude:(设置定时任务)
249
+
250
+ 每天 9:00 微信收到:
251
+ ┌─────────────────────────┐
252
+ │ ⏰ 每日提醒 │
253
+ │ │
254
+ │ 该开站会了! │
255
+ └─────────────────────────┘
256
+ ```
257
+
258
+ ## MCP 工具
259
+
260
+ ### 消息类
261
+
262
+ | 工具 | 说明 | 参数 |
263
+ |------|------|------|
264
+ | `send_message` | 发送消息到微信 | `content`(内容), `target_user`(可选) |
265
+ | `get_pending_messages` | 获取用户消息 | `clear`(是否清空队列) |
266
+
267
+ ### 审批类
268
+
269
+ | 工具 | 说明 | 参数 |
270
+ |------|------|------|
271
+ | `send_approval_request` | 发送审批卡片 | `title`, `description`, `request_id` |
272
+ | `get_approval_result` | 获取审批结果 | `task_id` |
273
+
274
+ ### 模式控制
275
+
276
+ | 工具 | 说明 |
277
+ |------|------|
278
+ | `enter_headless_mode` | 进入微信审批模式 |
279
+ | `exit_headless_mode` | 退出微信审批模式 |
280
+ | `check_connection` | 检查连接状态 |
281
+
282
+ ### 辅助类
283
+
284
+ | 工具 | 说明 |
285
+ |------|------|
286
+ | `get_setup_guide` | 获取安装指南 |
287
+ | `add_robot_config` | 生成新机器人配置片段 |
288
+
289
+ ## 多用户配置
290
+
291
+ ### 场景:团队共享
292
+
293
+ 每个开发者使用独立的机器人:
294
+
295
+ ```json
296
+ {
297
+ "mcpServers": {
298
+ "wecom-aibot-zhangsan": {
299
+ "command": "npx",
300
+ "args": ["@vrs-soft/wecom-aibot-mcp"],
301
+ "env": {
302
+ "WECOM_BOT_ID": "bot_zhangsan",
303
+ "WECOM_SECRET": "secret_zhangsan",
304
+ "WECOM_TARGET_USER": "zhangsan"
305
+ }
306
+ },
307
+ "wecom-aibot-lisi": {
308
+ "command": "npx",
309
+ "args": ["@vrs-soft/wecom-aibot-mcp"],
310
+ "env": {
311
+ "WECOM_BOT_ID": "bot_lisi",
312
+ "WECOM_SECRET": "secret_lisi",
313
+ "WECOM_TARGET_USER": "lisi"
314
+ }
315
+ }
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### 多实例端口分配
321
+
322
+ 当多个 Claude Code 实例同时运行时,HTTP 服务端口自动递增:
323
+
324
+ - 实例 1:端口 18963
325
+ - 实例 2:端口 18964
326
+ - 实例 3:端口 18965
327
+ - ...
328
+
329
+ 端口文件存储在 `~/.wecom-aibot-mcp/port-{PID}`,进程退出后自动清理。
330
+
331
+ ## 环境变量
332
+
333
+ | 变量 | 说明 | 必填 | 示例 |
334
+ |------|------|------|------|
335
+ | `WECOM_BOT_ID` | 机器人 ID | ✅ | `bot_abc123` |
336
+ | `WECOM_SECRET` | 机器人密钥 | ✅ | `xyz789...` |
337
+ | `WECOM_TARGET_USER` | 默认目标用户 | ✅ | `zhangsan` |
338
+
339
+ ## 故障排查
340
+
341
+ ### 无法收到消息
342
+
343
+ **可能原因**:
344
+ - 机器人未添加到通讯录
345
+ - 群聊中未 @机器人
346
+ - WebSocket 连接断开
347
+
348
+ **解决方法**:
349
+ ```
350
+ 1. 在企业微信中搜索机器人名称,添加到通讯录
351
+ 2. 群聊中必须 @机器人 才能触发消息
352
+ 3. 运行 check_connection 检查连接状态
353
+ ```
354
+
355
+ ### 连接失败
356
+
357
+ **检查项**:
358
+ ```bash
359
+ # 1. 检查网络连通性
360
+ curl -v wss://openws.work.weixin.qq.com
361
+
362
+ # 2. 确认凭证正确
363
+ cat ~/.wecom-aibot-mcp/config.json
364
+
365
+ # 3. 查看进程日志
366
+ # 重启 Claude Code,观察启动日志
367
+ ```
368
+
369
+ ### 审批没发到微信
370
+
371
+ **检查 headless 状态**:
372
+ ```bash
373
+ # 查看状态文件
374
+ ls ~/.wecom-aibot-mcp/headless-*
375
+
376
+ # 如果没有文件,说明不在 headless 模式
377
+ # 需要告诉 Claude:「现在开始通过微信联系」
378
+ ```
379
+
380
+ ### 端口文件残留
381
+
382
+ ```bash
383
+ # 查看残留文件
384
+ ls ~/.wecom-aibot-mcp/port-*
385
+
386
+ # 重启 MCP 服务会自动清理孤儿文件
387
+ # 或手动清理
388
+ rm ~/.wecom-aibot-mcp/port-*
389
+ ```
390
+
391
+ ## 安全建议
392
+
393
+ 1. **保护凭证**:Bot ID 和 Secret 不要提交到代码仓库
394
+ 2. **使用环境变量**:通过 `env` 传递敏感信息,而不是硬编码
395
+ 3. **定期轮换**:建议每 3 个月更换一次 Secret
396
+ 4. **权限最小化**:机器人的可见范围设置为需要的用户/部门即可
397
+
398
+ ## 开发
399
+
400
+ ```bash
401
+ # 克隆仓库
402
+ git clone https://github.com/eric2877/wecom-aibot-mcp.git
403
+ cd wecom-aibot-mcp
404
+
405
+ # 安装依赖
406
+ npm install
407
+
408
+ # 开发模式(自动重新编译)
409
+ npm run dev
410
+
411
+ # 构建
412
+ npm run build
413
+
414
+ # 运行
415
+ npm start
416
+ ```
417
+
418
+ ## License
419
+
420
+ MIT
421
+
422
+ ## 相关链接
423
+
424
+ - [企业微信智能机器人文档](https://developer.work.weixin.qq.com/document/path/101039)
425
+ - [Claude Code 文档](https://docs.anthropic.com/claude-code)
426
+ - [MCP 协议规范](https://modelcontextprotocol.io)
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * wecom-aibot-mcp - 企业微信智能机器人 MCP 服务
4
+ *
5
+ * npx 运行入口
6
+ */
7
+ import { getOrInitConfig, runConfigWizard, loadConfig, ensureHookInstalled } from './config-wizard.js';
8
+ import { initClient } from './client.js';
9
+ import { registerTools } from './tools/index.js';
10
+ import { startHttpServer } from './http-server.js';
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
+ const VERSION = '1.0.0';
14
+ function showHelp() {
15
+ console.log(`
16
+ 企业微信智能机器人 MCP 服务 v${VERSION}
17
+
18
+ 安装:
19
+ npx @vrs-soft/wecom-aibot-mcp
20
+
21
+ 用法:
22
+ npx @vrs-soft/wecom-aibot-mcp [选项]
23
+
24
+ 选项:
25
+ --help, -h 显示帮助信息
26
+ --version, -v 显示版本号
27
+ --config 重新配置(修改 Bot ID / Secret / 目标用户)
28
+ --status 显示当前配置状态
29
+
30
+ 配置方式(按优先级):
31
+ 1. 环境变量(推荐多实例场景):
32
+ WECOM_BOT_ID 机器人 ID
33
+ WECOM_SECRET 机器人密钥
34
+ WECOM_TARGET_USER 默认目标用户 ID
35
+
36
+ 2. 首次运行时自动启动配置向导
37
+
38
+ Claude Code 配置示例:
39
+ 在 ~/.claude.json 中添加:
40
+
41
+ {
42
+ "mcpServers": {
43
+ "wecom-aibot": {
44
+ "command": "npx",
45
+ "args": ["@vrs-soft/wecom-aibot-mcp"],
46
+ "env": {
47
+ "WECOM_BOT_ID": "your_bot_id",
48
+ "WECOM_SECRET": "your_secret",
49
+ "WECOM_TARGET_USER": "your_userid"
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ 多用户/多机器人配置:
56
+ 不同用户使用不同机器人,配置多个实例:
57
+
58
+ {
59
+ "mcpServers": {
60
+ "wecom-aibot-user1": {
61
+ "command": "npx",
62
+ "args": ["@vrs-soft/wecom-aibot-mcp"],
63
+ "env": {
64
+ "WECOM_BOT_ID": "bot_user1",
65
+ "WECOM_SECRET": "secret_user1",
66
+ "WECOM_TARGET_USER": "user1"
67
+ }
68
+ },
69
+ "wecom-aibot-user2": {
70
+ "command": "npx",
71
+ "args": ["@vrs-soft/wecom-aibot-mcp"],
72
+ "env": {
73
+ "WECOM_BOT_ID": "bot_user2",
74
+ "WECOM_SECRET": "secret_user2",
75
+ "WECOM_TARGET_USER": "user2"
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ 更多信息: https://github.com/eric2877/wecom-aibot-mcp
82
+ `);
83
+ }
84
+ function showVersion() {
85
+ console.log(`wecom-aibot-mcp v${VERSION}`);
86
+ }
87
+ function showStatus() {
88
+ const config = loadConfig();
89
+ if (config) {
90
+ console.log('当前配置:');
91
+ console.log(` Bot ID: ${config.botId}`);
92
+ console.log(` Secret: ${config.secret.slice(0, 8)}...${config.secret.slice(-4)}`);
93
+ console.log(` 目标用户: ${config.targetUserId}`);
94
+ }
95
+ else {
96
+ console.log('尚未配置,请运行 npx wecom-aibot-mcp 启动配置向导');
97
+ }
98
+ }
99
+ async function main() {
100
+ const args = process.argv.slice(2);
101
+ // 解析命令行参数
102
+ if (args.includes('--help') || args.includes('-h')) {
103
+ showHelp();
104
+ process.exit(0);
105
+ }
106
+ if (args.includes('--version') || args.includes('-v')) {
107
+ showVersion();
108
+ process.exit(0);
109
+ }
110
+ if (args.includes('--status')) {
111
+ showStatus();
112
+ process.exit(0);
113
+ }
114
+ const reconfig = args.includes('--config');
115
+ console.log('');
116
+ console.log(' ╔════════════════════════════════════════════════════════╗');
117
+ console.log(` ║ 企业微信智能机器人 MCP 服务 v${VERSION} ║`);
118
+ console.log(' ║ Claude Code 审批通道 ║');
119
+ console.log(' ╚════════════════════════════════════════════════════════╝');
120
+ console.log('');
121
+ // 获取或初始化配置
122
+ let config;
123
+ if (reconfig) {
124
+ console.log('[config] 重新配置模式\n');
125
+ config = await runConfigWizard();
126
+ }
127
+ else {
128
+ config = await getOrInitConfig();
129
+ }
130
+ // 确保 hook 已安装(幂等,每次启动检查)
131
+ ensureHookInstalled();
132
+ // 初始化 WebSocket 客户端
133
+ console.log(`[mcp] 初始化企业微信客户端...`);
134
+ console.log(`[mcp] 默认目标用户: ${config.targetUserId}`);
135
+ const wecomClient = initClient(config.botId, config.secret, config.targetUserId);
136
+ // 启动本地 HTTP 服务(用于 hooks 审批)
137
+ await startHttpServer(wecomClient);
138
+ // 创建 MCP Server
139
+ const server = new McpServer({
140
+ name: 'wecom-aibot-mcp',
141
+ version: VERSION,
142
+ });
143
+ // 注册工具
144
+ registerTools(server, wecomClient);
145
+ // 启动 MCP 服务(stdio 模式)
146
+ console.log('[mcp] 启动 MCP Server (stdio)...');
147
+ const transport = new StdioServerTransport();
148
+ await server.connect(transport);
149
+ // 定期清理过期消息
150
+ setInterval(() => {
151
+ wecomClient.cleanupMessages();
152
+ }, 60000);
153
+ console.log('[mcp] MCP Server 已就绪');
154
+ }
155
+ main().catch((err) => {
156
+ console.error('[mcp] 启动失败:', err);
157
+ process.exit(1);
158
+ });
@@ -0,0 +1,40 @@
1
+ interface ApprovalRecord {
2
+ taskId: string;
3
+ resolved: boolean;
4
+ result?: 'allow-once' | 'allow-always' | 'deny';
5
+ timestamp: number;
6
+ toolName?: string;
7
+ }
8
+ interface MessageRecord {
9
+ msgid: string;
10
+ content: string;
11
+ timestamp: number;
12
+ from_userid: string;
13
+ chatid: string;
14
+ chattype: 'single' | 'group';
15
+ }
16
+ declare class WecomClient {
17
+ private wsClient;
18
+ private approvals;
19
+ private messages;
20
+ private connected;
21
+ private targetUserId;
22
+ constructor(botId: string, secret: string, targetUserId: string);
23
+ private setupEventHandlers;
24
+ private handleMessage;
25
+ private handleApprovalResponse;
26
+ connect(): void;
27
+ disconnect(): void;
28
+ isConnected(): boolean;
29
+ getDefaultTargetUser(): string;
30
+ sendText(content: string, targetUser?: string): Promise<boolean>;
31
+ sendApprovalRequest(title: string, description: string, requestId: string, targetUser?: string): Promise<string>;
32
+ getApprovalResult(taskId: string): 'pending' | 'allow-once' | 'allow-always' | 'deny';
33
+ getPendingApprovals(): string[];
34
+ getLatestMessage(afterTimestamp: number): MessageRecord | undefined;
35
+ getPendingMessages(clear?: boolean): MessageRecord[];
36
+ cleanupMessages(maxAgeMs?: number): void;
37
+ }
38
+ export declare function initClient(botId: string, secret: string, targetUserId: string): WecomClient;
39
+ export declare function getClient(): WecomClient;
40
+ export { WecomClient, ApprovalRecord, MessageRecord };