From 15449c86d8735a0ff20e93eb45d0a03cc1674a59 Mon Sep 17 00:00:00 2001 From: bookworm Date: Fri, 10 Apr 2026 21:24:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20v2.1.0=20-=20=E5=90=8C=E6=AD=A5=2022=20?= =?UTF-8?q?MCP=20+=20=E7=AE=80=E5=8C=96=20Phase=205=20=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 5 MCP 注入改为直接调用 ~/.claude/inject-mcp.js (从 config 仓库), 移除复杂的 PowerShell JSON 提取+临时脚本生成逻辑。 inject-mcp.js 包含完整 22 个 portable MCP server 配置。 Co-Authored-By: Claude Opus 4.6 (1M context) --- auto-setup.ps1 | 41 +++---------- inject-mcp.js | 152 +++++++++++++++++++------------------------------ 2 files changed, 69 insertions(+), 124 deletions(-) diff --git a/auto-setup.ps1 b/auto-setup.ps1 index da76e60..57395a6 100644 --- a/auto-setup.ps1 +++ b/auto-setup.ps1 @@ -16,7 +16,7 @@ param( $ErrorActionPreference = "Stop" # ─── 版本号 (每次更新递增, build.ps1 自动读取) ────── -$BWVersion = "2.0.3" +$BWVersion = "2.1.0" # ─── B4: 单实例保护 (防止双击两次导致竞态) ───────── $mutexCreated = $false @@ -1103,38 +1103,15 @@ if (Test-Path $templateFile) { } # ── ~/.claude.json (Claude Code v2.1+ MCP 服务器配置的正确位置) ── - # Claude Code 不再从 settings.json 读取 mcpServers, 必须写入 ~/.claude.json - # 策略: 将合并脚本写入临时 .js 文件再用 node 执行 (避免 -e 参数解析问题) - $claudeJsonFile = Join-Path $env:USERPROFILE ".claude.json" - $mcpTmpJs = Join-Path $env:TEMP "bw-inject-mcp.js" + # 直接调用 config 仓库里的 inject-mcp.js (包含全部 22 个 portable MCP) + $injectScript = Join-Path $ClaudeDir "inject-mcp.js" try { - # 从渲染后的 settings.json 提取 mcpServers, 生成 Node.js 合并脚本 - $settingsObj = ConvertFrom-Json $content - if ($settingsObj.mcpServers) { - # 构建 mcpServers 的 JSON (过滤 __comment 键) - $cleanMcp = @{} - foreach ($prop in $settingsObj.mcpServers.PSObject.Properties) { - if ($prop.Name -notlike '__*') { $cleanMcp[$prop.Name] = $prop.Value } - } - $mcpJsonStr = ([PSCustomObject]$cleanMcp | ConvertTo-Json -Depth 10 -Compress) - - # 生成独立 JS 脚本文件 (不依赖 -e 参数传递, 不受终端换行影响) - $jsLines = @( - 'var fs=require("fs");' - "var p=`"$($claudeJsonFile.Replace('\','/'))`";" - 'var d={};' - 'try{d=JSON.parse(fs.readFileSync(p,"utf8"))}catch(e){}' - "var mcp=$mcpJsonStr;" - 'd.mcpServers=mcp;' - 'fs.writeFileSync(p,JSON.stringify(d,null,2));' - 'console.log(Object.keys(mcp).length);' - ) - $jsLines -join "`n" | Set-Content $mcpTmpJs -Encoding UTF8 - - $nodeOut = & node $mcpTmpJs 2>&1 - $mcpCount = ($nodeOut | Select-Object -First 1).ToString().Trim() - Remove-Item $mcpTmpJs -Force -ErrorAction SilentlyContinue - Log-OK ".claude.json 已写入 ($mcpCount 个 MCP 服务器)" + if (Test-Path $injectScript) { + $nodeOut = & node $injectScript 2>&1 + $firstLine = ($nodeOut | Select-Object -First 1).ToString().Trim() + Log-OK $firstLine + } else { + Bw-Log "WARN" "inject-mcp.js 不存在, 跳过 MCP 注入" } } catch { Bw-Log "WARN" ".claude.json 生成失败: $_" diff --git a/inject-mcp.js b/inject-mcp.js index b9e669b..2d97488 100644 --- a/inject-mcp.js +++ b/inject-mcp.js @@ -1,136 +1,104 @@ /** - * Bookworm MCP 注入脚本 - * 将 MCP 服务器配置写入 ~/.claude.json (Claude Code v2.1+ 正确位置) + * Bookworm MCP 注入脚本 v2 — 同步 Bookworm v6.5.1 全部 22 个 portable MCP + * 安全合并到 ~/.claude.json (保留所有现有字段) * 用法: node inject-mcp.js */ const fs = require("fs"); const path = require("path"); +const HOME = process.env.USERPROFILE || process.env.HOME || ""; -const f = path.join(process.env.USERPROFILE, ".claude.json"); +const f = path.join(HOME, ".claude.json"); let d = {}; -try { - d = JSON.parse(fs.readFileSync(f, "utf8")); -} catch (e) { - console.log("未找到 .claude.json, 将创建新文件"); -} +try { d = JSON.parse(fs.readFileSync(f, "utf8")); } catch (e) {} d.mcpServers = { + // ── Tier 1: npx (无需 API Key) ── "context7": { - command: "npx.cmd", - args: ["--yes", "@upstash/context7-mcp@2.1.1"], - type: "stdio" + command: "npx.cmd", args: ["--yes", "@upstash/context7-mcp@2.1.1"], type: "stdio" }, "sequential-thinking": { - command: "npx.cmd", - args: ["--yes", "@modelcontextprotocol/server-sequential-thinking@2025.12.18"], - type: "stdio" + command: "npx.cmd", args: ["--yes", "@modelcontextprotocol/server-sequential-thinking@2025.12.18"], type: "stdio" }, "playwright": { - command: "npx.cmd", - args: ["--yes", "@playwright/mcp@0.0.68", "--headless"], - type: "stdio" + command: "npx.cmd", args: ["--yes", "@playwright/mcp@0.0.68", "--headless"], type: "stdio" }, "session-continuity": { - command: "npx.cmd", - args: ["--yes", "claude-session-continuity-mcp@1.13.0"], - type: "stdio" + command: "npx.cmd", args: ["--yes", "claude-session-continuity-mcp@1.13.0"], type: "stdio" }, - "notebooklm": { - command: "npx.cmd", - args: ["--yes", "notebooklm-mcp@latest"], - type: "stdio", - env: { - https_proxy: "http://127.0.0.1:7893", - http_proxy: "http://127.0.0.1:7893" - } + "browser-mcp": { + command: "npx.cmd", args: ["--yes", "@browsermcp/mcp@latest"], type: "stdio" }, - "cloudflare": { - command: "npx.cmd", - args: ["--yes", "mcp-remote", "https://docs.mcp.cloudflare.com/sse"], - type: "stdio", - env: { - https_proxy: "http://127.0.0.1:7893", - http_proxy: "http://127.0.0.1:7893" - } + "desktop-commander": { + command: "npx.cmd", args: ["--yes", "@wonderwhy-er/desktop-commander@latest"], type: "stdio", + env: { PUPPETEER_SKIP_DOWNLOAD: "true", PUPPETEER_EXECUTABLE_PATH: "C:/Program Files/Google/Chrome/Application/chrome.exe" } }, "chrome-devtools": { command: "npx.cmd", - args: [ - "--yes", "chrome-devtools-mcp@0.18.1", + args: ["--yes", "chrome-devtools-mcp@0.18.1", "--executablePath", "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", - "--viewport", "1280x720", - "--proxyServer", "http://127.0.0.1:7893" - ], + "--viewport", "1280x720", "--proxyServer", "http://127.0.0.1:7893"], type: "stdio" }, + "mobile": { + command: "npx.cmd", args: ["--yes", "@mobilenext/mobile-mcp@0.0.35"], type: "stdio", + env: { ANDROID_HOME: path.join(HOME, "android-sdk") } + }, + + // ── Tier 2: npx + API Key (凭证从环境变量读取) ── "github": { - command: "npx.cmd", - args: ["--yes", "@modelcontextprotocol/server-github"], - type: "stdio", - env: { - GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN || "" - } + command: "npx.cmd", args: ["--yes", "@modelcontextprotocol/server-github"], type: "stdio", + env: { GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN || "" } }, "slack": { - command: "npx.cmd", - args: ["--yes", "@modelcontextprotocol/server-slack"], - type: "stdio", - env: { - SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN || "", - SLACK_TEAM_ID: "T0A4L1JLEER" - } + command: "npx.cmd", args: ["--yes", "@modelcontextprotocol/server-slack"], type: "stdio", + env: { SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN || "", SLACK_TEAM_ID: "T0A4L1JLEER" } }, "firecrawl": { - command: "npx.cmd", - args: ["--yes", "firecrawl-mcp"], - type: "stdio", - env: { - FIRECRAWL_API_KEY: process.env.FIRECRAWL_API_KEY || "" - } - }, - "browserbase": { - command: "npx.cmd", - args: ["--yes", "@anthropic-ai/browserbase-mcp"], - type: "stdio", - env: { - BROWSERBASE_API_KEY: process.env.BROWSERBASE_API_KEY || "", - BROWSERBASE_PROJECT_ID: "d3dbb32f-be2f-4e3a-b9ec-68e27474763c" - } + command: "npx.cmd", args: ["--yes", "firecrawl-mcp"], type: "stdio", + env: { FIRECRAWL_API_KEY: process.env.FIRECRAWL_API_KEY || "" } }, "mcp-image": { - command: "npx.cmd", - args: ["--yes", "mcp-image"], - type: "stdio", - env: { - GEMINI_API_KEY: process.env.GEMINI_API_KEY || "" - } + command: "npx.cmd", args: ["--yes", "mcp-image"], type: "stdio", + env: { GEMINI_API_KEY: process.env.GEMINI_API_KEY || "", IMAGE_OUTPUT_DIR: path.join(HOME, "Pictures/mcp-images") } }, "google-drive": { - command: "npx.cmd", - args: ["--yes", "@piotr-agier/google-drive-mcp"], - type: "stdio" + command: "npx.cmd", args: ["--yes", "@piotr-agier/google-drive-mcp"], type: "stdio", + env: { GOOGLE_DRIVE_OAUTH_CREDENTIALS: path.join(HOME, ".config/google-drive-mcp/gcp-oauth.keys.json") } }, - "windows-mcp": { - command: "uvx", - args: ["--python", "3.13", "windows-mcp"], - type: "stdio" + "browserbase": { + command: "npx.cmd", args: ["--yes", "@anthropic-ai/browserbase-mcp"], type: "stdio", + env: { BROWSERBASE_API_KEY: process.env.BROWSERBASE_API_KEY || "", BROWSERBASE_PROJECT_ID: "d3dbb32f-be2f-4e3a-b9ec-68e27474763c" } }, + + // ── Tier 3: npx + 代理 (需要外网访问) ── + "notebooklm": { + command: "npx.cmd", args: ["--yes", "notebooklm-mcp@latest"], type: "stdio", + env: { https_proxy: "http://127.0.0.1:7893", http_proxy: "http://127.0.0.1:7893" } + }, + "cloudflare": { + command: "npx.cmd", args: ["--yes", "mcp-remote", "https://docs.mcp.cloudflare.com/sse"], type: "stdio", + env: { https_proxy: "http://127.0.0.1:7893", http_proxy: "http://127.0.0.1:7893" } + }, + + // ── Tier 4: HTTP (零安装, 云端托管) ── + "linear": { type: "http", url: "https://mcp.linear.app/mcp" }, + "supabase": { type: "http", url: "https://mcp.supabase.com/mcp?project_ref=oepmihbtoylosbsxlmfo" }, + "figma": { type: "http", url: "https://mcp.figma.com/mcp" }, + + // ── Tier 5: Python/uvx (需要 Python + uv) ── + "windows-mcp": { command: "uvx", args: ["--python", "3.13", "windows-mcp"], type: "stdio" }, "atlassian": { - command: "uvx", - args: ["mcp-atlassian"], - type: "stdio", + command: "uvx", args: ["mcp-atlassian"], type: "stdio", env: { - JIRA_URL: "https://huakoh.atlassian.net", - JIRA_USERNAME: "huakoh449@gmail.com", + JIRA_URL: "https://huakoh.atlassian.net", JIRA_USERNAME: "huakoh449@gmail.com", JIRA_API_TOKEN: process.env.ATLASSIAN_API_TOKEN || "", - CONFLUENCE_URL: "https://huakoh.atlassian.net/wiki", - CONFLUENCE_USERNAME: "huakoh449@gmail.com", + CONFLUENCE_URL: "https://huakoh.atlassian.net/wiki", CONFLUENCE_USERNAME: "huakoh449@gmail.com", CONFLUENCE_API_TOKEN: process.env.ATLASSIAN_API_TOKEN || "" } - } + }, + "computer-control-mcp": { command: "uvx", args: ["computer-control-mcp@latest"], type: "stdio" } }; fs.writeFileSync(f, JSON.stringify(d, null, 2)); const count = Object.keys(d.mcpServers).length; -console.log("OK: " + count + " MCP servers written to " + f); -console.log("请重启 Claude Code 后运行 /mcp 验证"); +console.log("OK: " + count + " MCP servers -> " + f);