#!/usr/bin/env node
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { homedir } from 'os';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import * as readline from 'readline';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectRoot = join(__dirname, '..');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function question(query) {
return new Promise(resolve => rl.question(query, resolve));
}
async function main() {
console.log('\n🚀 Opus MCP Server Setup\n');
console.log('This script will help you configure the Opus MCP server.\n');
const apiKey = await question('Enter your Opus API Service Key: ');
if (!apiKey || apiKey.trim() === '') {
console.error('❌ API key is required!');
process.exit(1);
}
console.log('\nChoose installation type:');
console.log('1. Claude Desktop');
console.log('2. Factory/Droid');
console.log('3. Custom MCP Client');
const choice = await question('Enter choice (1-3): ');
const indexPath = join(projectRoot, 'build', 'index.js');
if (!existsSync(indexPath)) {
console.error('❌ Build files not found. Run "npm run build" first.');
process.exit(1);
}
switch (choice) {
case '1':
await setupClaudeDesktop(apiKey, indexPath);
break;
case '2':
await setupFactory(apiKey, indexPath);
break;
case '3':
await showCustomInstructions(apiKey, indexPath);
break;
default:
console.error('❌ Invalid choice');
process.exit(1);
}
rl.close();
}
async function setupClaudeDesktop(apiKey, indexPath) {
const platform = process.platform;
let configPath, configDir;
if (platform === 'darwin') {
configDir = join(homedir(), 'Library', 'Application Support', 'Claude');
configPath = join(configDir, 'claude_desktop_config.json');
} else if (platform === 'win32') {
configDir = join(process.env.APPDATA, 'Claude');
configPath = join(configDir, 'claude_desktop_config.json');
} else {
console.log('❌ Unsupported platform for automatic Claude Desktop setup.');
return;
}
if (!existsSync(configDir)) {
mkdirSync(configDir, { recursive: true });
}
let config = { mcpServers: {} };
if (existsSync(configPath)) {
try {
config = JSON.parse(readFileSync(configPath, 'utf8'));
} catch (e) {
console.log('⚠️ Creating new config file.');
}
}
if (!config.mcpServers) {
config.mcpServers = {};
}
config.mcpServers.opus = {
command: 'node',
args: [indexPath],
env: {
OPUS_SERVICE_KEY: apiKey
}
};
try {
writeFileSync(configPath, JSON.stringify(config, null, 2));
console.log('\n✅ Claude Desktop configuration updated!');
console.log(`📍 Config file: ${configPath}`);
console.log('\n⚠️ Please restart Claude Desktop to load the MCP server.');
} catch (e) {
console.error('❌ Failed to write config:', e.message);
}
}
async function setupFactory(apiKey, indexPath) {
const factoryConfigPath = join(homedir(), '.factory', 'mcp.json');
if (!existsSync(factoryConfigPath)) {
console.log('❌ Factory config not found at:', factoryConfigPath);
return;
}
let config;
try {
config = JSON.parse(readFileSync(factoryConfigPath, 'utf8'));
} catch (e) {
console.error('❌ Failed to read Factory config:', e.message);
return;
}
if (!config.mcpServers) {
config.mcpServers = {};
}
config.mcpServers.opus = {
command: 'node',
args: [indexPath],
env: {
OPUS_SERVICE_KEY: apiKey
}
};
try {
writeFileSync(factoryConfigPath, JSON.stringify(config, null, 2));
console.log('\n✅ Factory/Droid configuration updated!');
console.log(`📍 Config file: ${factoryConfigPath}`);
console.log('\n⚠️ Please restart Droid to load the MCP server.');
} catch (e) {
console.error('❌ Failed to write config:', e.message);
}
}
async function showCustomInstructions(apiKey, indexPath) {
console.log('\n📋 Manual Configuration Instructions\n');
console.log('Add this to your MCP client configuration:\n');
const config = {
command: 'node',
args: [indexPath],
env: {
OPUS_SERVICE_KEY: apiKey
}
};
console.log(JSON.stringify({ opus: config }, null, 2));
}
main().catch(error => {
console.error('❌ Setup failed:', error.message);
process.exit(1);
});