#!/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"