From 2e8517c62daf70eab472a6b7519644769af5eb23 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sun, 11 Jan 2026 15:55:35 -0700 Subject: [PATCH] Add /health endpoint for Docker healthchecks - Create wrapper Starlette app with health route + FastMCP mount - Pass FastMCP lifespan to parent app for proper session management - Health returns JSON with status, version, timestamp, transport - Use uvicorn directly for better ASGI integration --- src/mcwaddams/server.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/mcwaddams/server.py b/src/mcwaddams/server.py index a5f6a8e..4ff7beb 100644 --- a/src/mcwaddams/server.py +++ b/src/mcwaddams/server.py @@ -12,6 +12,7 @@ Architecture uses official FastMCP MCPMixin pattern for clean separation of conc import os import tempfile +from datetime import datetime, timezone from fastmcp import FastMCP from fastmcp.prompts import Prompt @@ -568,6 +569,11 @@ def main(): if transport == "streamable-http": # HTTP transport for hosted/Docker mode + import uvicorn + from starlette.applications import Starlette + from starlette.responses import JSONResponse + from starlette.routing import Route, Mount + host = os.environ.get("MCP_HOST", "0.0.0.0") port = int(os.environ.get("MCP_PORT", "8000")) @@ -577,17 +583,37 @@ def main(): except Exception: pkg_version = "0.1.0" + # Health check endpoint + async def health_check(request): + return JSONResponse({ + "status": "healthy", + "service": "mcwaddams", + "version": pkg_version, + "timestamp": datetime.now(timezone.utc).isoformat(), + "transport": "streamable-http", + }) + + # Get FastMCP's HTTP app + mcp_app = app.http_app() + + # Create wrapper app with health endpoint + MCP routes + # IMPORTANT: Must pass mcp_app.lifespan to initialize task groups + wrapper_app = Starlette( + routes=[ + Route("/health", health_check, methods=["GET"]), + Mount("/", app=mcp_app), # Mount MCP at root (serves /mcp) + ], + lifespan=mcp_app.lifespan, # Required for FastMCP session management + ) + print(f"🖨️ mcwaddams v{pkg_version}") print(f"📋 MCP Office Tools - Document Extraction Server") print(f"🌐 Starting streamable-http transport on {host}:{port}") print(f" Endpoint: http://{host}:{port}/mcp") + print(f" Health: http://{host}:{port}/health") print() - app.run( - transport="streamable-http", - host=host, - port=port, - ) + uvicorn.run(wrapper_app, host=host, port=port, log_level="info") else: # Default stdio transport for local CLI usage # CRITICAL: show_banner=False is required for stdio transport!