@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 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
  ## 常用命令
@@ -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
@@ -468,12 +468,37 @@ if [[ "$WECHAT_MODE" != "true" ]]; then
468
468
  exit 0
469
469
  fi
470
470
 
471
- # 检查 MCP Server 是否在线
472
- HEALTH=$(curl -s -m 2 "http://127.0.0.1:$MCP_PORT/health" 2>/dev/null)
473
- log_debug "[$(date)] Health check: $HEALTH"
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)] Health check failed, exit 0"
476
- exit 0
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 "http://127.0.0.1:$MCP_PORT/approve" \\
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 "http://127.0.0.1:$MCP_PORT/approval_status/$TASK_ID" 2>/dev/null)
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 "http://127.0.0.1:$MCP_PORT/approval_status/$TASK_ID" 2>/dev/null)
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 "http://127.0.0.1:$MCP_PORT/approval_timeout/$TASK_ID" -H "Content-Type: application/json" -d '{"result":"deny","reason":"超时自动拒绝:删除操作需人工确认"}' > /dev/null 2>&1 &
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 "http://127.0.0.1:$MCP_PORT/approval_timeout/$TASK_ID" -H "Content-Type: application/json" -d '{"result":"allow-once","reason":"超时自动允许:项目内操作"}' > /dev/null 2>&1 &
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 "http://127.0.0.1:$MCP_PORT/approval_timeout/$TASK_ID" -H "Content-Type: application/json" -d '{"result":"deny","reason":"超时自动拒绝:项目外操作需人工确认"}' > /dev/null 2>&1 &
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
- # 检查 MCP Server 是否在线
687
- HEALTH=$(curl -s -m 2 "http://127.0.0.1:$MCP_PORT/health" 2>/dev/null)
688
- log_debug "[$(date)] Health check: $HEALTH"
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
- log_debug "[$(date)] MCP Server offline, exit 0 (allow complete)"
691
- exit 0
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
- // 删除全局 PermissionRequest hookhook enter_headless_mode 写入项目级别)
975
- if (settings.hooks && settings.hooks['PermissionRequest']) {
976
- // 只删除 wecom-aibot 相关的 hook
977
- settings.hooks['PermissionRequest'] = settings.hooks['PermissionRequest'].filter((hook) => !hook.hooks?.some?.((h) => h.command?.includes?.('wecom-aibot-mcp')));
978
- if (settings.hooks['PermissionRequest'].length === 0) {
979
- delete settings.hooks['PermissionRequest'];
980
- }
981
- if (Object.keys(settings.hooks).length === 0) {
982
- delete settings.hooks;
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrs-soft/wecom-aibot-mcp",
3
- "version": "2.4.5",
3
+ "version": "2.4.7",
4
4
  "description": "企业微信智能机器人 MCP 服务 - Claude Code 审批通道",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",