Skip to main content
Glama
cli.tsβ€’15.2 kB
#!/usr/bin/env node /** * CLI launcher for MCP Fullstack server */ import { program } from 'commander'; import { createMCPServer } from './server.js'; import { ServiceManager, ServiceConfig } from './utils/service-manager.js'; import path from 'path'; import fs from 'fs'; import os from 'os'; program .name('mcp-fullstack') .description('MCP Server for full-stack software engineering') .version('1.0.0'); program .command('start') .description('Start the MCP server') .option('-p, --port <port>', 'Port to listen on', '3000') .option('-h, --host <host>', 'Host to bind to', 'localhost') .option('--log-level <level>', 'Log level (debug, info, warn, error)', 'info') .option('--no-dashboard', 'Disable dashboard') .option('--no-websocket', 'Disable WebSocket support') .action(async (options) => { process.env.PORT = options.port; process.env.HOST = options.host; process.env.LOG_LEVEL = options.logLevel; try { const { server, transport } = await createMCPServer(); console.log('πŸš€ Starting MCP Fullstack Server...'); console.log(`πŸ“Š Dashboard: ${options.dashboard ? 'enabled' : 'disabled'}`); console.log(`πŸ”Œ WebSocket: ${options.websocket ? 'enabled' : 'disabled'}`); console.log(`πŸ“ Log Level: ${options.logLevel}`); await transport.start(); console.log('\nβœ… Server started successfully!'); console.log('\nπŸ“ Available endpoints:'); console.log(` β€’ MCP API: http://${options.host}:${options.port}/mcp`); if (options.websocket) { console.log(` β€’ WebSocket: ws://${options.host}:${options.port}/ws`); } if (options.dashboard) { console.log(` β€’ Dashboard: http://${options.host}:${options.port}`); } console.log(` β€’ Health: http://${options.host}:${options.port}/health`); console.log(` β€’ Tools: http://${options.host}:${options.port}/tools`); console.log(` β€’ Sessions: http://${options.host}:${options.port}/sessions`); console.log('\nπŸ› οΈ Available namespaces:'); console.log(' β€’ browser.* - WebDriver automation'); console.log(' β€’ search.* - Web search and extraction'); console.log(' β€’ messages.* - Internal messaging (coming soon)'); console.log(' β€’ secrets.* - Key-value storage (coming soon)'); console.log(' β€’ db.* - Database operations (coming soon)'); console.log(' β€’ supabase.* - Supabase integration (coming soon)'); console.log(' β€’ render.* - Render deployment (coming soon)'); console.log(' β€’ vercel.* - Vercel deployment (coming soon)'); console.log(' β€’ tracking.* - Analytics & recordings (coming soon)'); console.log('\nπŸ”§ Environment variables to configure:'); console.log(' β€’ WEB_DRIVER_REMOTE_URL - Remote WebDriver URL (optional)'); console.log(' β€’ SUPABASE_URL - Supabase project URL'); console.log(' β€’ SUPABASE_SERVICE_ROLE - Supabase service role key'); console.log(' β€’ RENDER_API_TOKEN - Render API token'); console.log(' β€’ VERCEL_TOKEN - Vercel API token'); console.log(' β€’ POSTHOG_API_HOST - PostHog API host'); console.log(' β€’ POSTHOG_PROJECT_KEY - PostHog project key'); console.log(' β€’ POSTHOG_PERSONAL_API_KEY - PostHog personal API key'); console.log(' β€’ GEMINI_API_KEY - Google Gemini API key'); } catch (error) { console.error('❌ Failed to start server:', error); process.exit(1); } }); program .command('test') .description('Run smoke tests') .action(async () => { console.log('πŸ§ͺ Running smoke tests...'); try { const { server, transport } = await createMCPServer(); await transport.start(); // Test basic functionality const healthResponse = await fetch(`http://${server.getConfig().host}:${server.getConfig().port}/health`); const health = await healthResponse.json(); console.log('βœ… Health check passed:', health.status); // Test tools listing const toolsResponse = await fetch(`http://${server.getConfig().host}:${server.getConfig().port}/tools`); const tools = await toolsResponse.json(); console.log(`βœ… Tools listing passed: ${tools.tools.length} tools available`); // Test MCP request const mcpResponse = await fetch(`http://${server.getConfig().host}:${server.getConfig().port}/mcp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' }) }); const mcpResult = await mcpResponse.json(); console.log(`βœ… MCP request passed: ${mcpResult.result.length} tools returned`); await transport.stop(); console.log('βœ… All smoke tests passed!'); } catch (error) { console.error('❌ Smoke tests failed:', error); process.exit(1); } }); program .command('info') .description('Show server information') .action(() => { console.log('πŸ“‹ MCP Fullstack Server Information'); console.log(`Version: 1.0.0`); console.log(`Node.js: ${process.version}`); console.log(`Platform: ${process.platform}`); console.log(`Architecture: ${process.arch}`); console.log(`Working Directory: ${process.cwd()}`); console.log('\nπŸ”§ Environment Configuration:'); const envVars = [ 'PORT', 'HOST', 'LOG_LEVEL', 'WEB_DRIVER_REMOTE_URL', 'SUPABASE_URL', 'SUPABASE_SERVICE_ROLE', 'SUPABASE_ANON', 'RENDER_API_TOKEN', 'RENDER_ACCOUNT_ID', 'VERCEL_TOKEN', 'VERCEL_TEAM_ID', 'POSTHOG_API_HOST', 'POSTHOG_PROJECT_KEY', 'POSTHOG_PERSONAL_API_KEY', 'GEMINI_API_KEY', 'TRACKING_REDACT_RULES_JSON' ]; for (const envVar of envVars) { const value = process.env[envVar]; const status = value ? 'βœ…' : '❌'; const displayValue = value ? (envVar.includes('KEY') || envVar.includes('TOKEN') ? '***masked***' : value) : 'not set'; console.log(` ${status} ${envVar}: ${displayValue}`); } }); // Service Management Commands const service = program .command('service') .description('Manage MCP Fullstack as a system service/daemon'); service .command('install') .description('Install MCP Fullstack as a system service') .option('--user <user>', 'User to run the service as (Linux only)') .option('--port <port>', 'Port for the service to listen on', '3000') .option('--env-file <file>', 'Environment file to load') .action(async (options) => { try { const serviceConfig = await createServiceConfig(options); const serviceManager = new ServiceManager(serviceConfig); console.log('πŸš€ Installing MCP Fullstack Service...'); console.log(` Name: ${serviceConfig.name}`); console.log(` Display Name: ${serviceConfig.displayName}`); console.log(` Script: ${serviceConfig.script}`); console.log(` Working Directory: ${serviceConfig.workingDirectory}`); if (process.platform !== 'win32' && process.getuid && process.getuid() !== 0) { console.log('⚠️ Note: You may need to run this command with sudo on Linux'); } await serviceManager.install(); console.log('βœ… Service installed successfully!'); console.log('\nπŸ“ Next steps:'); console.log(' β€’ Start the service: mcp-fullstack service start'); console.log(' β€’ Check status: mcp-fullstack service status'); console.log(' β€’ View logs: mcp-fullstack service logs'); } catch (error) { console.error('❌ Service installation failed:', error); process.exit(1); } }); service .command('uninstall') .description('Uninstall the MCP Fullstack system service') .action(async () => { try { const serviceConfig = await createServiceConfig({}); const serviceManager = new ServiceManager(serviceConfig); console.log('πŸ—‘οΈ Uninstalling MCP Fullstack Service...'); if (process.platform !== 'win32' && process.getuid && process.getuid() !== 0) { console.log('⚠️ Note: You may need to run this command with sudo on Linux'); } await serviceManager.uninstall(); console.log('βœ… Service uninstalled successfully!'); } catch (error) { console.error('❌ Service uninstallation failed:', error); process.exit(1); } }); service .command('start') .description('Start the MCP Fullstack service') .action(async () => { try { const serviceConfig = await createServiceConfig({}); const serviceManager = new ServiceManager(serviceConfig); await serviceManager.start(); console.log('βœ… Service started successfully!'); // Show status after starting setTimeout(async () => { const status = await serviceManager.status(); console.log(`πŸ“Š Status: ${status.status} ${status.running ? '(Running)' : '(Stopped)'}`); if (status.pid) { console.log(` PID: ${status.pid}`); } }, 2000); } catch (error) { console.error('❌ Failed to start service:', error); process.exit(1); } }); service .command('stop') .description('Stop the MCP Fullstack service') .action(async () => { try { const serviceConfig = await createServiceConfig({}); const serviceManager = new ServiceManager(serviceConfig); await serviceManager.stop(); console.log('βœ… Service stopped successfully!'); } catch (error) { console.error('❌ Failed to stop service:', error); process.exit(1); } }); service .command('restart') .description('Restart the MCP Fullstack service') .action(async () => { try { const serviceConfig = await createServiceConfig({}); const serviceManager = new ServiceManager(serviceConfig); await serviceManager.restart(); console.log('βœ… Service restarted successfully!'); // Show status after restarting setTimeout(async () => { const status = await serviceManager.status(); console.log(`πŸ“Š Status: ${status.status} ${status.running ? '(Running)' : '(Stopped)'}`); if (status.pid) { console.log(` PID: ${status.pid}`); } }, 3000); } catch (error) { console.error('❌ Failed to restart service:', error); process.exit(1); } }); service .command('status') .description('Show MCP Fullstack service status') .action(async () => { try { const serviceConfig = await createServiceConfig({}); const serviceManager = new ServiceManager(serviceConfig); console.log('πŸ“Š MCP Fullstack Service Status'); console.log('================================'); const status = await serviceManager.status(); console.log(`Installed: ${status.installed ? 'βœ…' : '❌'}`); console.log(`Running: ${status.running ? 'βœ…' : '❌'}`); console.log(`Status: ${status.status}`); if (status.pid) { console.log(`PID: ${status.pid}`); } if (status.error) { console.log(`Error: ${status.error}`); } // Try to check if server is responding if (status.running) { try { const response = await fetch('http://localhost:3000/health'); if (response.ok) { const health = await response.json(); console.log(`\n🌐 Server Health: ${health.status}`); console.log(` Uptime: ${Math.floor(health.server?.uptime || 0)}s`); console.log(` Active Sessions: ${health.registry?.active_sessions || 0}`); console.log(` Total Tools: ${health.registry?.total_tools || 0}`); } } catch (error) { console.log('\n⚠️ Service is running but HTTP server is not responding'); } } } catch (error) { console.error('❌ Failed to get service status:', error); process.exit(1); } }); service .command('logs') .description('Show MCP Fullstack service logs') .option('-n, --lines <lines>', 'Number of log lines to show', '50') .option('-f, --follow', 'Follow log output (tail -f style)', false) .action(async (options) => { try { const serviceConfig = await createServiceConfig({}); const serviceManager = new ServiceManager(serviceConfig); console.log(`πŸ“ MCP Fullstack Service Logs (last ${options.lines} lines)`); console.log('================================================'); const logs = await serviceManager.logs(parseInt(options.lines)); logs.forEach(line => console.log(line)); if (options.follow) { console.log('\nπŸ”„ Following logs... (Press Ctrl+C to exit)'); // Simple log following implementation const logFile = path.join(os.homedir(), '.mcp-fullstack', 'logs', 'service.log'); if (fs.existsSync(logFile)) { const { spawn } = await import('child_process'); const tail = spawn('tail', ['-f', logFile], { stdio: 'inherit' }); process.on('SIGINT', () => { tail.kill(); process.exit(0); }); } else { console.log('❌ Log file not found'); } } } catch (error) { console.error('❌ Failed to get service logs:', error); process.exit(1); } }); // Helper function to create service configuration async function createServiceConfig(options: any): Promise<ServiceConfig> { const scriptPath = path.resolve(__dirname, 'server.js'); const workingDirectory = path.dirname(scriptPath); // Load environment variables from file if specified let env: Record<string, string> = {}; if (options.envFile && fs.existsSync(options.envFile)) { const envContent = fs.readFileSync(options.envFile, 'utf-8'); envContent.split('\n').forEach(line => { const match = line.match(/^([^=]+)=(.*)$/); if (match) { env[match[1].trim()] = match[2].trim(); } }); } // Add default environment variables env = { NODE_ENV: 'production', PORT: options.port || process.env.PORT || '3000', HOST: process.env.HOST || '0.0.0.0', LOG_LEVEL: process.env.LOG_LEVEL || 'info', ...env }; return { name: 'mcp-fullstack', displayName: 'MCP Fullstack Server', description: 'MCP Server for full-stack software engineering with browser automation, web search, and deployment tools', script: scriptPath, user: options.user, env, workingDirectory, logFile: path.join(os.homedir(), '.mcp-fullstack', 'logs', 'service.log'), errorFile: path.join(os.homedir(), '.mcp-fullstack', 'logs', 'service.error.log') }; } // Parse command line arguments program.parse(process.argv); // Show help if no command specified if (!process.argv.slice(2).length) { program.outputHelp(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JacobFV/mcp-fullstack'

If you have feedback or need assistance with the MCP directory API, please join our Discord server