UNPKG

remcode

Version:

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

279 lines (278 loc) 13.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MCPServer = void 0; const express_1 = __importDefault(require("express")); const cors_1 = __importDefault(require("cors")); const logger_1 = require("../utils/logger"); // MCP Handlers const pinecone_1 = require("./handlers/pinecone"); const github_1 = require("./handlers/github"); const huggingface_1 = require("./handlers/huggingface"); const setup_1 = require("./handlers/setup"); const search_1 = require("./handlers/search"); const processing_1 = require("./handlers/processing"); const repository_1 = require("./handlers/repository"); const remcode_1 = require("./handlers/remcode"); // SWE guidance middleware not implemented yet const simple_validator_1 = require("./validation/simple-validator"); const mcp_sse_handler_1 = require("./sse/mcp-sse-handler"); const logger = (0, logger_1.getLogger)('MCPServer'); /** * MCP Server Module * * This module provides Model Context Protocol (MCP) server functionality * to allow AI assistants to interact with the remcode tools. */ class MCPServer { constructor(options = {}) { this.app = (0, express_1.default)(); // Initialize SSE Handler this.mcpSSEHandler = new mcp_sse_handler_1.MCPSSEHandler(); // Initialize handlers with proper tokens const githubToken = process.env.GITHUB_TOKEN; const pineconeApiKey = process.env.PINECONE_API_KEY; const huggingfaceToken = process.env.HUGGINGFACE_TOKEN; this.pineconeHandler = new pinecone_1.PineconeMCPHandler({ apiKey: pineconeApiKey || '' }); this.githubHandler = new github_1.GitHubMCPHandler({ token: githubToken || '' }); this.huggingfaceHandler = new huggingface_1.HuggingFaceMCPHandler({ token: huggingfaceToken || '' }); this.setupHandler = new setup_1.SetupMCPHandler(githubToken); this.searchHandler = new search_1.SearchMCPHandler(); this.processingHandler = new processing_1.ProcessingMCPHandler(githubToken); this.repositoryHandler = new repository_1.RepositoryMCPHandler(githubToken || ''); this.remcodeHandler = new remcode_1.RemcodeMCPHandler(); this.setupMiddleware(options); this.setupRoutes(); logger.info('MCP Server initialized with universal validation'); } setupMiddleware(options) { // CORS configuration const corsOptions = { origin: true, credentials: true, methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], ...options.corsOptions }; this.app.use((0, cors_1.default)(corsOptions)); this.app.use(express_1.default.json()); this.app.use(express_1.default.text()); // SWE Guidance Middleware (placeholder) // this.app.use(sweGuidanceMiddleware); logger.debug('Middleware configured'); } setupRoutes() { // Health check endpoint this.app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), version: '0.1.8', services: ['pinecone', 'github', 'huggingface', 'setup', 'search', 'processing'] }); }); // MCP specification endpoint this.app.get('/mcp/spec', (req, res) => { res.json({ tools: this.getMCPToolSpecs(), version: '0.1.8', protocol: 'MCP', transport: 'SSE' }); }); // SSE endpoint for MCP Inspector this.app.get('/sse', (req, res) => { this.mcpSSEHandler.handleSSEConnection(req, res); }); // JSON-RPC 2.0 messages endpoint for MCP Inspector this.app.post('/messages', async (req, res) => { const toolHandlers = { pinecone: this.pineconeHandler, github: this.githubHandler, huggingface: this.huggingfaceHandler, setup: this.setupHandler, search: this.searchHandler, processing: this.processingHandler, repository: this.repositoryHandler, remcode: this.remcodeHandler }; await this.mcpSSEHandler.handleMCPMessage(req, res, toolHandlers); }); // Universal MCP tool router with validation guard this.app.post('/mcp/:tool', async (req, res, next) => { const tool = req.params.tool; try { // Universal validation for ALL MCP tools const validation = await simple_validator_1.SimpleValidator.validateQuick(); if (!validation.allValid) { const errorMessage = `Missing required services. Configure: ${!validation.github.valid ? 'GitHub token, ' : ''}${!validation.huggingface.valid ? 'HuggingFace token, ' : ''}${!validation.pinecone.valid ? 'Pinecone API key' : ''}`.replace(/, $/, ''); res.status(400).json({ error: 'Service Configuration Required', message: errorMessage, services: validation, instructions: [ 'Set GITHUB_TOKEN environment variable with your GitHub token', 'Set HUGGINGFACE_TOKEN environment variable with your HuggingFace token', 'Set PINECONE_API_KEY environment variable with your Pinecone API key', 'Restart the MCP server after setting environment variables' ] }); return; } // Route to appropriate handler if (tool === 'pinecone_query' || tool === 'pinecone_upsert' || tool === 'pinecone_list_indexes') { return this.pineconeHandler.handleRequest(req, res); } else if (tool === 'github_get_repo' || tool === 'github_list_files' || tool === 'github_get_file') { return this.githubHandler.handleRequest(req, res); } else if (tool === 'huggingface_embed_code' || tool === 'huggingface_embed_query' || tool === 'huggingface_list_models') { return this.huggingfaceHandler.handleRequest(req, res); } else if (tool === 'setup-repository' || tool === 'check-prerequisites' || tool === 'configure-repository' || tool === 'setup-secrets' || tool === 'generate-workflows') { return this.setupHandler.handleSetupRepository(req, res); } else if (tool === 'search' || tool === 'search_code' || tool === 'get_code_context') { return this.searchHandler.handleSearch(req, res); } else if (tool === 'trigger-reprocessing' || tool === 'get-processing-status') { return this.processingHandler.handleTriggerReprocessing(req, res); } else if (tool === 'list_repositories' || tool === 'get_repository_status') { // Use a basic method for repository handler res.status(200).json({ message: 'Repository handler not fully implemented' }); } else if (tool === 'default_prompt' || tool === 'get_scenarios' || tool === 'get_guidelines') { // Use a basic method for remcode handler res.status(200).json({ message: 'SWE guidance handler not fully implemented' }); } else { res.status(404).json({ error: `Unknown tool: ${tool}` }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`MCP tool error for ${tool}: ${errorMessage}`); res.status(500).json({ error: errorMessage }); } }); logger.info('Routes configured with universal validation guard'); } /** * Get MCP tool specifications for all available tools */ getMCPToolSpecs() { return [ // Setup Tools { name: 'setup-repository', description: 'Initialize and configure repository for remcode usage', parameters: { owner: { type: 'string', description: 'Repository owner' }, repo: { type: 'string', description: 'Repository name' }, token: { type: 'string', description: 'GitHub token (optional)', optional: true }, branch: { type: 'string', description: 'Default branch (optional, default: main)', optional: true }, workflowType: { type: 'string', description: 'Workflow type: basic, advanced, scheduled (optional)', optional: true }, skipWorkflows: { type: 'boolean', description: 'Skip GitHub Actions workflow creation (optional)', optional: true }, skipSecrets: { type: 'boolean', description: 'Skip repository secrets configuration (optional)', optional: true }, confirm: { type: 'boolean', description: 'Confirm setup (optional, default: false)', optional: true } } }, // Search Tools { name: 'search', description: 'Search for code patterns and functions in the codebase', parameters: { text: { type: 'string', description: 'Search query text' }, topK: { type: 'number', description: 'Number of results to return (optional, default: 5)', optional: true }, includeContent: { type: 'boolean', description: 'Include full content in results (optional)', optional: true } } }, // GitHub Tools { name: 'github_get_repo', description: 'Get repository information from GitHub', parameters: { owner: { type: 'string', description: 'Repository owner' }, repo: { type: 'string', description: 'Repository name' } } }, // Pinecone Tools { name: 'pinecone_query', description: 'Query vectors in Pinecone database', parameters: { text: { type: 'string', description: 'Query text to search for' }, topK: { type: 'number', description: 'Number of results to return (optional, default: 5)', optional: true }, namespace: { type: 'string', description: 'Pinecone namespace (optional)', optional: true } } }, // HuggingFace Tools { name: 'huggingface_embed_code', description: 'Generate embeddings for code using HuggingFace models', parameters: { code: { type: 'string', description: 'Code to embed' }, model: { type: 'string', description: 'Model to use (optional)', optional: true } } }, // Processing Tools { name: 'trigger-reprocessing', description: 'Trigger reprocessing of repository codebase', parameters: { force: { type: 'boolean', description: 'Force full reprocessing (optional)', optional: true } } }, // Repository Tools { name: 'get_repository_status', description: 'Get current repository processing status', parameters: {} }, // SWE Tools { name: 'default_prompt', description: 'Get default software engineering prompt with Remcode MCP integration', parameters: { scenario: { type: 'string', description: 'Specific scenario (optional)', optional: true } } } ]; } async start(port = 3000, host = 'localhost') { return new Promise((resolve, reject) => { try { this.server = this.app.listen(port, host, () => { logger.info(`🚀 MCP Server running on http://${host}:${port}`); logger.info(`📡 SSE endpoint: http://${host}:${port}/sse`); logger.info(`🔧 Health check: http://${host}:${port}/health`); logger.info(`📋 MCP spec: http://${host}:${port}/mcp/spec`); resolve(); }); this.server.on('error', (error) => { logger.error(`Server error: ${error.message}`); reject(error); }); } catch (error) { logger.error(`Failed to start server: ${error}`); reject(error); } }); } async stop() { if (this.server) { return new Promise((resolve) => { this.server.close(() => { logger.info('MCP Server stopped'); resolve(); }); }); } } } exports.MCPServer = MCPServer; exports.default = MCPServer;