Four read-only, service-token-protected MCP provider endpoints (operations summary, attention vehicles, vehicle details, knowledge search facade). Shared-secret auth reusing the M4 callback pattern. Service-request audit trail for every call. Extracted shared operations-summary logic out of the dashboard router to avoid duplicating retrieval logic. 66 backend tests passing, ruff clean. Verified all four endpoints and audit trail directly via curl against the live stack (no live MCP Hub instance available in this environment).
91 lines
2.4 KiB
Python
91 lines
2.4 KiB
Python
import uuid
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI, HTTPException, Request
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from app.api.routers import (
|
|
audit,
|
|
bookings,
|
|
dashboard,
|
|
data_quality,
|
|
demo,
|
|
integrations,
|
|
knowledge,
|
|
mcp_integrations,
|
|
vehicles,
|
|
workflows,
|
|
)
|
|
from app.core.config import get_settings
|
|
from app.core.errors import AppError, error_body
|
|
from app.services.dispatcher import start_background_dispatcher, stop_background_dispatcher
|
|
|
|
settings = get_settings()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(_app: FastAPI):
|
|
start_background_dispatcher()
|
|
yield
|
|
stop_background_dispatcher()
|
|
|
|
|
|
app = FastAPI(title="MobilityOps API", version="0.1.0", lifespan=lifespan)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[o.strip() for o in settings.cors_allow_origins.split(",")],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
@app.exception_handler(AppError)
|
|
def handle_app_error(_request: Request, exc: AppError) -> JSONResponse:
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content=error_body(exc.code, exc.message, exc.correlation_id, exc.details),
|
|
)
|
|
|
|
|
|
@app.exception_handler(HTTPException)
|
|
def handle_http_exception(_request: Request, exc: HTTPException) -> JSONResponse:
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content=error_body(
|
|
code=str(exc.status_code),
|
|
message=str(exc.detail),
|
|
correlation_id=str(uuid.uuid4()),
|
|
details={},
|
|
),
|
|
)
|
|
|
|
|
|
@app.get("/health")
|
|
def health() -> dict[str, str]:
|
|
return {"status": "ok", "service": "mobilityops-api"}
|
|
|
|
|
|
@app.get("/api/v1/system/status")
|
|
def system_status() -> dict[str, object]:
|
|
return {
|
|
"service": "mobilityops-api",
|
|
"environment": settings.mobilityops_env,
|
|
"demo_mode": settings.mobilityops_demo_mode,
|
|
"knowledge_provider": settings.knowledge_provider,
|
|
}
|
|
|
|
|
|
app.include_router(demo.router)
|
|
app.include_router(dashboard.router)
|
|
app.include_router(vehicles.router)
|
|
app.include_router(bookings.router)
|
|
app.include_router(audit.router)
|
|
app.include_router(data_quality.router)
|
|
app.include_router(workflows.router)
|
|
app.include_router(integrations.router)
|
|
app.include_router(knowledge.router)
|
|
app.include_router(mcp_integrations.router)
|