Spaces:
Sleeping
Sleeping
| from mcp.server.fastmcp import FastMCP | |
| from starlette.middleware.cors import CORSMiddleware | |
| from starlette.requests import Request | |
| from app.core.config import settings | |
| from app.core.lifespan import app_lifespan | |
| from app.core.logger import logger | |
| from app.tools import register_markdown_tools | |
| from app.utils.response import create_response | |
| def _build_mcp_server() -> FastMCP: | |
| """Build the FastMCP instance and register every tool.""" | |
| mcp = FastMCP(settings.APP_NAME, lifespan=app_lifespan) | |
| register_markdown_tools(mcp) | |
| return mcp | |
| async def _health(_: Request): | |
| """Simple liveness probe.""" | |
| return create_response( | |
| status_value=True, | |
| message="Service is healthy", | |
| data={"app": settings.APP_NAME}, | |
| ) | |
| def create_app(): | |
| """Build the ASGI application. | |
| Mounts CORS and the health route directly on the MCP Starlette app to avoid | |
| nesting two Starlette instances (which produces a double http.response.start | |
| crash with uvicorn). | |
| """ | |
| logger.info("Building application: {}", settings.APP_NAME) | |
| mcp = _build_mcp_server() | |
| # sse_app() returns a Starlette instance — use it as the single ASGI app. | |
| app = mcp.sse_app() | |
| # Inject health route before the MCP catch-all mount. | |
| app.add_route("/health", _health, methods=["GET"]) | |
| # Add CORS once at the outermost middleware layer. | |
| app = CORSMiddleware( | |
| app, | |
| allow_origins=settings.CORS_ALLOW_ORIGINS, | |
| allow_methods=settings.CORS_ALLOW_METHODS, | |
| allow_headers=settings.CORS_ALLOW_HEADERS, | |
| ) | |
| return app | |