UNPKG

url-builder-mcp

Version:

URL Builder MCP Server - Professional URL construction with intelligent parameter handling for Claude Desktop

236 lines (194 loc) • 7.39 kB
#!/usr/bin/env node /** * URL Builder MCP Server - NPX Launcher * * Intelligent launcher that detects environment and runs the URL Builder MCP server * with optimal configuration for Claude Desktop integration. */ import { spawn } from 'child_process'; import fs from 'fs'; import path from 'path'; import os from 'os'; import { fileURLToPath } from 'url'; // ES module compatibility const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // ANSI color codes for beautiful output const colors = { reset: '\x1b[0m', bright: '\x1b[1m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m' }; function log(message, color = 'reset') { console.log(`${colors[color]}${message}${colors.reset}`); } function findProjectRoot() { let currentDir = __dirname; // Go up from bin/ to project root const projectRoot = path.dirname(currentDir); // Verify it's the correct project by checking for dist/index.js or src/index.ts const distFile = path.join(projectRoot, 'dist', 'index.js'); const srcFile = path.join(projectRoot, 'src', 'index.ts'); if (fs.existsSync(distFile) || fs.existsSync(srcFile)) { return projectRoot; } // If not found, try current working directory const cwd = process.cwd(); const cwdDistFile = path.join(cwd, 'dist', 'index.js'); const cwdSrcFile = path.join(cwd, 'src', 'index.ts'); if (fs.existsSync(cwdDistFile) || fs.existsSync(cwdSrcFile)) { return cwd; } throw new Error('URL Builder MCP server files not found. Please run from project directory.'); } function checkEnvironment(projectRoot) { const distFile = path.join(projectRoot, 'dist', 'index.js'); const srcFile = path.join(projectRoot, 'src', 'index.ts'); const packageFile = path.join(projectRoot, 'package.json'); const nodeModules = path.join(projectRoot, 'node_modules'); if (fs.existsSync(distFile)) { return 'production'; } else if (fs.existsSync(srcFile) && fs.existsSync(nodeModules)) { return 'development'; } else if (fs.existsSync(srcFile)) { return 'source'; } return 'unknown'; } function buildCommand(projectRoot, environment) { switch (environment) { case 'production': return { command: 'node', args: [path.join(projectRoot, 'dist', 'index.js')], cwd: projectRoot }; case 'development': // Check if ts-node is available const tsNodePath = path.join(projectRoot, 'node_modules', '.bin', 'ts-node'); const isWindows = os.platform() === 'win32'; const tsNodeBin = isWindows ? `${tsNodePath}.cmd` : tsNodePath; if (fs.existsSync(tsNodeBin)) { return { command: tsNodeBin, args: [path.join(projectRoot, 'src', 'index.ts')], cwd: projectRoot }; } // Fall through to source case 'source': return { command: 'npx', args: ['ts-node', path.join(projectRoot, 'src', 'index.ts')], cwd: projectRoot }; default: throw new Error('Unable to determine how to run the URL Builder MCP server'); } } function loadEnvironment(projectRoot) { const envFile = path.join(projectRoot, '.env'); const env = { ...process.env }; if (fs.existsSync(envFile)) { const envContent = fs.readFileSync(envFile, 'utf8'); const envLines = envContent.split('\n'); for (const line of envLines) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#')) { const [key, ...valueParts] = trimmed.split('='); if (key && valueParts.length > 0) { env[key.trim()] = valueParts.join('=').trim(); } } } } return env; } function displayHeader() { log('šŸ”— URL Builder MCP Server', 'cyan'); log('─'.repeat(50), 'blue'); log('šŸš€ Starting intelligent MCP server for Claude Desktop', 'green'); log(''); } function displayEnvironmentInfo(projectRoot, environment) { log('šŸ“‹ Environment Detection:', 'yellow'); log(` šŸ“‚ Project Root: ${projectRoot}`, 'reset'); log(` šŸ”§ Environment: ${environment}`, 'reset'); log(` šŸ’» Platform: ${os.platform()} ${os.arch()}`, 'reset'); log(''); } async function main() { try { // Handle help flag if (process.argv.includes('--help') || process.argv.includes('-h')) { displayHeader(); log('šŸ“‹ Usage:', 'yellow'); log(' npx url-builder-mcp # Start the MCP server', 'reset'); log(' npx url-builder-mcp --help # Show this help', 'reset'); log('', 'reset'); log('šŸ“š Documentation: https://github.com/enpetrache/url_builder_mcp#readme', 'reset'); return; } displayHeader(); // Find project root and detect environment const projectRoot = findProjectRoot(); const environment = checkEnvironment(projectRoot); displayEnvironmentInfo(projectRoot, environment); // Build command const { command, args, cwd } = buildCommand(projectRoot, environment); // Load environment variables const env = loadEnvironment(projectRoot); log('šŸŽÆ Starting server...', 'green'); log(` Command: ${command} ${args.join(' ')}`, 'reset'); log(''); // Spawn the process const child = spawn(command, args, { cwd, env, stdio: 'inherit' }); // Handle process events child.on('error', (error) => { log(`āŒ Failed to start server: ${error.message}`, 'red'); process.exit(1); }); child.on('exit', (code, signal) => { if (signal) { log(`\nšŸ›‘ Server stopped by signal: ${signal}`, 'yellow'); } else if (code !== 0) { log(`\nāŒ Server exited with code: ${code}`, 'red'); process.exit(code); } else { log('\nšŸ‘‹ Server stopped gracefully', 'green'); } }); // Handle Ctrl+C gracefully process.on('SIGINT', () => { log('\nšŸ›‘ Received SIGINT, stopping server...', 'yellow'); child.kill('SIGINT'); }); process.on('SIGTERM', () => { log('\nšŸ›‘ Received SIGTERM, stopping server...', 'yellow'); child.kill('SIGTERM'); }); } catch (error) { log(`āŒ Error: ${error.message}`, 'red'); log('', 'reset'); log('šŸ’” Troubleshooting:', 'yellow'); log(' 1. Ensure you have Node.js 18+ installed', 'reset'); log(' 2. Run from the url-builder-mcp project directory', 'reset'); log(' 3. Build the project: npm run build', 'reset'); log(' 4. Install dependencies: npm install', 'reset'); process.exit(1); } } // Run the launcher if (import.meta.url === `file://${process.argv[1]}`) { main(); } export { main };