Files
mcp-tool/http-server.js
nasir@endelospay.com 8c74b0e23f first
2025-07-11 20:22:12 +05:00

462 lines
13 KiB
JavaScript

#!/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 };