bookworm-boot/拓展/mcp-config-wizard/wizard.js
bookworm 83c1e613a1 feat: 纳入拓展工具 + guide.html badges 对齐
- 拓展/: MCP配置向导、PowerShell工具、mcp-auto-loader 纳入版本管理
- guide.html: badges 从模糊 "90+" 改为精确 "92 Skills | 18 Agents | 34 Hooks"
2026-04-06 00:26:45 +08:00

497 lines
16 KiB
JavaScript
Raw 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.

#!/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);