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