UNPKG

remcode

Version:

Turn your AI assistant into a codebase expert. Intelligent code analysis, semantic search, and software engineering guidance through MCP integration.

241 lines (240 loc) • 14.6 kB
"use strict"; /** * Enhanced MCP Server Command * * Command to start the Model Context Protocol (MCP) server with smart token management * and automatic port selection for AI assistant integration */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.serveCommand = serveCommand; const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const mcp_1 = require("../mcp"); const logger_1 = require("../utils/logger"); const port_manager_1 = require("../utils/port-manager"); const logger = (0, logger_1.getLogger)('MCP-Command'); function serveCommand(program) { program .command('serve') .description('Start the MCP server for AI assistant integration') .option('-p, --port <port>', 'Port to listen on', '3000') .option('-h, --host <host>', 'Host to bind to', 'localhost') .option('--github-token <token>', 'GitHub Personal Access Token') .option('--pinecone-key <key>', 'Pinecone API key') .option('--huggingface-token <token>', 'HuggingFace API token') .option('--cors-origins <origins>', 'Allowed CORS origins (comma-separated)') .option('-v, --verbose', 'Enable verbose output') .option('--skip-token-collection', 'Skip interactive token collection') .action(async (options) => { // Configure logger based on environment and CLI options let logLevel = logger_1.LogLevel.INFO; // Default to INFO if (options.verbose) { logLevel = logger_1.LogLevel.DEBUG; } else if (process.env.LOG_LEVEL) { // Support LOG_LEVEL environment variable const envLevel = process.env.LOG_LEVEL.toUpperCase(); switch (envLevel) { case 'TRACE': logLevel = logger_1.LogLevel.TRACE; break; case 'DEBUG': logLevel = logger_1.LogLevel.DEBUG; break; case 'INFO': logLevel = logger_1.LogLevel.INFO; break; case 'WARN': logLevel = logger_1.LogLevel.WARN; break; case 'ERROR': logLevel = logger_1.LogLevel.ERROR; break; case 'FATAL': logLevel = logger_1.LogLevel.FATAL; break; case 'SILENT': logLevel = logger_1.LogLevel.SILENT; break; default: console.log(chalk_1.default.yellow(`⚠ Unknown LOG_LEVEL: ${process.env.LOG_LEVEL}, using INFO`)); logLevel = logger_1.LogLevel.INFO; } } // Configure the logger (0, logger_1.configureLogger)({ level: logLevel, colors: true, timestamp: true }); console.log(chalk_1.default.cyan('šŸš€ Starting Remcode MCP Server...\n')); console.log(chalk_1.default.gray(`šŸ”§ Debug: Log level set to ${logLevel === logger_1.LogLevel.DEBUG ? 'DEBUG' : logLevel === logger_1.LogLevel.INFO ? 'INFO' : 'OTHER'}`)); try { // Step 1: Smart Port Selection const preferredPort = parseInt(options.port); const selectedPort = await port_manager_1.PortManager.getAvailablePort(preferredPort); // Step 2: MCP Token Management (Environment Variables Only) const mcpTokens = { GITHUB_TOKEN: process.env.GITHUB_TOKEN || options.githubToken || '', PINECONE_API_KEY: process.env.PINECONE_API_KEY || options.pineconeKey || '', HUGGINGFACE_TOKEN: process.env.HUGGINGFACE_TOKEN || options.huggingfaceToken || '' }; // Display token status (without showing actual values for security) console.log(chalk_1.default.cyan('šŸ”‘ Token Status:')); console.log(chalk_1.default.green(`āœ“ GITHUB_TOKEN: ${mcpTokens.GITHUB_TOKEN ? 'Provided via MCP environment' : 'Not provided'}`)); console.log(chalk_1.default.green(`āœ“ PINECONE_API_KEY: ${mcpTokens.PINECONE_API_KEY ? 'Provided via MCP environment' : 'Not provided'}`)); console.log(chalk_1.default.green(`āœ“ HUGGINGFACE_TOKEN: ${mcpTokens.HUGGINGFACE_TOKEN ? 'Provided via MCP environment' : 'Not provided'}`)); // Step 3: Validate Required Tokens (Graceful Degradation) const missingTokens = []; if (!mcpTokens.GITHUB_TOKEN || mcpTokens.GITHUB_TOKEN.trim() === '') missingTokens.push('GITHUB_TOKEN'); if (!mcpTokens.PINECONE_API_KEY || mcpTokens.PINECONE_API_KEY.trim() === '') missingTokens.push('PINECONE_API_KEY'); if (!mcpTokens.HUGGINGFACE_TOKEN || mcpTokens.HUGGINGFACE_TOKEN.trim() === '') missingTokens.push('HUGGINGFACE_TOKEN'); if (missingTokens.length > 0) { console.log(chalk_1.default.yellow(`\nāš ļø Missing tokens: ${missingTokens.join(', ')}`)); console.log(chalk_1.default.cyan('šŸ’” Add tokens to your AI assistant MCP configuration:')); console.log(chalk_1.default.gray(' {')); console.log(chalk_1.default.gray(' "mcpServers": {')); console.log(chalk_1.default.gray(' "remcode": {')); console.log(chalk_1.default.gray(' "command": "npx",')); console.log(chalk_1.default.gray(' "args": ["remcode"],')); console.log(chalk_1.default.gray(' "env": {')); if (missingTokens.includes('PINECONE_API_KEY')) { console.log(chalk_1.default.yellow(' "PINECONE_API_KEY": "your_key_here",')); } if (missingTokens.includes('HUGGINGFACE_TOKEN')) { console.log(chalk_1.default.yellow(' "HUGGINGFACE_TOKEN": "your_token_here",')); } if (missingTokens.includes('GITHUB_TOKEN')) { console.log(chalk_1.default.yellow(' "GITHUB_TOKEN": "your_github_token"')); } console.log(chalk_1.default.gray(' }')); console.log(chalk_1.default.gray(' }')); console.log(chalk_1.default.gray(' }')); console.log(chalk_1.default.gray(' }')); console.log(chalk_1.default.cyan('\nšŸ“š Get API keys (30 seconds total):')); console.log(chalk_1.default.cyan(' • Pinecone: https://app.pinecone.io/organizations/-/projects/-/keys')); console.log(chalk_1.default.gray(' → Sign up free → Create project → Copy API key')); console.log(chalk_1.default.cyan(' • HuggingFace: https://huggingface.co/settings/tokens')); console.log(chalk_1.default.gray(' → Sign up free → New token → Read permission → Copy')); console.log(chalk_1.default.cyan(' • GitHub: https://github.com/settings/tokens/new?scopes=repo,workflow&description=Remcode%20MCP%20Tools')); console.log(chalk_1.default.gray(' → Generate token → Select repo,workflow scopes → Copy')); // Feature availability messaging console.log(chalk_1.default.cyan('\nšŸ› ļø Available Features:')); console.log(chalk_1.default.green(' āœ… SWE prompts and guidelines (no tokens required)')); if (!missingTokens.includes('GITHUB_TOKEN')) { console.log(chalk_1.default.green(' āœ… GitHub repository operations')); } else { console.log(chalk_1.default.red(' āŒ GitHub repository operations (requires GITHUB_TOKEN)')); } if (!missingTokens.includes('HUGGINGFACE_TOKEN')) { console.log(chalk_1.default.green(' āœ… Code embedding and AI model access')); } else { console.log(chalk_1.default.red(' āŒ Code embedding and AI model access (requires HUGGINGFACE_TOKEN)')); } if (!missingTokens.includes('PINECONE_API_KEY')) { console.log(chalk_1.default.green(' āœ… Vector storage and semantic search')); } else { console.log(chalk_1.default.red(' āŒ Vector storage and semantic search (requires PINECONE_API_KEY)')); } console.log(chalk_1.default.yellow('\nšŸš€ Server will start with partial functionality\n')); } else { console.log(chalk_1.default.green('\nāœ… All tokens configured - full functionality available\n')); } // Step 4: Initialize MCP Server const spinner = (0, ora_1.default)('Initializing MCP server').start(); const server = new mcp_1.MCPServer({ port: selectedPort, host: options.host, corsOptions: { origin: options.corsOrigins?.split(',') || ['http://localhost:3000', 'http://127.0.0.1:3000'] } }); // Step 5: Start the server with enhanced error handling try { await server.start(selectedPort, options.host); spinner.succeed(chalk_1.default.green(`āœ… MCP server started successfully!`)); // Display server information console.log(''); console.log(chalk_1.default.cyan('šŸ“ Server Information:')); console.log(` 🌐 URL: ${chalk_1.default.white(`http://${options.host}:${selectedPort}`)}`); console.log(` šŸ”— Health Check: ${chalk_1.default.white(`http://${options.host}:${selectedPort}/health`)}`); console.log(` šŸ“Š API Spec: ${chalk_1.default.white(`http://${options.host}:${selectedPort}/mcp/spec`)}`); console.log(''); // Display token status console.log(chalk_1.default.cyan('šŸ”‘ Token Status:')); console.log(` GitHub: ${mcpTokens.GITHUB_TOKEN && mcpTokens.GITHUB_TOKEN.trim() ? chalk_1.default.green('āœ“ Available') : chalk_1.default.red('āœ— Missing')}`); console.log(` Pinecone: ${mcpTokens.PINECONE_API_KEY && mcpTokens.PINECONE_API_KEY.trim() ? chalk_1.default.green('āœ“ Available') : chalk_1.default.red('āœ— Missing')}`); console.log(` HuggingFace: ${mcpTokens.HUGGINGFACE_TOKEN && mcpTokens.HUGGINGFACE_TOKEN.trim() ? chalk_1.default.green('āœ“ Available') : chalk_1.default.red('āœ— Missing')}`); console.log(''); // Display available tools console.log(chalk_1.default.cyan('šŸ›  Available MCP Tools:')); console.log(' šŸ“ Repository: setup-repository, get_repository_status, list_repositories'); console.log(' šŸ” Search: search, search_code, get_code_context'); console.log(' āš™ļø Processing: trigger-reprocessing, get-processing-status'); console.log(' šŸ¤– SWE: default_prompt, get_scenarios, get_guidelines'); console.log(' šŸ™ GitHub: github_get_repo, github_list_files, github_get_file'); console.log(' 🌲 Pinecone: pinecone_query, pinecone_list_indexes'); console.log(' šŸ¤— HuggingFace: huggingface_embed_code, huggingface_embed_query'); console.log(''); console.log(chalk_1.default.magenta('šŸŽÆ Next Steps:')); console.log(' 1. Configure your AI assistant to connect to this MCP server'); console.log(` 2. Use server URL: http://${options.host}:${selectedPort}`); console.log(' 3. Start asking questions about your codebase!'); console.log(''); console.log(chalk_1.default.gray('Press Ctrl+C to stop the server')); } catch (startError) { spinner.fail('Failed to start MCP server'); if (startError instanceof Error && startError.message.includes('EADDRINUSE')) { console.error(chalk_1.default.red(`āŒ Port ${selectedPort} is unexpectedly busy`)); console.error(chalk_1.default.yellow(`šŸ’” Try a different port: npx remcode serve --port ${selectedPort + 1}`)); } else { console.error(chalk_1.default.red(`āŒ Error: ${startError instanceof Error ? startError.message : String(startError)}`)); } process.exit(1); } // Handle graceful shutdown const gracefulShutdown = () => { console.log(chalk_1.default.yellow('\nšŸ›‘ Shutting down MCP server...')); try { server.stop(); console.log(chalk_1.default.green('āœ… Server stopped successfully')); process.exit(0); } catch (error) { console.error(chalk_1.default.red('āŒ Error during shutdown:', error)); process.exit(1); } }; process.on('SIGINT', gracefulShutdown); process.on('SIGTERM', gracefulShutdown); } catch (error) { console.error(chalk_1.default.red(`\nāŒ Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}`)); // Provide helpful error messages if (error instanceof Error) { if (error.message.includes('ENOTFOUND')) { console.error(chalk_1.default.yellow('🌐 Network connectivity issue. Check your internet connection.')); } else if (error.message.includes('EACCES')) { console.error(chalk_1.default.yellow('šŸ”’ Permission denied. Try running with appropriate permissions.')); } else if (error.message.includes('No available ports')) { console.error(chalk_1.default.yellow('🚪 Try specifying a different port range: --port 4000')); } } process.exit(1); } }); }