Spaces:
Sleeping
Sleeping
| """FastAPI application entry point - MCP Code Executor.""" | |
| from __future__ import annotations | |
| import logging | |
| import sys | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI, Request | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from app.config import get_settings | |
| from app.executor import get_executor | |
| from app.routes import health, mcp, tools | |
| # βββ Logging Configuration ββββββββββββββββββββββββββββββββββββ | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", | |
| datefmt="%Y-%m-%d %H:%M:%S", | |
| stream=sys.stdout, | |
| ) | |
| logger = logging.getLogger("mcp_code_executor") | |
| # βββ Application Lifespan βββββββββββββββββββββββββββββββββββββ | |
| async def lifespan(app: FastAPI): | |
| """Startup and shutdown logic.""" | |
| settings = get_settings() | |
| logger.info("=" * 60) | |
| logger.info("MCP Code Executor starting up") | |
| logger.info(" Environment: %s", settings.env_type.value) | |
| logger.info(" Code storage: %s", settings.code_storage_dir) | |
| logger.info(" Max concurrent: %d", settings.max_concurrent_executions) | |
| logger.info(" Execution timeout: %ds", settings.execution_timeout) | |
| logger.info(" Python executable: %s", settings.get_python_executable()) | |
| logger.info(" MCP endpoint: /mcp/jsonrpc") | |
| logger.info("=" * 60) | |
| yield | |
| # Shutdown: cleanup active processes | |
| logger.info("Shutting down - cleaning up active processes...") | |
| executor = get_executor() | |
| await executor.cleanup() | |
| logger.info("Shutdown complete") | |
| # βββ FastAPI Application ββββββββββββββββββββββββββββββββββββββ | |
| app = FastAPI( | |
| title="MCP Code Executor", | |
| description=( | |
| "An MCP server that allows LLMs to execute Python code within a " | |
| "specified Python environment. Supports the full MCP 2025-03-26 " | |
| "Streamable HTTP transport protocol, incremental code generation, " | |
| "package management, and concurrent execution." | |
| ), | |
| version="1.0.0", | |
| lifespan=lifespan, | |
| docs_url="/docs", | |
| redoc_url="/redoc", | |
| openapi_url="/openapi.json", | |
| ) | |
| # βββ CORS Middleware βββββββββββββββββββββββββββββββββββββββββββ | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| expose_headers=["*"], | |
| ) | |
| # βββ MCP Headers Middleware ββββββββββββββββββββββββββββββββββββ | |
| async def add_mcp_headers(request: Request, call_next): | |
| """Add MCP-required headers to responses on MCP endpoints.""" | |
| response = await call_next(request) | |
| if request.url.path.startswith("/mcp/"): | |
| # MCP Streamable HTTP requires these headers | |
| response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" | |
| response.headers["Connection"] = "keep-alive" | |
| return response | |
| # βββ Global Exception Handler βββββββββββββββββββββββββββββββββ | |
| async def global_exception_handler(request: Request, exc: Exception): | |
| logger.exception("Unhandled exception on %s %s", request.method, request.url.path) | |
| return JSONResponse( | |
| status_code=500, | |
| content={ | |
| "error": "Internal server error", | |
| "detail": str(exc), | |
| }, | |
| ) | |
| # βββ Register Routes ββββββββββββββββββββββββββββββββββββββββββ | |
| app.include_router(health.router) | |
| app.include_router(mcp.router) | |
| app.include_router(tools.router) | |
| # βββ Direct Run (development) βββββββββββββββββββββββββββββββββ | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| "app.main:app", | |
| host="0.0.0.0", | |
| port=7860, | |
| reload=True, | |
| log_level="info", | |
| ) |