#!/usr/bin/env node /** * @fileoverview HTTP Server for Laravel Healthcare MCP Server * Provides HTTP endpoints for testing, monitoring, and direct API access */ import express from "express"; import cors from "cors"; import { ConfigManager } from "./src/config/ConfigManager.js"; import { AuthManager } from "./src/auth/AuthManager.js"; import { ApiClient } from "./src/proxy/ApiClient.js"; import { ToolGenerator } from "./src/tools/ToolGenerator.js"; import { logger, auditLog } from "./src/utils/logger.js"; import { ErrorHandler } from "./src/utils/errors.js"; /** * HTTP Server class for Laravel Healthcare MCP Server */ class HttpServer { constructor() { this.app = express(); this.config = null; this.authManager = null; this.apiClient = null; this.toolGenerator = null; this.server = null; } /** * Initialize the HTTP server */ async initialize() { try { console.log("🔄 Initializing HTTP Server..."); logger.info("Initializing HTTP Server..."); // Load configuration this.config = new ConfigManager(); // Initialize components this.authManager = new AuthManager(null, this.config.getAll(true)); this.apiClient = new ApiClient(this.config.getAll(), this.authManager); this.toolGenerator = new ToolGenerator(this.apiClient); // Setup Express middleware this.setupMiddleware(); // Setup routes this.setupRoutes(); console.log("✅ HTTP Server initialized successfully"); logger.info("HTTP Server initialized successfully"); } catch (error) { logger.error("Failed to initialize HTTP server:", error); throw error; } } /** * Setup Express middleware */ setupMiddleware() { // CORS if (this.config.get("ENABLE_CORS", true)) { const corsOrigins = this.config.get("CORS_ORIGINS", "*"); this.app.use( cors({ origin: corsOrigins === "*" ? true : corsOrigins.split(","), credentials: true, }) ); } // JSON parsing this.app.use(express.json({ limit: "10mb" })); this.app.use(express.urlencoded({ extended: true, limit: "10mb" })); // Request logging this.app.use((req, res, next) => { logger.debug(`HTTP ${req.method} ${req.path}`, { ip: req.ip, userAgent: req.get("User-Agent"), query: req.query, }); next(); }); } /** * Setup HTTP routes */ setupRoutes() { // Health check endpoint this.app.get("/health", (req, res) => { try { const health = { status: "healthy", timestamp: new Date().toISOString(), server: { name: this.config.get("MCP_SERVER_NAME"), version: this.config.get("MCP_SERVER_VERSION"), uptime: process.uptime(), }, tools: { total: this.toolGenerator.getToolNames().length, categories: this.getToolCategories(), }, auth: { configured: this.getConfiguredAuthTypes(), cacheStats: this.authManager.getCacheStats(), }, api: this.apiClient.getHealthStatus(), }; res.json(health); } catch (error) { logger.error("Health check failed:", error); res.status(500).json({ status: "unhealthy", error: error.message, timestamp: new Date().toISOString(), }); } }); // List all MCP tools this.app.get("/tools", (req, res) => { try { const tools = this.toolGenerator.generateAllTools(); const toolsWithDetails = tools.map((tool) => { const toolDef = this.toolGenerator.getTool(tool.name); return { name: tool.name, description: tool.description, authType: toolDef?.authType, category: toolDef?.endpoint?.category, method: toolDef?.endpoint?.method, path: toolDef?.endpoint?.path, inputSchema: tool.inputSchema, }; }); res.json({ total: toolsWithDetails.length, tools: toolsWithDetails, }); } catch (error) { logger.error("Failed to list tools:", error); res.status(500).json({ error: error.message }); } }); // Get tool by name this.app.get("/tools/:toolName", (req, res) => { try { const { toolName } = req.params; const tool = this.toolGenerator.getTool(toolName); if (!tool) { return res.status(404).json({ error: "Tool not found" }); } res.json({ name: tool.name, description: tool.description, authType: tool.authType, endpoint: tool.endpoint, inputSchema: tool.inputSchema, }); } catch (error) { logger.error("Failed to get tool:", error); res.status(500).json({ error: error.message }); } }); // Execute MCP tool via HTTP this.app.post("/tools/:toolName/execute", async (req, res) => { const { toolName } = req.params; const parameters = req.body; try { logger.info(`HTTP execution of tool: ${toolName}`); const tool = this.toolGenerator.getTool(toolName); if (!tool) { return res.status(404).json({ error: "Tool not found" }); } const result = await tool.execute(parameters); auditLog("tool_executed_http", "http_user", { toolName, authType: tool.authType, success: true, }); res.json({ success: true, toolName, result, }); } catch (error) { logger.error(`HTTP tool execution failed for ${toolName}:`, error); auditLog("tool_executed_http", "http_user", { toolName, success: false, error: error.message, }); const errorResponse = ErrorHandler.handleMcpError(error, toolName); res.status(error.status || 500).json({ success: false, toolName, ...errorResponse, }); } }); // Get server statistics this.app.get("/stats", (req, res) => { try { const stats = { server: { name: this.config.get("MCP_SERVER_NAME"), version: this.config.get("MCP_SERVER_VERSION"), uptime: process.uptime(), memory: process.memoryUsage(), nodeVersion: process.version, }, tools: { total: this.toolGenerator.getToolNames().length, byAuthType: this.getToolsByAuthType(), byCategory: this.getToolCategories(), }, auth: { configured: this.getConfiguredAuthTypes(), cacheStats: this.authManager.getCacheStats(), }, config: this.config.getSummary(), }; res.json(stats); } catch (error) { logger.error("Failed to get stats:", error); res.status(500).json({ error: error.message }); } }); // Configuration endpoint (non-sensitive) this.app.get("/config", (req, res) => { try { const config = this.config.getAll(false); // Don't include sensitive data res.json(config); } catch (error) { logger.error("Failed to get config:", error); res.status(500).json({ error: error.message }); } }); // Authentication status this.app.get("/auth/status", async (req, res) => { try { const results = await this.authManager.validateAllCredentials(); res.json({ authTypes: results, summary: { total: Object.keys(results).length, valid: Object.values(results).filter((r) => r.valid).length, invalid: Object.values(results).filter((r) => !r.valid).length, }, }); } catch (error) { logger.error("Failed to check auth status:", error); res.status(500).json({ error: error.message }); } }); // 404 handler this.app.use("*", (req, res) => { res.status(404).json({ error: "Endpoint not found", availableEndpoints: [ "GET /health", "GET /tools", "GET /tools/:toolName", "POST /tools/:toolName/execute", "GET /stats", "GET /config", "GET /auth/status", ], }); }); // Error handler this.app.use((error, req, res, next) => { logger.error("HTTP server error:", error); res.status(500).json({ error: "Internal server error", message: error.message, }); }); } /** * Get configured authentication types */ getConfiguredAuthTypes() { const summary = this.config.getSummary(); return summary.authTypesConfigured; } /** * Get tool categories summary */ getToolCategories() { const tools = this.toolGenerator.generateAllTools(); const categories = {}; tools.forEach((tool) => { const toolDef = this.toolGenerator.getTool(tool.name); if (toolDef?.endpoint?.category) { const category = toolDef.endpoint.category; categories[category] = (categories[category] || 0) + 1; } }); return categories; } /** * Get tools by auth type summary */ getToolsByAuthType() { const tools = this.toolGenerator.generateAllTools(); const authTypes = {}; tools.forEach((tool) => { const toolDef = this.toolGenerator.getTool(tool.name); if (toolDef?.authType) { const authType = toolDef.authType; authTypes[authType] = (authTypes[authType] || 0) + 1; } }); return authTypes; } /** * Start the HTTP server */ async start() { const port = this.config.get("MCP_SERVER_PORT", 3000); const host = this.config.get("MCP_SERVER_HOST", "0.0.0.0"); return new Promise((resolve, reject) => { this.server = this.app.listen(port, host, (error) => { if (error) { logger.error("Failed to start HTTP server:", error); reject(error); } else { const serverUrl = `http://${ host === "0.0.0.0" ? "localhost" : host }:${port}`; logger.info(`HTTP Server started on http://${host}:${port}`); // Clear console output with startup banner console.log("\n" + "=".repeat(60)); console.log("🚀 LARAVEL HEALTHCARE MCP SERVER - HTTP MODE"); console.log("=".repeat(60)); console.log(`📡 Server URL: ${serverUrl}`); console.log(`🌐 Host: ${host}`); console.log(`🔌 Port: ${port}`); console.log("=".repeat(60)); console.log("📋 Available Endpoints:"); console.log(` • Health Check: ${serverUrl}/health`); console.log(` • Tools List: ${serverUrl}/tools`); console.log(` • Server Stats: ${serverUrl}/stats`); console.log(` • Auth Status: ${serverUrl}/auth/status`); console.log(` • Configuration: ${serverUrl}/config`); console.log("=".repeat(60)); console.log("🔧 Tool Execution:"); console.log(` POST ${serverUrl}/tools/{toolName}/execute`); console.log("=".repeat(60)); console.log("📊 Server Status: READY"); console.log(`⏰ Started at: ${new Date().toLocaleString()}`); console.log("=".repeat(60)); console.log("💡 Press Ctrl+C to stop the server"); console.log(""); auditLog("http_server_started", "system", { port, host, url: serverUrl, }); resolve(); } }); }); } /** * Stop the HTTP server */ async stop() { if (this.server) { return new Promise((resolve) => { this.server.close(() => { logger.info("HTTP Server stopped"); auditLog("http_server_stopped", "system", {}); resolve(); }); }); } } } /** * Main execution function */ async function main() { console.log("🚀 Starting Laravel Healthcare MCP HTTP Server..."); const httpServer = new HttpServer(); try { // Initialize and start HTTP server console.log("📋 Step 1: Initializing server..."); await httpServer.initialize(); console.log("📋 Step 2: Starting HTTP server..."); await httpServer.start(); // Graceful shutdown const shutdown = async (signal) => { logger.info(`Received ${signal}, shutting down HTTP server...`); await httpServer.stop(); process.exit(0); }; process.on("SIGTERM", () => shutdown("SIGTERM")); process.on("SIGINT", () => shutdown("SIGINT")); process.on("SIGUSR2", () => shutdown("SIGUSR2")); } catch (error) { console.error("❌ HTTP Server startup failed:", error.message); console.error("Stack trace:", error.stack); logger.error("HTTP Server startup failed:", error); process.exit(1); } } // Run if executed directly if (import.meta.url === `file://${process.argv[1]}`) { main().catch((error) => { console.error("Fatal error:", error); process.exit(1); }); } export { HttpServer };