codeMcp / app /main.py
sarveshpatel's picture
Update app/main.py
612b769 verified
"""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 ─────────────────────────────────────
@asynccontextmanager
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 ────────────────────────────────────
@app.middleware("http")
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 ─────────────────────────────────
@app.exception_handler(Exception)
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",
)