From 494a95d80c1c2cd4c466ce03cbdd27cdceadc73e Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Tue, 21 Apr 2026 12:42:00 +0200 Subject: [PATCH] fix(mcp-docugen): mount FastMCP a root + lifespan session_manager.run() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - streamable_http_app() espone /mcp internamente; mount a root evita doppio prefisso. - mcp.session_manager.run() integrato nel lifespan FastAPI: l'app mounted non riceve automaticamente il proprio lifespan -> la task group streamable-http non veniva inizializzata e le richieste MCP fallivano con RuntimeError. Smoke test end-to-end via gateway Caddy (porta 8090): - MCP initialize handshake OK - tools/list espone 6 tool corretti - template_create + template_list: round-trip con persistenza su volume - document_generate propaga LLMAuthError come MCP isError:true - Registrato in claude mcp list: ✓ Connected Co-Authored-By: Claude Opus 4.7 (1M context) --- services/mcp-docugen/src/mcp_docugen/main.py | 25 +++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/services/mcp-docugen/src/mcp_docugen/main.py b/services/mcp-docugen/src/mcp_docugen/main.py index 04de7e3..deff841 100644 --- a/services/mcp-docugen/src/mcp_docugen/main.py +++ b/services/mcp-docugen/src/mcp_docugen/main.py @@ -49,24 +49,27 @@ async def build_app(settings: Settings | None = None) -> FastAPI: ) mcp = build_mcp_server(template_store, renderer) + mcp_asgi = mcp.streamable_http_app() @asynccontextmanager async def lifespan(app: FastAPI): - cleanup_task = asyncio.create_task( - _periodic_cleanup(generation_store, interval_seconds=24 * 3600) - ) - try: - yield - finally: - cleanup_task.cancel() + async with mcp.session_manager.run(): + cleanup_task = asyncio.create_task( + _periodic_cleanup(generation_store, interval_seconds=24 * 3600) + ) try: - await cleanup_task - except asyncio.CancelledError: - pass + yield + finally: + cleanup_task.cancel() + try: + await cleanup_task + except asyncio.CancelledError: + pass app = build_http_app(template_store, generation_store) app.router.lifespan_context = lifespan - app.mount("/mcp", mcp.streamable_http_app()) + # streamable_http_app() espone /mcp internamente; monto a root. + app.mount("/", mcp_asgi) app.add_middleware( ApiKeyAuthMiddleware,