Spaces:
Sleeping
Sleeping
File size: 4,386 Bytes
7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 612b769 7094811 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | """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",
) |