UNPKG

remcode

Version:

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

339 lines (338 loc) 14.1 kB
"use strict"; /** * SSE (Server-Sent Events) Handler for MCP Tools * * Provides real-time streaming communication for MCP Inspector integration * without STDIO bridge compatibility issues. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SSEHandler = void 0; const logger_1 = require("../../utils/logger"); const simple_validator_1 = require("../validation/simple-validator"); const logger = (0, logger_1.getLogger)('SSE-Handler'); /** * SSE connection management for MCP tools */ class SSEHandler { constructor() { this.activeConnections = new Map(); this.connectionCounter = 0; } /** * Initialize SSE connection for MCP Inspector */ initializeConnection(req, res) { const connectionId = `sse_${++this.connectionCounter}_${Date.now()}`; // Set SSE headers res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Cache-Control', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS' }); // Store connection this.activeConnections.set(connectionId, res); // Send initial connection event this.sendEvent(res, { type: 'connection', data: { connectionId, status: 'connected', timestamp: new Date().toISOString(), message: 'SSE connection established for MCP tools' } }); // Handle client disconnect req.on('close', () => { this.activeConnections.delete(connectionId); logger.info(`SSE connection closed: ${connectionId}`); }); logger.info(`SSE connection established: ${connectionId}`); return connectionId; } /** * Send Server-Sent Event to client */ sendEvent(res, event) { try { if (event.id) { res.write(`id: ${event.id}\n`); } res.write(`event: ${event.type}\n`); res.write(`data: ${JSON.stringify(event.data)}\n\n`); } catch (error) { logger.error(`Failed to send SSE event: ${error instanceof Error ? error.message : String(error)}`); } } /** * Broadcast event to all active connections */ broadcast(event) { this.activeConnections.forEach((res, connectionId) => { try { this.sendEvent(res, event); } catch (error) { logger.error(`Failed to send to connection ${connectionId}: ${error instanceof Error ? error.message : String(error)}`); this.activeConnections.delete(connectionId); } }); } /** * Handle MCP tool execution via SSE */ async handleMCPToolRequest(req, res, toolHandlers) { const { tool, parameters = {} } = req.body; const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; try { // Send request started event this.sendEvent(res, { type: 'tool_request_started', id: requestId, data: { tool, parameters, timestamp: new Date().toISOString() } }); // 🛡️ ONE-SHOT PERMISSION VALIDATION logger.info(`🔍 Validating permissions for SSE MCP tool: ${tool}`); const validation = await simple_validator_1.SimpleValidator.validateQuick(); if (!validation.allValid) { logger.warn(`❌ Permission validation failed for SSE tool: ${tool}`); this.sendEvent(res, { type: 'tool_error', id: requestId, data: { tool, error: 'setup_required', message: 'Missing required API tokens', validation: { github: validation.github, huggingface: validation.huggingface, pinecone: validation.pinecone }, setupUrls: validation.setupUrls, instructions: [ '1. Create required API tokens using the URLs above', '2. Add tokens to your .env file', '3. Restart the MCP server', '4. Try again' ] } }); return; } logger.info(`✅ All API tokens validated for SSE tool: ${tool}`); // Route to appropriate handler and execute let result; if (tool && tool.startsWith('pinecone_')) { result = await this.executePineconeHandler(tool, parameters, toolHandlers.pinecone); } else if (tool && tool.startsWith('github_')) { result = await this.executeGitHubHandler(tool, parameters, toolHandlers.github); } else if (tool && tool.startsWith('huggingface_')) { result = await this.executeHuggingFaceHandler(tool, parameters, toolHandlers.huggingface); } else { result = await this.executeOtherHandler(tool, parameters, toolHandlers); } // Send successful result this.sendEvent(res, { type: 'tool_result', id: requestId, data: { tool, result, status: 'success', timestamp: new Date().toISOString() } }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`SSE MCP tool execution error for ${tool}: ${errorMessage}`); this.sendEvent(res, { type: 'tool_error', id: requestId, data: { tool, error: errorMessage, status: 'failed', timestamp: new Date().toISOString() } }); } } /** * Execute Pinecone handler */ async executePineconeHandler(tool, parameters, handler) { const action = tool.replace('pinecone_', ''); switch (action) { case 'query': return await handler.handleQuery(parameters); case 'list_indexes': return await handler.handleListIndexes(parameters); default: throw new Error(`Unknown Pinecone action: ${action}`); } } /** * Execute GitHub handler */ async executeGitHubHandler(tool, parameters, handler) { const action = tool.replace('github_', ''); switch (action) { case 'get_repo': return await handler.handleGetRepo(parameters); case 'list_files': return await handler.handleListFiles(parameters); case 'get_file': return await handler.handleGetFile(parameters); case 'search_code': return await handler.handleSearchCode(parameters); default: throw new Error(`Unknown GitHub action: ${action}`); } } /** * Execute HuggingFace handler */ async executeHuggingFaceHandler(tool, parameters, handler) { const action = tool.replace('huggingface_', ''); switch (action) { case 'embed_code': return await handler.handleEmbedCode(parameters); case 'embed_query': return await handler.handleEmbedQuery(parameters); case 'list_models': return await handler.handleListModels(parameters); default: throw new Error(`Unknown HuggingFace action: ${action}`); } } /** * Execute other handlers (Setup, Search, Processing, etc.) */ async executeOtherHandler(tool, parameters, handlers) { switch (tool) { case 'setup-repository': return await handlers.setup.handleSetupRepository(null, null, parameters); case 'check-prerequisites': return await handlers.setup.handleCheckPrerequisites(null, null, parameters); case 'configure-repository': return await handlers.setup.handleConfigureRepository(null, null, parameters); case 'setup-secrets': return await handlers.setup.handleSetupSecrets(null, null, parameters); case 'generate-workflows': return await handlers.setup.handleGenerateWorkflows(null, null, parameters); case 'search': return await handlers.search.handleSearch(null, null, parameters); case 'search_code': return await handlers.search.handleSearchCode(null, null, parameters); case 'get_code_context': return await handlers.search.handleGetCodeContext(null, null, parameters); case 'trigger-reprocessing': return await handlers.processing.handleTriggerReprocessing(null, null, parameters); case 'get-processing-status': return await handlers.processing.handleGetProcessingStatus(null, null, parameters); case 'get-processing-history': return await handlers.processing.handleGetProcessingHistory(null, null, parameters); case 'cancel-processing': return await handlers.processing.handleCancelProcessing(null, null, parameters); case 'retry-processing': return await handlers.processing.handleRetryProcessing(null, null, parameters); case 'get-processing-logs': return await handlers.processing.handleGetProcessingLogs(null, null, parameters); case 'get-processing-metrics': return await handlers.processing.handleGetProcessingMetrics(null, null, parameters); case 'get-workflow-analytics': return await handlers.processing.handleGetWorkflowAnalytics(null, null, parameters); case 'monitor-workflow-health': return await handlers.processing.handleMonitorWorkflowHealth(null, null, parameters); case 'get-workflow-recommendations': return await handlers.processing.handleGetWorkflowRecommendations(null, null, parameters); case 'get_repository_status': return await handlers.repository.handleGetRepositoryStatus(null, null, parameters); case 'list_repositories': return await handlers.repository.handleListRepositories(null, null, parameters); case 'default_prompt': return await handlers.remcode.handleDefaultPrompt(null, null, parameters); case 'get_scenarios': return await handlers.remcode.handleGetScenarios(null, null, parameters); case 'get_guidelines': return await handlers.remcode.handleGetGuidelines(null, null, parameters); case 'get_contextual_guidance': return await handlers.remcode.handleGetContextualGuidance(null, null, parameters); default: throw new Error(`Unknown tool: ${tool}`); } } /** * Handle SSE health check */ handleHealthCheck(req, res) { this.sendEvent(res, { type: 'health', data: { status: 'healthy', activeConnections: this.activeConnections.size, timestamp: new Date().toISOString(), version: '0.1.3' } }); } /** * Handle SSE tool listing */ handleToolList(req, res) { const tools = [ 'setup-repository', 'check-prerequisites', 'configure-repository', 'setup-secrets', 'generate-workflows', 'get_repository_status', 'list_repositories', 'search', 'search_code', 'get_code_context', 'trigger-reprocessing', 'get-processing-status', 'get-processing-history', 'cancel-processing', 'retry-processing', 'get-processing-logs', 'get-processing-metrics', 'get-workflow-analytics', 'monitor-workflow-health', 'get-workflow-recommendations', 'default_prompt', 'get_scenarios', 'get_guidelines', 'get_contextual_guidance', 'github_get_repo', 'github_list_files', 'github_get_file', 'github_search_code', 'pinecone_query', 'pinecone_list_indexes', 'huggingface_embed_code', 'huggingface_embed_query', 'huggingface_list_models' ]; this.sendEvent(res, { type: 'tool_list', data: { tools, count: tools.length, timestamp: new Date().toISOString() } }); } /** * Close all active connections */ closeAllConnections() { this.activeConnections.forEach((res, connectionId) => { try { this.sendEvent(res, { type: 'server_shutdown', data: { message: 'Server is shutting down', timestamp: new Date().toISOString() } }); res.end(); } catch (error) { logger.error(`Error closing connection ${connectionId}: ${error instanceof Error ? error.message : String(error)}`); } }); this.activeConnections.clear(); logger.info('All SSE connections closed'); } } exports.SSEHandler = SSEHandler;