Files
ai-proj-helper/init.sh
John Qiu 58516a57a9 feat: add comprehensive MCP verification in init.sh
Three-step verification after setup:
1. Server connection test (initialize → HTTP 200)
2. Tool registration test (tools/list → tool count)
3. Claude Code MCP test (claude mcp list → Connected)

Uses Mcp-Session-Id from initialize response for tools/list call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 13:25:05 +10:30

277 lines
10 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ai-proj-helper 初始化脚本
# 配置 MCP 连接 + 安装技能到 ~/.claude/skills/
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
MCP_CONFIG="$HOME/.claude/.mcp.json"
MCP_BRIDGE_DIR="$SCRIPT_DIR/mcp-task-bridge"
CONFIG_FILE="$SCRIPT_DIR/claude-config.yaml"
API_BASE="https://ai.pipexerp.com/api/v1"
SSE_URL="${API_BASE}/mcp/sse"
# Default values
MODE=""
TOKEN=""
# ── Parse command line arguments ──────────────────────────────────────
while [[ $# -gt 0 ]]; do
case $1 in
--mode) MODE="$2"; shift 2 ;;
--token) TOKEN="$2"; shift 2 ;;
-h|--help)
echo "Usage: ./init.sh [--mode remote|stdio] [--token MCP_API_KEY]"
echo ""
echo "Options:"
echo " --mode MCP connection mode: remote (Streamable HTTP, recommended) or stdio (local Node.js)"
echo " --token MCP API Key (aiproj_pk_xxx)"
echo ""
echo "Without arguments, runs in interactive mode."
exit 0
;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
# ── Read config defaults ─────────────────────────────────────────────
read_config_value() {
local key="$1"
local default="$2"
if [ -f "$CONFIG_FILE" ]; then
local val
val=$(grep "^ ${key}:" "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/^[^:]*: *//' | sed 's/ *#.*//' | tr -d '"' | tr -d "'")
if [ -n "$val" ]; then
echo "$val"
return
fi
fi
echo "$default"
}
CONFIG_MODE=$(read_config_value "mode" "remote")
# Normalize mode aliases: sse → remote
if [ "$MODE" = "sse" ]; then
MODE="remote"
fi
# ── Detect claude CLI ────────────────────────────────────────────────
CLAUDE_BIN=""
if command -v claude &>/dev/null; then
CLAUDE_BIN="claude"
elif [ -x "/opt/homebrew/bin/claude" ]; then
CLAUDE_BIN="/opt/homebrew/bin/claude"
elif [ -x "/usr/local/bin/claude" ]; then
CLAUDE_BIN="/usr/local/bin/claude"
fi
HAS_CLAUDE=false
if [ -n "$CLAUDE_BIN" ]; then
HAS_CLAUDE=true
fi
# ── Interactive mode selection ────────────────────────────────────────
if [ -z "$MODE" ]; then
echo "┌─────────────────────────────────────┐"
echo "│ ai-proj MCP 初始化 │"
echo "└─────────────────────────────────────┘"
echo ""
echo "MCP 连接模式:"
echo " 1) remote - 远程连接 (推荐零依赖Streamable HTTP)"
echo " 2) stdio - 本地进程 (需要 Node.js + mcp-task-bridge)"
echo ""
read -p "选择模式 [1/2] (default: $([ "$CONFIG_MODE" = "stdio" ] && echo "2" || echo "1")): " mode_choice
case "$mode_choice" in
2|stdio) MODE="stdio" ;;
*) MODE="remote" ;;
esac
fi
# ── Token input ───────────────────────────────────────────────────────
if [ -z "$TOKEN" ]; then
echo ""
echo "请输入 MCP API Key:"
echo " 格式: aiproj_pk_xxxxxxxx"
echo " 获取: 登录 AI-Proj → 设置 → MCP API Keys → 创建"
echo ""
read -sp "API Key: " TOKEN
echo ""
fi
if [ -z "$TOKEN" ]; then
echo "❌ API Key 不能为空"
exit 1
fi
echo ""
echo "配置信息:"
echo " 模式: $MODE"
echo " Claude CLI: $($HAS_CLAUDE && echo '检测到' || echo '未检测到 (回退写 .mcp.json)')"
if [ "$MODE" = "remote" ]; then
echo " URL: $SSE_URL"
else
echo " API: $API_BASE"
fi
echo ""
# ── Configure MCP ────────────────────────────────────────────────────
if [ "$MODE" = "remote" ]; then
if $HAS_CLAUDE; then
# 新版 Claude Code: 用 claude mcp add 注册transport=http即 Streamable HTTP
# 先移除旧配置(忽略错误)
"$CLAUDE_BIN" mcp remove -s user ai-proj 2>/dev/null || true
"$CLAUDE_BIN" mcp add ai-proj "$SSE_URL" -t http -s user -H "X-API-Key: $TOKEN" 2>&1
echo "✅ 已通过 claude mcp add 注册 (Streamable HTTP)"
else
# 旧版或无 CLI: 回退写 .mcp.json使用 http transport
mkdir -p "$(dirname "$MCP_CONFIG")"
cat > "$MCP_CONFIG" << EOF
{
"mcpServers": {
"ai-proj": {
"type": "http",
"url": "$SSE_URL",
"headers": {
"X-API-Key": "$TOKEN"
}
}
}
}
EOF
echo "✅ 已生成 $MCP_CONFIG (Streamable HTTP 模式)"
fi
elif [ "$MODE" = "stdio" ]; then
if [ ! -d "$MCP_BRIDGE_DIR" ]; then
echo "📦 mcp-task-bridge 未找到,正在克隆..."
git clone https://gitea.pipexerp.com/pipexerp/mcp-task-bridge.git "$MCP_BRIDGE_DIR"
fi
if [ ! -f "$MCP_BRIDGE_DIR/dist/index.js" ]; then
echo "🔨 构建 mcp-task-bridge..."
cd "$MCP_BRIDGE_DIR"
npm install
npm run build
cd "$SCRIPT_DIR"
fi
BRIDGE_ENTRY="$MCP_BRIDGE_DIR/dist/index.js"
if $HAS_CLAUDE; then
"$CLAUDE_BIN" mcp remove -s user ai-proj 2>/dev/null || true
"$CLAUDE_BIN" mcp add ai-proj -s user -e "NODE_ENV=production" -e "TASK_API_BASE=$API_BASE" -e "TASK_API_TOKEN=$TOKEN" -e "MCP_SERVER_NAME=ai-proj" -- node "$BRIDGE_ENTRY" 2>&1
echo "✅ 已通过 claude mcp add 注册 (stdio)"
else
mkdir -p "$(dirname "$MCP_CONFIG")"
cat > "$MCP_CONFIG" << EOF
{
"mcpServers": {
"ai-proj": {
"command": "node",
"args": ["$BRIDGE_ENTRY"],
"env": {
"NODE_ENV": "production",
"TASK_API_BASE": "$API_BASE",
"TASK_API_TOKEN": "$TOKEN",
"MCP_SERVER_NAME": "ai-proj"
}
}
}
}
EOF
echo "✅ 已生成 $MCP_CONFIG (stdio 模式)"
fi
fi
# ── Install skills to ~/.claude/skills/ ──────────────────────────────
echo "📦 安装技能到 ~/.claude/skills/ ..."
SKILLS_DIR="$HOME/.claude/skills"
mkdir -p "$SKILLS_DIR"
SKILL_COUNT=0
for plugin_dir in "$SCRIPT_DIR"/skills-*/; do
for skill_path in "$plugin_dir"*-plugin/; do
[ -d "$skill_path" ] || continue
skill_md="$skill_path/skills/SKILL.md"
[ -f "$skill_md" ] || continue
# Extract skill name: ai-proj-plugin -> ai-proj
dir_name=$(basename "$skill_path")
skill_name="${dir_name%-plugin}"
target_dir="$SKILLS_DIR/$skill_name"
mkdir -p "$target_dir"
# Copy SKILL.md (overwrite if exists)
cp "$skill_md" "$target_dir/SKILL.md"
SKILL_COUNT=$((SKILL_COUNT + 1))
done
done
echo " 已安装 $SKILL_COUNT 个技能"
echo "✅ 技能安装完成 → $SKILLS_DIR"
# ── Verify MCP connection ────────────────────────────────────────────
echo ""
echo "🔍 验证 MCP 连接..."
if [ "$MODE" = "remote" ]; then
# Step 1: 服务端连接测试 (initialize)
INIT_RESPONSE=$(curl -s -D /tmp/mcp_headers -X POST "$SSE_URL" \
-H "Content-Type: application/json" \
-H "X-API-Key: $TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"init-verify","version":"1.0"}}}' \
--connect-timeout 10 --max-time 15 2>/dev/null)
INIT_HTTP=$(grep -i "^HTTP" /tmp/mcp_headers 2>/dev/null | tail -1 | awk '{print $2}')
SESSION_ID=$(grep -i "^mcp-session-id:" /tmp/mcp_headers 2>/dev/null | tail -1 | tr -d '\r' | awk '{print $2}')
if [ "$INIT_HTTP" = "200" ]; then
echo " ✅ 服务端连接正常 (HTTP 200)"
# Step 2: 工具列表测试 (tools/list)
if [ -n "$SESSION_ID" ]; then
TOOLS_RESPONSE=$(curl -s -X POST "$SSE_URL" \
-H "Content-Type: application/json" \
-H "X-API-Key: $TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
--connect-timeout 10 --max-time 15 2>/dev/null)
TOOL_COUNT=$(echo "$TOOLS_RESPONSE" | grep -o '"name"' | wc -l | tr -d ' ')
if [ "$TOOL_COUNT" -gt 0 ] 2>/dev/null; then
echo " ✅ 工具注册正常 ($TOOL_COUNT 个工具可用)"
else
echo " ⚠️ 工具列表为空,请检查服务端日志"
fi
fi
# Step 3: claude mcp list 测试 (如果有 Claude CLI)
if $HAS_CLAUDE; then
MCP_STATUS=$("$CLAUDE_BIN" mcp list 2>&1 | grep "ai-proj")
if echo "$MCP_STATUS" | grep -q "Connected"; then
echo " ✅ Claude Code MCP 连接正常"
else
echo " ⚠️ Claude Code MCP 未连接: $MCP_STATUS"
fi
fi
else
echo " ❌ MCP 服务端连接失败 (HTTP ${INIT_HTTP:-无响应})"
echo " 请检查: 1) API Key 是否正确 2) 服务端是否运行 3) 网络是否通畅"
fi
rm -f /tmp/mcp_headers
fi
echo ""
echo "┌─────────────────────────────────────┐"
echo "│ ✅ 初始化完成! │"
echo "└─────────────────────────────────────┘"
echo ""
echo "已完成配置:"
if $HAS_CLAUDE; then
echo " ✅ MCP 服务器 (claude mcp add, transport=http)"
else
echo " ✅ MCP 服务器 → $MCP_CONFIG"
fi
echo " ✅ 技能 ($SKILL_COUNT 个) → $SKILLS_DIR"
echo ""
echo "重启 Claude Code 即可使用。"
echo "如需更改配置,编辑 claude-config.yaml 后重新运行 ./init.sh"