fix: use correct Starlette mount pattern for MCP SSE routing
All checks were successful
Build and Push Monarch MCP Docker Image / build (push) Successful in 8s

The previous implementation added health route directly to mcp_app and
returned it, which broke MCP's internal /mcp endpoint routing.

Now matches the working pattern from komodo-mcp-custom:
- Mount mcp_app at / inside a parent Starlette app
- Pass lifespan=mcp_app.lifespan for proper task group init
- Health check is a separate route in the parent app
This commit is contained in:
Ben
2025-12-24 05:10:32 +00:00
parent 65c79efc60
commit 1210cbf6d2
2 changed files with 64 additions and 22 deletions

View File

@@ -5,17 +5,13 @@ Monarch Money MCP Server - Custom SSE Implementation.
import os
import logging
import json
import asyncio
from typing import Optional, List, Dict, Any
from datetime import datetime
from typing import Optional, Any
from dotenv import load_dotenv
from fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route, Mount
from mcp.server.sse import SseServerTransport
import uvicorn
from monarch_mcp_custom.auth import get_authenticated_client
@@ -166,35 +162,36 @@ async def refresh_accounts() -> str:
return f"Error: {str(e)}"
# --- Health Check ---
# --- Health Check Endpoint ---
async def health_check(request):
"""Simple health check endpoint."""
return JSONResponse({"status": "ok", "timestamp": datetime.now().isoformat()})
async def health(request):
"""Health check endpoint for Docker."""
return JSONResponse({"status": "ok"})
# --- ASGI App Setup ---
# --- ASGI Application ---
def create_app() -> Starlette:
"""Create the Starlette application with health check and MCP."""
def create_app():
"""Create the ASGI application with health check and MCP routes."""
mcp_app = mcp.http_app()
# Add health check route directly to the MCP app
mcp_app.add_route("/health", health_check, methods=["GET"])
# Wrapper app: /health is standalone, everything else goes to MCP
# IMPORTANT: Must pass mcp_app.lifespan for task group initialization
routes = [
Route("/health", health, methods=["GET"]),
Mount("/", app=mcp_app), # MCP handles /mcp endpoint
]
return mcp_app
return Starlette(routes=routes, lifespan=mcp_app.lifespan)
# Create the app instance for uvicorn
app = create_app()
def main():
"""Entry point for running the server."""
port = int(os.getenv("PORT", 8000))
uvicorn.run(app, host="0.0.0.0", port=port)
if __name__ == "__main__":
main()
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)