From c0190e90c99c4e4f48fbd4d21449fced8689125a Mon Sep 17 00:00:00 2001 From: leex279 Date: Sat, 8 Nov 2025 20:45:04 +0100 Subject: [PATCH 1/2] Fix MCP server HTTP health endpoint for monitoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing HTTP /health endpoint to MCP server to support the HTTP-based health monitoring system introduced in commit 987c874. Background: After removing Docker socket monitoring for security (CVE-2025-9074), the backend API switched to HTTP health checks. However, the MCP server only exposed MCP tools via the MCP protocol, causing 404 errors when the API tried to check /health. Changes: - Add Starlette Request/JSONResponse imports for HTTP handling - Implement http_health_endpoint() async function - Track server uptime using module-level _server_start_time - Register /health endpoint using FastMCP's custom_route decorator - Return proper JSON response with success, status, uptime_seconds Response format: { "success": true, "status": "ready", "uptime_seconds": , "timestamp": "" } Testing: - Direct endpoint: curl http://localhost:8051/health - Via API: curl http://localhost:8181/api/mcp/status - UI MCP Status Dashboard now displays properly Fixes: MCP server 404 errors and UI error state 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- python/src/mcp_server/mcp_server.py | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/python/src/mcp_server/mcp_server.py b/python/src/mcp_server/mcp_server.py index eac60401..0d84eb7b 100644 --- a/python/src/mcp_server/mcp_server.py +++ b/python/src/mcp_server/mcp_server.py @@ -31,6 +31,8 @@ from typing import Any from dotenv import load_dotenv from mcp.server.fastmcp import Context, FastMCP +from starlette.requests import Request +from starlette.responses import JSONResponse # Add the project root to Python path for imports sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) @@ -546,6 +548,56 @@ except Exception as e: raise +# Track server start time at module level for health checks +_server_start_time = time.time() + + +# Define health endpoint function at module level +async def http_health_endpoint(request: Request): + """HTTP health check endpoint for monitoring systems.""" + logger.info("Health endpoint called via HTTP") + try: + # Calculate uptime from module load time + uptime = time.time() - _server_start_time + + # Try to get the shared context for detailed health info + if _shared_context and hasattr(_shared_context, "health_status"): + await perform_health_checks(_shared_context) + + return JSONResponse({ + "success": True, + "health": _shared_context.health_status, + "uptime_seconds": uptime, + "timestamp": datetime.now().isoformat(), + }) + else: + # Server starting up or no MCP connections yet - still return uptime + return JSONResponse({ + "success": True, + "status": "ready", + "uptime_seconds": uptime, + "message": "MCP server is running (no active connections yet)", + "timestamp": datetime.now().isoformat(), + }) + except Exception as e: + logger.error(f"HTTP health check failed: {e}") + return JSONResponse({ + "success": False, + "error": f"Health check failed: {str(e)}", + "uptime_seconds": time.time() - _server_start_time, + "timestamp": datetime.now().isoformat(), + }, status_code=500) + + +# Register health endpoint using FastMCP's custom_route decorator +try: + mcp.custom_route("/health", methods=["GET"])(http_health_endpoint) + logger.info("✓ HTTP /health endpoint registered successfully") +except Exception as e: + logger.error(f"✗ Failed to register /health endpoint: {e}") + logger.error(traceback.format_exc()) + + def main(): """Main entry point for the MCP server.""" try: From 4620cfa8d6561aa54e487aa512b0480cec9d5a3e Mon Sep 17 00:00:00 2001 From: leex279 Date: Sat, 8 Nov 2025 22:34:09 +0100 Subject: [PATCH 2/2] Fix: Address CodeRabbit suggestions for health endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use context.startup_time for consistent uptime tracking - Prefer context.startup_time when available (matches MCP health_check tool) - Fallback to module-level _server_start_time during startup - Add exc_info=True to exception logging for full stack traces - Follows coding guidelines for error message preservation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- python/src/mcp_server/mcp_server.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/python/src/mcp_server/mcp_server.py b/python/src/mcp_server/mcp_server.py index 0d84eb7b..f4796acb 100644 --- a/python/src/mcp_server/mcp_server.py +++ b/python/src/mcp_server/mcp_server.py @@ -29,7 +29,6 @@ from pathlib import Path from typing import Any from dotenv import load_dotenv - from mcp.server.fastmcp import Context, FastMCP from starlette.requests import Request from starlette.responses import JSONResponse @@ -557,11 +556,10 @@ async def http_health_endpoint(request: Request): """HTTP health check endpoint for monitoring systems.""" logger.info("Health endpoint called via HTTP") try: - # Calculate uptime from module load time - uptime = time.time() - _server_start_time - # Try to get the shared context for detailed health info if _shared_context and hasattr(_shared_context, "health_status"): + # Use actual server startup time for consistency with MCP health_check tool + uptime = time.time() - _shared_context.startup_time await perform_health_checks(_shared_context) return JSONResponse({ @@ -571,7 +569,8 @@ async def http_health_endpoint(request: Request): "timestamp": datetime.now().isoformat(), }) else: - # Server starting up or no MCP connections yet - still return uptime + # Server starting up or no MCP connections yet - use module load time as fallback + uptime = time.time() - _server_start_time return JSONResponse({ "success": True, "status": "ready", @@ -580,7 +579,7 @@ async def http_health_endpoint(request: Request): "timestamp": datetime.now().isoformat(), }) except Exception as e: - logger.error(f"HTTP health check failed: {e}") + logger.error(f"HTTP health check failed: {e}", exc_info=True) return JSONResponse({ "success": False, "error": f"Health check failed: {str(e)}",