testdays-web/asgi.py
Jaroslav Groman 80f4c961df
All checks were successful
Run tests / test (pull_request) Successful in 31s
Implement MCP server
- add simple read-only MCP server for reading testdays and test results
- add MCP server docs
- add example testdays-mcp skill
- extend tests to cover new features

Fixes #108

Assisted-by: Claude Code
2026-06-02 11:40:24 +00:00

54 lines
2 KiB
Python

"""ASGI entry point for the Fedora Testdays application.
Serves the Flask WSGI app alongside the MCP server under a single
Starlette router. Gunicorn with UvicornWorker uses this as the
application target::
gunicorn asgi:application -k uvicorn.workers.UvicornWorker
"""
import logging
from asgiref.wsgi import WsgiToAsgi
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from starlette.routing import Mount
from mcp_server import mcp
from testdays import create_app
LOGGER = logging.getLogger(__name__)
class _McpAccessLogMiddleware(BaseHTTPMiddleware):
"""Log /mcp requests in the same format as Flask's @after_request hook."""
async def dispatch(self, request: Request, call_next): # type: ignore[override]
response: Response = await call_next(request)
if request.url.path.startswith("/mcp"):
LOGGER.info(
'%s - "%s %s HTTP/%s" %s',
request.client.host if request.client else "-",
request.method,
request.url.path,
request.scope.get("http_version", "1.1"),
response.status_code,
)
return response
# Build the top-level ASGI app directly from the MCP instance.
# streamable_http_app() creates a Starlette application with the MCP
# handler at "/mcp" (the default but also set explicitly in
# mcp_server.py) We then mount the WSGI-converted Flask app at /.
# Since this happens after the "/mcp" route is set up, it will route
# everything except /mcp to the Flask app.
# The MCP instance creates its own Flask app instance (cached per-process)
# separate from the WSGI app below. They share the same config and database.
#
# Pattern adapted from: github.com/modelcontextprotocol/python-sdk/issues/1367
flask_app = create_app()
application = mcp.streamable_http_app()
application.router.routes.append(Mount("/", app=WsgiToAsgi(flask_app)))
application.add_middleware(_McpAccessLogMiddleware)