fix(mcp-docugen): mount FastMCP a root + lifespan session_manager.run()
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user