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
JavaScript
/**
* 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);
}
});
}
;