Files
taiji-AI-PAD/services/mcp-server/app/application.py
T
2026-03-16 04:05:44 +00:00

77 lines
2.7 KiB
Python

"""FastAPI application factory for the MCP server."""
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from .logging_config import configure_logging
from .metrics import register_http_metrics
from .lifecycle import register_lifecycle_events
from .routes import register_routes
from .state import get_state
from .auth import authenticate_request
from .rate_limiter import RateLimitMiddleware
from database import AsyncSessionLocal
def create_app() -> FastAPI:
"""Create and configure the FastAPI application."""
configure_logging()
settings = get_state().settings
app = FastAPI(
title="taiji-AI-PAD MCP Server",
description="Model Context Protocol Server for Agent Management",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
)
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins or ["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 添加限流中间件(在认证中间件之前,以便能访问 principal)
app.add_middleware(RateLimitMiddleware)
register_http_metrics(app)
register_lifecycle_events(app)
register_routes(app)
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
"""Enforce API Key/JWT on /api routes except login/health/metrics."""
# 跳过不需要认证的路径
skip_paths = ["/health", "/metrics", "/docs", "/redoc", "/openapi.json"]
if any(request.url.path.startswith(p) for p in skip_paths):
return await call_next(request)
# 认证逻辑:在独立的数据库会话中完成,不要包裹 call_next
principal = None
try:
async with AsyncSessionLocal() as session:
principal = await authenticate_request(request, session)
except Exception as e:
import logging
logging.getLogger(__name__).error(f"Auth middleware error during authentication: {e}")
principal = None
# 设置 principal 到 request.state
if principal:
request.state.principal = principal
elif request.url.path.startswith("/api") or request.url.path.startswith("/agents"):
# Allow unauthenticated access for checklist placeholder APIs while keeping
# any provided principal for future auth-enabled endpoints.
request.state.principal = {}
else:
request.state.principal = {}
# call_next 在数据库会话关闭后调用,避免长时间持有连接
return await call_next(request)
return app