#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const os = require('os'); const readline = require('readline'); // MCP 服务定义 const MCP_SERVICES = { // 无需环境变量的服务 'playwright': { package: '@modelcontextprotocol/server-playwright', description: '浏览器自动化测试', category: 'automation', envVars: [] }, 'chrome-devtools': { package: '@executeautomation/chrome-devtools-mcp', description: 'Chrome 开发者工具集成', category: 'automation', envVars: [] }, 'context7': { package: 'context7-mcp', description: '编程文档查询', category: 'documentation', envVars: [] }, 'deep-research': { package: 'deep-research-mcp', description: '深度研究助手', category: 'research', envVars: [] }, 'sequential-thinking': { package: '@modelcontextprotocol/server-sequential-thinking', description: '顺序思考推理', category: 'reasoning', envVars: [] }, 'scrapling': { package: 'scrapling-mcp', description: '网页抓取工具', category: 'web', envVars: [] }, 'figma': { package: '@figma/mcp-server-figma', description: 'Figma 设计工具集成', category: 'design', envVars: [], oauth: true }, // 需要环境变量的服务 'github': { package: '@modelcontextprotocol/server-github', description: 'GitHub 代码仓库集成', category: 'development', envVars: ['GITHUB_PERSONAL_ACCESS_TOKEN'], setup: 'https://github.com/settings/tokens' }, 'slack': { package: '@modelcontextprotocol/server-slack', description: 'Slack 团队协作', category: 'communication', envVars: ['SLACK_BOT_TOKEN', 'SLACK_TEAM_ID'], setup: 'https://api.slack.com/apps' }, 'linear': { package: '@modelcontextprotocol/server-linear', description: 'Linear 项目管理', category: 'productivity', envVars: ['LINEAR_API_KEY'], setup: 'https://linear.app/settings/api' }, 'browserbase': { package: '@browserbasehq/mcp-server-browserbase', description: '云端浏览器服务', category: 'automation', envVars: ['BROWSERBASE_PROJECT_ID', 'BROWSERBASE_API_KEY'], setup: 'https://browserbase.com' }, 'cloudflare': { package: '@cloudflare/mcp-server-cloudflare', description: 'Cloudflare CDN 管理', category: 'infrastructure', envVars: ['CLOUDFLARE_API_TOKEN'], setup: 'https://dash.cloudflare.com/profile/api-tokens' }, 'firecrawl': { package: '@mendable/firecrawl-mcp', description: '智能网页爬虫', category: 'web', envVars: ['FIRECRAWL_API_KEY'], setup: 'https://firecrawl.dev' }, 'supabase': { package: '@supabase/mcp-server-supabase', description: 'Supabase 数据库', category: 'database', envVars: ['SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY'], setup: 'https://supabase.com/dashboard/project/_/settings/api' }, 'sentry': { package: '@sentry/mcp-server', description: 'Sentry 错误监控', category: 'monitoring', envVars: ['SENTRY_AUTH_TOKEN'], setup: 'https://sentry.io/settings/account/api/auth-tokens/' }, 'notion': { package: '@notionhq/mcp-server', description: 'Notion 知识库', category: 'productivity', envVars: ['NOTION_API_KEY'], setup: 'https://www.notion.so/my-integrations' }, 'vercel': { package: '@vercel/mcp-server', description: 'Vercel 部署平台', category: 'deployment', envVars: ['VERCEL_TOKEN'], setup: 'https://vercel.com/account/tokens' }, 'firebase': { package: '@firebase/mcp-server', description: 'Firebase 后端服务', category: 'backend', envVars: ['FIREBASE_PROJECT_ID', 'FIREBASE_PRIVATE_KEY', 'FIREBASE_CLIENT_EMAIL'], setup: 'https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk' } }; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); function question(prompt) { return new Promise(resolve => rl.question(prompt, resolve)); } async function main() { console.log('╔════════════════════════════════════════════════════════╗'); console.log('║ MCP Configuration Wizard v1.0 ║'); console.log('║ Universal Setup Tool for Claude Code ║'); console.log('╚════════════════════════════════════════════════════════╝\n'); // 加载检测结果 let detection = null; const detectionFile = path.join(__dirname, 'detection-result.json'); if (fs.existsSync(detectionFile)) { detection = JSON.parse(fs.readFileSync(detectionFile, 'utf-8')); console.log('✓ Loaded system detection results\n'); } else { console.log('⚠ No detection results found. Run "node detect.js" first.\n'); const proceed = await question('Continue anyway? (y/n): '); if (proceed.toLowerCase() !== 'y') { rl.close(); return; } } console.log('This wizard will help you configure MCP services for Claude Code.\n'); console.log('Available configuration modes:'); console.log(' 1. Quick Setup - Install recommended services'); console.log(' 2. Custom Setup - Choose services manually'); console.log(' 3. Import from file - Load existing configuration'); console.log(' 4. Export current config - Save for another machine\n'); const mode = await question('Select mode (1-4): '); const config = { mcpServers: {}, environment: {} }; switch (mode) { case '1': await quickSetup(config, detection); break; case '2': await customSetup(config, detection); break; case '3': await importConfig(config); break; case '4': await exportConfig(detection); rl.close(); return; default: console.log('Invalid selection'); rl.close(); return; } // 生成配置文件 await generateConfig(config, detection); rl.close(); } async function quickSetup(config, detection) { console.log('\n=== Quick Setup ===\n'); console.log('Installing recommended services:'); const recommended = [ 'playwright', 'chrome-devtools', 'context7', 'deep-research', 'sequential-thinking', 'scrapling' ]; recommended.forEach(name => { const service = MCP_SERVICES[name]; console.log(` ✓ ${name} - ${service.description}`); addService(config, name, service); }); console.log('\nOptional services with API keys:'); console.log(' - github, slack, linear, supabase, etc.\n'); const addMore = await question('Configure services with API keys? (y/n): '); if (addMore.toLowerCase() === 'y') { await configureApiServices(config, detection); } } async function customSetup(config, detection) { console.log('\n=== Custom Setup ===\n'); console.log('Available services by category:\n'); const categories = {}; Object.entries(MCP_SERVICES).forEach(([name, service]) => { if (!categories[service.category]) { categories[service.category] = []; } categories[service.category].push({ name, ...service }); }); for (const [category, services] of Object.entries(categories)) { console.log(`\n${category.toUpperCase()}:`); services.forEach((s, i) => { const envInfo = s.envVars.length > 0 ? ' (requires API key)' : ''; console.log(` ${i + 1}. ${s.name} - ${s.description}${envInfo}`); }); } console.log('\nEnter service names separated by commas (or "all" for all services):'); const selection = await question('Services: '); if (selection.toLowerCase() === 'all') { Object.entries(MCP_SERVICES).forEach(([name, service]) => { addService(config, name, service); }); } else { const selected = selection.split(',').map(s => s.trim()); selected.forEach(name => { if (MCP_SERVICES[name]) { addService(config, name, MCP_SERVICES[name]); } }); } await configureApiServices(config, detection); } async function configureApiServices(config, detection) { console.log('\n=== API Key Configuration ===\n'); for (const [name, service] of Object.entries(config.mcpServers)) { const serviceDef = MCP_SERVICES[name]; if (serviceDef.envVars && serviceDef.envVars.length > 0) { console.log(`\n${name} requires:`); serviceDef.envVars.forEach(v => console.log(` - ${v}`)); if (serviceDef.setup) { console.log(` Setup: ${serviceDef.setup}`); } // 检查是否已有环境变量 const hasExisting = detection && serviceDef.envVars.every(v => detection.environment.mcpRelated.includes(v) ); if (hasExisting) { console.log(' ✓ Found existing environment variables'); const useExisting = await question(' Use existing? (y/n): '); if (useExisting.toLowerCase() === 'y') { continue; } } const configure = await question(' Configure now? (y/n): '); if (configure.toLowerCase() === 'y') { for (const varName of serviceDef.envVars) { const value = await question(` ${varName}: `); if (value) { config.environment[varName] = value; } } } } } } async function importConfig(config) { console.log('\n=== Import Configuration ===\n'); const filePath = await question('Enter path to config file (.json): '); if (fs.existsSync(filePath)) { const imported = JSON.parse(fs.readFileSync(filePath, 'utf-8')); if (imported.mcpServers) { config.mcpServers = imported.mcpServers; console.log(`✓ Imported ${Object.keys(config.mcpServers).length} services`); } if (imported.environment) { config.environment = imported.environment; console.log(`✓ Imported ${Object.keys(config.environment).length} environment variables`); } } else { console.log('✗ File not found'); } } async function exportConfig(detection) { console.log('\n=== Export Configuration ===\n'); if (!detection || !detection.claude.hasExistingConfig) { console.log('✗ No existing configuration found'); return; } const configFile = path.join(os.homedir(), '.claude.json'); const config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); const exportData = { mcpServers: config.mcpServers || {}, environment: {} }; // 导出环境变量 if (detection.environment.mcpRelated.length > 0) { console.log('Export environment variables?'); const exportEnv = await question('(y/n): '); if (exportEnv.toLowerCase() === 'y') { detection.environment.mcpRelated.forEach(varName => { const value = process.env[varName]; if (value) { exportData.environment[varName] = value; } }); } } const outputPath = path.join(__dirname, 'mcp-config-export.json'); fs.writeFileSync(outputPath, JSON.stringify(exportData, null, 2)); console.log(`\n✓ Configuration exported to: ${outputPath}`); } function addService(config, name, service) { const isWindows = os.platform() === 'win32'; const command = isWindows ? 'cmd' : 'npx'; const args = isWindows ? ['/c', 'npx', '-y', service.package] : ['-y', service.package]; config.mcpServers[name] = { command, args }; if (service.envVars && service.envVars.length > 0) { config.mcpServers[name].env = {}; service.envVars.forEach(v => { config.mcpServers[name].env[v] = `\${${v}}`; }); } } async function generateConfig(config, detection) { console.log('\n=== Generating Configuration Files ===\n'); // 1. 生成 .claude.json const claudeConfigPath = path.join(__dirname, 'generated-claude.json'); fs.writeFileSync(claudeConfigPath, JSON.stringify({ mcpServers: config.mcpServers }, null, 2)); console.log(`✓ Generated: ${claudeConfigPath}`); // 2. 生成环境变量文件 if (Object.keys(config.environment).length > 0) { const envPath = path.join(__dirname, 'generated-env.json'); fs.writeFileSync(envPath, JSON.stringify(config.environment, null, 2)); console.log(`✓ Generated: ${envPath}`); // 生成 PowerShell 脚本(Windows) if (os.platform() === 'win32') { const psScript = generatePowerShellScript(config.environment); const psPath = path.join(__dirname, 'apply-env.ps1'); fs.writeFileSync(psPath, psScript); console.log(`✓ Generated: ${psPath}`); } // 生成 Shell 脚本(Unix) const shScript = generateShellScript(config.environment); const shPath = path.join(__dirname, 'apply-env.sh'); fs.writeFileSync(shPath, shScript); fs.chmodSync(shPath, '755'); console.log(`✓ Generated: ${shPath}`); } // 3. 生成安装说明 const readme = generateReadme(config, detection); const readmePath = path.join(__dirname, 'INSTALL.md'); fs.writeFileSync(readmePath, readme); console.log(`✓ Generated: ${readmePath}`); console.log('\n╔════════════════════════════════════════════════════════╗'); console.log('║ Configuration Generated Successfully ║'); console.log('╚════════════════════════════════════════════════════════╝'); console.log('\nNext steps:'); console.log(' 1. Review generated files'); console.log(' 2. Run "node apply.js" to install configuration'); console.log(' 3. Restart Claude Code'); } function generatePowerShellScript(env) { let script = '# MCP Environment Variables Setup\n'; script += '# Generated by MCP Configuration Wizard\n\n'; script += '$ErrorActionPreference = "Continue"\n\n'; script += 'Write-Host "Setting up MCP environment variables..." -ForegroundColor Cyan\n\n'; Object.entries(env).forEach(([key, value]) => { script += `[System.Environment]::SetEnvironmentVariable("${key}", "${value}", [System.EnvironmentVariableTarget]::User)\n`; script += `Write-Host "[OK] ${key}" -ForegroundColor Green\n`; }); script += '\nWrite-Host "\\nEnvironment variables configured successfully!" -ForegroundColor Green\n'; script += 'Write-Host "Please restart Claude Code to apply changes" -ForegroundColor Yellow\n'; script += 'Read-Host "\\nPress Enter to exit"\n'; return script; } function generateShellScript(env) { let script = '#!/bin/bash\n'; script += '# MCP Environment Variables Setup\n'; script += '# Generated by MCP Configuration Wizard\n\n'; Object.entries(env).forEach(([key, value]) => { script += `export ${key}="${value}"\n`; }); script += '\necho "Environment variables configured for this session"\n'; script += 'echo "To make permanent, add these exports to your ~/.bashrc or ~/.zshrc"\n'; return script; } function generateReadme(config, detection) { let readme = '# MCP Configuration Installation Guide\n\n'; readme += 'Generated by MCP Configuration Wizard\n\n'; readme += `## System Information\n\n`; if (detection) { readme += `- Platform: ${detection.system.platform}\n`; readme += `- User: ${detection.system.username}\n`; readme += `- Node.js: ${detection.system.nodeVersion}\n\n`; } readme += `## Configured Services (${Object.keys(config.mcpServers).length})\n\n`; Object.keys(config.mcpServers).forEach(name => { const service = MCP_SERVICES[name]; readme += `- **${name}**: ${service.description}\n`; }); readme += '\n## Installation Steps\n\n'; readme += '### 1. Apply Environment Variables\n\n'; if (os.platform() === 'win32') { readme += '**Windows:**\n```powershell\n.\\apply-env.ps1\n```\n\n'; } readme += '**Unix/Mac:**\n```bash\nsource ./apply-env.sh\n```\n\n'; readme += '### 2. Install MCP Configuration\n\n'; readme += '```bash\nnode apply.js\n```\n\n'; readme += '### 3. Restart Claude Code\n\n'; readme += 'Close and reopen Claude Code to load the new MCP services.\n\n'; readme += '## Manual Installation\n\n'; readme += 'If automatic installation fails:\n\n'; readme += '1. Copy `generated-claude.json` content to `~/.claude.json`\n'; readme += '2. Set environment variables from `generated-env.json`\n'; readme += '3. Restart Claude Code\n'; return readme; } main().catch(console.error);