first
This commit is contained in:
426
http-tools-server.js
Normal file
426
http-tools-server.js
Normal file
@@ -0,0 +1,426 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* HTTP server with MCP tool execution support
|
||||
*/
|
||||
|
||||
import dotenv from "dotenv";
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
|
||||
// Load environment variables from .env file
|
||||
dotenv.config();
|
||||
|
||||
console.log("🔧 Environment variables loaded:");
|
||||
console.log(
|
||||
` LARAVEL_API_BASE_URL: ${process.env.LARAVEL_API_BASE_URL || "NOT SET"}`
|
||||
);
|
||||
console.log(` MCP_SERVER_PORT: ${process.env.MCP_SERVER_PORT || "NOT SET"}`);
|
||||
console.log(` MCP_SERVER_HOST: ${process.env.MCP_SERVER_HOST || "NOT SET"}`);
|
||||
console.log("");
|
||||
|
||||
// Set default values if not provided
|
||||
process.env.LARAVEL_API_BASE_URL =
|
||||
process.env.LARAVEL_API_BASE_URL || "https://example.com";
|
||||
|
||||
console.log("🚀 Starting Laravel Healthcare MCP HTTP Server with Tools...");
|
||||
|
||||
const app = express();
|
||||
const port = process.env.MCP_SERVER_PORT || 3000;
|
||||
const host = process.env.MCP_SERVER_HOST || "0.0.0.0";
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json({ limit: "10mb" }));
|
||||
|
||||
// Initialize MCP components
|
||||
let toolGenerator = null;
|
||||
let authManager = null;
|
||||
|
||||
async function initializeMCP() {
|
||||
try {
|
||||
console.log("📋 Initializing MCP components...");
|
||||
|
||||
const { ConfigManager } = await import("./src/config/ConfigManager.js");
|
||||
const { AuthManager } = await import("./src/auth/AuthManager.js");
|
||||
const { ApiClient } = await import("./src/proxy/ApiClient.js");
|
||||
const { ToolGenerator } = await import("./src/tools/ToolGenerator.js");
|
||||
|
||||
const config = new ConfigManager();
|
||||
authManager = new AuthManager(null, config.getAll(true));
|
||||
const apiClient = new ApiClient(config.getAll(), authManager);
|
||||
toolGenerator = new ToolGenerator(apiClient);
|
||||
|
||||
console.log("✅ MCP components initialized");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to initialize MCP:", error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Health endpoint
|
||||
app.get("/health", (req, res) => {
|
||||
res.json({
|
||||
status: "healthy",
|
||||
timestamp: new Date().toISOString(),
|
||||
server: "Laravel Healthcare MCP Server",
|
||||
port: port,
|
||||
mcpInitialized: toolGenerator !== null,
|
||||
});
|
||||
});
|
||||
|
||||
// List all tools
|
||||
app.get("/tools", (req, res) => {
|
||||
try {
|
||||
if (!toolGenerator) {
|
||||
return res.status(500).json({ error: "MCP not initialized" });
|
||||
}
|
||||
|
||||
const tools = toolGenerator.generateAllTools();
|
||||
const toolsWithDetails = tools.map((tool) => {
|
||||
const toolDef = toolGenerator.getTool(tool.name);
|
||||
return {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
authType: toolDef?.authType,
|
||||
method: toolDef?.endpoint?.method,
|
||||
path: toolDef?.endpoint?.path,
|
||||
inputSchema: tool.inputSchema,
|
||||
};
|
||||
});
|
||||
|
||||
res.json({
|
||||
total: toolsWithDetails.length,
|
||||
tools: toolsWithDetails,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to list tools:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get specific tool
|
||||
app.get("/tools/:toolName", (req, res) => {
|
||||
try {
|
||||
if (!toolGenerator) {
|
||||
return res.status(500).json({ error: "MCP not initialized" });
|
||||
}
|
||||
|
||||
const { toolName } = req.params;
|
||||
const tool = 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) {
|
||||
console.error("Failed to get tool:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Execute MCP tool
|
||||
app.post("/tools/:toolName/execute", async (req, res) => {
|
||||
const { toolName } = req.params;
|
||||
const parameters = req.body;
|
||||
|
||||
try {
|
||||
if (!toolGenerator) {
|
||||
return res.status(500).json({ error: "MCP not initialized" });
|
||||
}
|
||||
|
||||
console.log(`🔧 Executing tool: ${toolName}`);
|
||||
console.log(`📝 Parameters:`, JSON.stringify(parameters, null, 2));
|
||||
|
||||
// Get all tools and find the one we want
|
||||
const allTools = toolGenerator.generateAllTools();
|
||||
const toolDef = allTools.find((tool) => tool.name === toolName);
|
||||
|
||||
if (!toolDef) {
|
||||
console.log(`❌ Tool ${toolName} not found in generated tools`);
|
||||
return res.status(404).json({ error: "Tool not found" });
|
||||
}
|
||||
|
||||
console.log(`🔍 Found tool: ${toolDef.name}`);
|
||||
|
||||
// Get the actual tool implementation
|
||||
const tool = toolGenerator.getTool(toolName);
|
||||
console.log(tool);
|
||||
if (!tool || !tool.execute) {
|
||||
console.log(`❌ Tool ${toolName} has no execute method`);
|
||||
return res
|
||||
.status(500)
|
||||
.json({ error: "Tool execution method not available" });
|
||||
}
|
||||
|
||||
console.log(`🚀 Executing tool...`);
|
||||
const result = await tool.execute(parameters);
|
||||
|
||||
console.log(`✅ Tool ${toolName} executed successfully`);
|
||||
console.log(`📊 Result:`, JSON.stringify(result, null, 2));
|
||||
|
||||
// Special handling for login tools - extract and store token
|
||||
if (toolName === "public_manage_login" && result && authManager) {
|
||||
try {
|
||||
let token = null;
|
||||
let expiresIn = 3600; // Default 1 hour
|
||||
let userData = null;
|
||||
|
||||
// Extract token from different possible response formats
|
||||
if (result.accessToken || result.access_token || result.token) {
|
||||
token = result.accessToken || result.access_token || result.token;
|
||||
expiresIn = result.expiresIn || result.expires_in || 3600;
|
||||
userData = result.userData || result.user || result.data || null;
|
||||
} else if (result.data) {
|
||||
// Token might be nested in data object
|
||||
token =
|
||||
result.data.accessToken ||
|
||||
result.data.access_token ||
|
||||
result.data.token;
|
||||
expiresIn = result.data.expiresIn || result.data.expires_in || 3600;
|
||||
userData = result.data.userData || result.data.user || null;
|
||||
}
|
||||
|
||||
if (token) {
|
||||
// Store token for provider auth type
|
||||
authManager.setToken("provider", token, expiresIn, userData);
|
||||
console.log(
|
||||
`🔑 Stored bearer token for provider authentication (expires in ${expiresIn}s)`
|
||||
);
|
||||
|
||||
// Add token info to response
|
||||
result._tokenInfo = {
|
||||
stored: true,
|
||||
authType: "provider",
|
||||
expiresIn: expiresIn,
|
||||
message: "Token automatically stored for provider endpoints",
|
||||
};
|
||||
} else {
|
||||
console.log(`⚠️ No token found in login response`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`❌ Failed to store token from login response:`,
|
||||
error.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
toolName,
|
||||
result,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`❌ Tool execution failed for ${toolName}:`, error.message);
|
||||
console.error(`📋 Error stack:`, error.stack);
|
||||
|
||||
res.status(error.status || 500).json({
|
||||
success: false,
|
||||
toolName,
|
||||
error: error.message,
|
||||
details: error.details || null,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Server stats
|
||||
app.get("/stats", (req, res) => {
|
||||
try {
|
||||
if (!toolGenerator) {
|
||||
return res.status(500).json({ error: "MCP not initialized" });
|
||||
}
|
||||
|
||||
const tools = toolGenerator.generateAllTools();
|
||||
const publicTools = tools.filter((tool) => {
|
||||
const toolDef = toolGenerator.getTool(tool.name);
|
||||
return toolDef?.authType === "public";
|
||||
});
|
||||
const providerTools = tools.filter((tool) => {
|
||||
const toolDef = toolGenerator.getTool(tool.name);
|
||||
return toolDef?.authType === "provider";
|
||||
});
|
||||
|
||||
res.json({
|
||||
server: {
|
||||
name: "Laravel Healthcare MCP Server",
|
||||
version: "1.0.0",
|
||||
uptime: process.uptime(),
|
||||
memory: process.memoryUsage(),
|
||||
},
|
||||
tools: {
|
||||
total: tools.length,
|
||||
public: publicTools.length,
|
||||
provider: providerTools.length,
|
||||
},
|
||||
config: {
|
||||
port: port,
|
||||
host: host,
|
||||
apiUrl: process.env.LARAVEL_API_BASE_URL,
|
||||
},
|
||||
authentication: {
|
||||
hasProviderToken: authManager
|
||||
? authManager.getCacheStats().keys?.includes("token_provider") ||
|
||||
false
|
||||
: false,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to get stats:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Set bearer token manually
|
||||
app.post("/auth/set-token", (req, res) => {
|
||||
try {
|
||||
if (!authManager) {
|
||||
return res.status(500).json({ error: "Auth manager not initialized" });
|
||||
}
|
||||
|
||||
const {
|
||||
authType = "provider",
|
||||
token,
|
||||
expiresIn = 3600,
|
||||
userData = null,
|
||||
} = req.body;
|
||||
|
||||
if (!token) {
|
||||
return res.status(400).json({ error: "Token is required" });
|
||||
}
|
||||
|
||||
authManager.setToken(authType, token, expiresIn, userData);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Token set for ${authType}`,
|
||||
authType,
|
||||
expiresIn,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to set token:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get auth status
|
||||
app.get("/auth/status", (req, res) => {
|
||||
try {
|
||||
if (!authManager) {
|
||||
return res.status(500).json({ error: "Auth manager not initialized" });
|
||||
}
|
||||
|
||||
const cacheStats = authManager.getCacheStats();
|
||||
const hasProviderToken =
|
||||
cacheStats.keys?.includes("token_provider") || false;
|
||||
|
||||
res.json({
|
||||
authManager: "initialized",
|
||||
cacheStats,
|
||||
tokens: {
|
||||
provider: hasProviderToken ? "present" : "missing",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to get auth status:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 404 handler
|
||||
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",
|
||||
"POST /auth/set-token",
|
||||
"GET /auth/status",
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// Error handler
|
||||
app.use((error, req, res, next) => {
|
||||
console.error("HTTP server error:", error);
|
||||
res.status(500).json({
|
||||
error: "Internal server error",
|
||||
message: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize and start server
|
||||
async function startServer() {
|
||||
const mcpReady = await initializeMCP();
|
||||
|
||||
if (!mcpReady) {
|
||||
console.error("❌ Cannot start server without MCP initialization");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const server = app.listen(port, host, () => {
|
||||
const serverUrl = `http://${
|
||||
host === "0.0.0.0" ? "localhost" : host
|
||||
}:${port}`;
|
||||
|
||||
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(`🔗 API URL: ${process.env.LARAVEL_API_BASE_URL}`);
|
||||
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(
|
||||
` • Tool Execute: 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("");
|
||||
console.log("🧪 Test login tool:");
|
||||
console.log(
|
||||
` curl -X POST ${serverUrl}/tools/public_manage_login/execute \\`
|
||||
);
|
||||
console.log(` -H "Content-Type: application/json" \\`);
|
||||
console.log(
|
||||
` -d '{"email": "test@example.com", "password": "password"}'`
|
||||
);
|
||||
console.log("");
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
const shutdown = (signal) => {
|
||||
console.log(`\n🛑 Received ${signal}, shutting down HTTP server...`);
|
||||
server.close(() => {
|
||||
console.log("✅ HTTP server stopped");
|
||||
process.exit(0);
|
||||
});
|
||||
};
|
||||
|
||||
process.on("SIGINT", () => shutdown("SIGINT"));
|
||||
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
||||
}
|
||||
|
||||
startServer().catch((error) => {
|
||||
console.error("❌ Failed to start server:", error);
|
||||
process.exit(1);
|
||||
});
|
Reference in New Issue
Block a user