@vrs-soft/wecom-aibot-mcp 2.4.5 → 2.4.7
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 +6 -0
- package/dist/channel-server.js +5 -0
- package/dist/config-wizard.js +75 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,12 @@ npx @vrs-soft/wecom-aibot-mcp --stop # 停止
|
|
|
58
58
|
|
|
59
59
|
使用微信模式时告诉 Claude「**现在开始通过微信联系**」,会自动触发 `headless-mode` skill。
|
|
60
60
|
|
|
61
|
+
**Channel 模式下 Claude 的启动命令**:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
claude --dangerously-load-development-channels server:wecom-aibot-channel
|
|
65
|
+
```
|
|
66
|
+
|
|
61
67
|
---
|
|
62
68
|
|
|
63
69
|
## 常用命令
|
package/dist/channel-server.js
CHANGED
|
@@ -16,6 +16,7 @@ import * as fs from 'fs';
|
|
|
16
16
|
import * as path from 'path';
|
|
17
17
|
import * as os from 'os';
|
|
18
18
|
import { VERSION } from './config-wizard.js';
|
|
19
|
+
import { addPermissionHook } from './project-config.js';
|
|
19
20
|
const MCP_URL = process.env.MCP_URL || 'http://127.0.0.1:18963';
|
|
20
21
|
const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
|
|
21
22
|
// 构建带 auth 的 fetch headers
|
|
@@ -419,6 +420,10 @@ function registerChannelTools(server) {
|
|
|
419
420
|
if (parsed.ccId) {
|
|
420
421
|
logChannel('Got ccId, connecting SSE', { ccId: parsed.ccId, mode });
|
|
421
422
|
connectSSE(parsed.ccId);
|
|
423
|
+
// Channel 模式:在本地项目写入 PermissionRequest hook
|
|
424
|
+
const localProjectDir = project_dir || process.cwd();
|
|
425
|
+
const hookResult = addPermissionHook(localProjectDir);
|
|
426
|
+
logChannel('本地 PermissionRequest hook 已写入', { path: hookResult.path, success: hookResult.success });
|
|
422
427
|
// Channel 模式:过滤 heartbeat 信息,简化消息
|
|
423
428
|
if (mode === 'channel' || parsed.mode === 'channel') {
|
|
424
429
|
delete parsed.heartbeat; // Channel 模式不需要 heartbeat loop
|
package/dist/config-wizard.js
CHANGED
|
@@ -468,12 +468,37 @@ if [[ "$WECHAT_MODE" != "true" ]]; then
|
|
|
468
468
|
exit 0
|
|
469
469
|
fi
|
|
470
470
|
|
|
471
|
-
#
|
|
472
|
-
|
|
473
|
-
|
|
471
|
+
# 确定 MCP Server 地址(本地优先,失败则尝试远程 channel 配置)
|
|
472
|
+
MCP_BASE_URL="http://127.0.0.1:$MCP_PORT"
|
|
473
|
+
AUTH_ARGS=()
|
|
474
|
+
|
|
475
|
+
HEALTH=$(curl -s -m 2 "$MCP_BASE_URL/health" 2>/dev/null)
|
|
476
|
+
log_debug "[$(date)] Local health check: $HEALTH"
|
|
474
477
|
if ! echo "$HEALTH" | jq -e '.status == "ok"' > /dev/null 2>&1; then
|
|
475
|
-
log_debug "[$(date)]
|
|
476
|
-
|
|
478
|
+
log_debug "[$(date)] Local server not available, trying remote channel config..."
|
|
479
|
+
CLAUDE_JSON="$HOME/.claude.json"
|
|
480
|
+
if [[ -f "$CLAUDE_JSON" ]]; then
|
|
481
|
+
REMOTE_URL=$(jq -r '.mcpServers["wecom-aibot-channel"].env.MCP_URL // empty' "$CLAUDE_JSON" 2>/dev/null)
|
|
482
|
+
REMOTE_TOKEN=$(jq -r '.mcpServers["wecom-aibot-channel"].env.MCP_AUTH_TOKEN // empty' "$CLAUDE_JSON" 2>/dev/null)
|
|
483
|
+
if [[ -n "$REMOTE_URL" ]]; then
|
|
484
|
+
REMOTE_HEALTH=$(curl -s -m 5 \${REMOTE_TOKEN:+-H "Authorization: Bearer $REMOTE_TOKEN"} "$REMOTE_URL/health" 2>/dev/null)
|
|
485
|
+
log_debug "[$(date)] Remote health check ($REMOTE_URL): $REMOTE_HEALTH"
|
|
486
|
+
if echo "$REMOTE_HEALTH" | jq -e '.status == "ok"' > /dev/null 2>&1; then
|
|
487
|
+
MCP_BASE_URL="$REMOTE_URL"
|
|
488
|
+
[[ -n "$REMOTE_TOKEN" ]] && AUTH_ARGS=(-H "Authorization: Bearer $REMOTE_TOKEN")
|
|
489
|
+
log_debug "[$(date)] Using remote server: $MCP_BASE_URL"
|
|
490
|
+
else
|
|
491
|
+
log_debug "[$(date)] Remote health check failed, exit 0"
|
|
492
|
+
exit 0
|
|
493
|
+
fi
|
|
494
|
+
else
|
|
495
|
+
log_debug "[$(date)] No remote URL configured, exit 0"
|
|
496
|
+
exit 0
|
|
497
|
+
fi
|
|
498
|
+
else
|
|
499
|
+
log_debug "[$(date)] No ~/.claude.json found, exit 0"
|
|
500
|
+
exit 0
|
|
501
|
+
fi
|
|
477
502
|
fi
|
|
478
503
|
|
|
479
504
|
# 读取当前项目使用的机器人名称和 ccId
|
|
@@ -486,7 +511,8 @@ BODY=$(jq -n --arg tool_name "$TOOL_NAME" --argjson tool_input "$TOOL_INPUT" --a
|
|
|
486
511
|
'{"tool_name":$tool_name,"tool_input":$tool_input,"projectDir":$project_dir,"robotName":$robot_name,"ccId":$cc_id}')
|
|
487
512
|
|
|
488
513
|
log_debug "[$(date)] Sending approval request..."
|
|
489
|
-
RESPONSE=$(curl -s -m 10 -X POST "
|
|
514
|
+
RESPONSE=$(curl -s -m 10 -X POST "$MCP_BASE_URL/approve" \\
|
|
515
|
+
"\${AUTH_ARGS[@]}" \\
|
|
490
516
|
-H "Content-Type: application/json" \\
|
|
491
517
|
-d "$BODY")
|
|
492
518
|
|
|
@@ -515,7 +541,7 @@ while [[ $POLL_COUNT -lt $MAX_POLL ]]; do
|
|
|
515
541
|
sleep 2
|
|
516
542
|
POLL_COUNT=$((POLL_COUNT + 1))
|
|
517
543
|
|
|
518
|
-
STATUS=$(curl -s -m 3 "
|
|
544
|
+
STATUS=$(curl -s -m 3 "\${AUTH_ARGS[@]}" "$MCP_BASE_URL/approval_status/$TASK_ID" 2>/dev/null)
|
|
519
545
|
RESULT=$(echo "$STATUS" | jq -r '.result // empty')
|
|
520
546
|
log_debug "[$(date)] Poll $POLL_COUNT/$MAX_POLL: result=$RESULT"
|
|
521
547
|
|
|
@@ -543,7 +569,7 @@ if [[ "$AUTO_APPROVE" != "true" ]]; then
|
|
|
543
569
|
# autoApprove 关闭,继续无限等待用户响应
|
|
544
570
|
while true; do
|
|
545
571
|
sleep 2
|
|
546
|
-
STATUS=$(curl -s -m 3 "
|
|
572
|
+
STATUS=$(curl -s -m 3 "\${AUTH_ARGS[@]}" "$MCP_BASE_URL/approval_status/$TASK_ID" 2>/dev/null)
|
|
547
573
|
RESULT=$(echo "$STATUS" | jq -r '.result // empty')
|
|
548
574
|
|
|
549
575
|
if [[ "$RESULT" == "allow-once" || "$RESULT" == "allow-always" ]]; then
|
|
@@ -578,7 +604,7 @@ log_debug "[$(date)] IS_DELETE: $IS_DELETE"
|
|
|
578
604
|
if [[ $IS_DELETE -eq 1 ]]; then
|
|
579
605
|
log_debug "[$(date)] Auto-deny: delete operation"
|
|
580
606
|
# 通知 MCP Server 发送微信消息
|
|
581
|
-
curl -s -m 5 -X POST "
|
|
607
|
+
curl -s -m 5 -X POST "$MCP_BASE_URL/approval_timeout/$TASK_ID" "\${AUTH_ARGS[@]}" -H "Content-Type: application/json" -d '{"result":"deny","reason":"超时自动拒绝:删除操作需人工确认"}' > /dev/null 2>&1 &
|
|
582
608
|
printf '%s\\n' '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"deny","message":"超时自动拒绝:删除操作需人工确认"}}}'
|
|
583
609
|
exit 0
|
|
584
610
|
fi
|
|
@@ -617,12 +643,12 @@ log_debug "[$(date)] IS_IN_PROJECT: $IS_IN_PROJECT"
|
|
|
617
643
|
if [[ $IS_IN_PROJECT -eq 1 ]]; then
|
|
618
644
|
log_debug "[$(date)] Auto-allow: project operation"
|
|
619
645
|
# 通知 MCP Server 发送微信消息
|
|
620
|
-
curl -s -m 5 -X POST "
|
|
646
|
+
curl -s -m 5 -X POST "$MCP_BASE_URL/approval_timeout/$TASK_ID" "\${AUTH_ARGS[@]}" -H "Content-Type: application/json" -d '{"result":"allow-once","reason":"超时自动允许:项目内操作"}' > /dev/null 2>&1 &
|
|
621
647
|
printf '%s\\n' '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow","message":"超时自动允许:项目内操作"}}}'
|
|
622
648
|
else
|
|
623
649
|
log_debug "[$(date)] Auto-deny: outside project"
|
|
624
650
|
# 通知 MCP Server 发送微信消息
|
|
625
|
-
curl -s -m 5 -X POST "
|
|
651
|
+
curl -s -m 5 -X POST "$MCP_BASE_URL/approval_timeout/$TASK_ID" "\${AUTH_ARGS[@]}" -H "Content-Type: application/json" -d '{"result":"deny","reason":"超时自动拒绝:项目外操作需人工确认"}' > /dev/null 2>&1 &
|
|
626
652
|
printf '%s\\n' '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"deny","message":"超时自动拒绝:项目外操作需人工确认"}}}'
|
|
627
653
|
fi
|
|
628
654
|
`;
|
|
@@ -683,12 +709,35 @@ if [[ "$AUTO_APPROVE" != "true" ]]; then
|
|
|
683
709
|
exit 0
|
|
684
710
|
fi
|
|
685
711
|
|
|
686
|
-
#
|
|
687
|
-
|
|
688
|
-
|
|
712
|
+
# 确定 MCP Server 地址(本地优先,失败则尝试远程 channel 配置)
|
|
713
|
+
MCP_BASE_URL="http://127.0.0.1:$MCP_PORT"
|
|
714
|
+
AUTH_ARGS=()
|
|
715
|
+
|
|
716
|
+
HEALTH=$(curl -s -m 2 "$MCP_BASE_URL/health" 2>/dev/null)
|
|
717
|
+
log_debug "[$(date)] Local health check: $HEALTH"
|
|
689
718
|
if ! echo "$HEALTH" | jq -e '.status == "ok"' > /dev/null 2>&1; then
|
|
690
|
-
|
|
691
|
-
|
|
719
|
+
CLAUDE_JSON="$HOME/.claude.json"
|
|
720
|
+
if [[ -f "$CLAUDE_JSON" ]]; then
|
|
721
|
+
REMOTE_URL=$(jq -r '.mcpServers["wecom-aibot-channel"].env.MCP_URL // empty' "$CLAUDE_JSON" 2>/dev/null)
|
|
722
|
+
REMOTE_TOKEN=$(jq -r '.mcpServers["wecom-aibot-channel"].env.MCP_AUTH_TOKEN // empty' "$CLAUDE_JSON" 2>/dev/null)
|
|
723
|
+
if [[ -n "$REMOTE_URL" ]]; then
|
|
724
|
+
REMOTE_HEALTH=$(curl -s -m 5 \${REMOTE_TOKEN:+-H "Authorization: Bearer $REMOTE_TOKEN"} "$REMOTE_URL/health" 2>/dev/null)
|
|
725
|
+
if echo "$REMOTE_HEALTH" | jq -e '.status == "ok"' > /dev/null 2>&1; then
|
|
726
|
+
MCP_BASE_URL="$REMOTE_URL"
|
|
727
|
+
[[ -n "$REMOTE_TOKEN" ]] && AUTH_ARGS=(-H "Authorization: Bearer $REMOTE_TOKEN")
|
|
728
|
+
log_debug "[$(date)] Using remote server: $MCP_BASE_URL"
|
|
729
|
+
else
|
|
730
|
+
log_debug "[$(date)] MCP Server offline, exit 0 (allow complete)"
|
|
731
|
+
exit 0
|
|
732
|
+
fi
|
|
733
|
+
else
|
|
734
|
+
log_debug "[$(date)] MCP Server offline, exit 0 (allow complete)"
|
|
735
|
+
exit 0
|
|
736
|
+
fi
|
|
737
|
+
else
|
|
738
|
+
log_debug "[$(date)] MCP Server offline, exit 0 (allow complete)"
|
|
739
|
+
exit 0
|
|
740
|
+
fi
|
|
692
741
|
fi
|
|
693
742
|
|
|
694
743
|
# 获取 ccId
|
|
@@ -971,19 +1020,18 @@ function writeMcpPermissions() {
|
|
|
971
1020
|
if (!existingPerms.has(perm))
|
|
972
1021
|
settings.permissions.allow.push(perm);
|
|
973
1022
|
}
|
|
974
|
-
//
|
|
975
|
-
if (settings.hooks
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
}
|
|
1023
|
+
// 注册全局 PermissionRequest hook(支持 channel 模式,hook 内部有 wechatMode 检查)
|
|
1024
|
+
if (!settings.hooks)
|
|
1025
|
+
settings.hooks = {};
|
|
1026
|
+
if (!settings.hooks['PermissionRequest'])
|
|
1027
|
+
settings.hooks['PermissionRequest'] = [];
|
|
1028
|
+
const hookCommand = HOOK_SCRIPT_PATH;
|
|
1029
|
+
const alreadyRegistered = settings.hooks['PermissionRequest'].some((entry) => entry.hooks?.some?.((h) => h.command === hookCommand));
|
|
1030
|
+
if (!alreadyRegistered) {
|
|
1031
|
+
settings.hooks['PermissionRequest'].push({ hooks: [{ type: 'command', command: hookCommand }] });
|
|
984
1032
|
}
|
|
985
1033
|
fs.writeFileSync(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2));
|
|
986
|
-
// 确保 hook
|
|
1034
|
+
// 确保 hook 脚本文件存在
|
|
987
1035
|
writeHookScript();
|
|
988
1036
|
}
|
|
989
1037
|
catch (err) {
|