init
This commit is contained in:
102
main.py
Normal file
102
main.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
main.py
|
||||
Application entrypoint.
|
||||
Starts the FastAPI HTTP server (web UI + REST API) and mounts the FastMCP
|
||||
streamable-HTTP endpoint under /mcp.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from app.api import routes_config, routes_instructions, routes_status
|
||||
from app.config import settings
|
||||
from app.database import init_db
|
||||
from app.logging_setup import configure_logging
|
||||
from app.mcp_server import mcp, mcp_asgi_app
|
||||
from app.services import instruction_service
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Logging must be configured before any module emits log messages
|
||||
# ---------------------------------------------------------------------------
|
||||
configure_logging(settings)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build the FastAPI application
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Startup
|
||||
logger.info("local-mcp starting up – initialising database …")
|
||||
init_db(settings.db_path)
|
||||
await instruction_service.init_wakeup()
|
||||
logger.info(
|
||||
"local-mcp ready http://%s:%d | MCP http://%s:%d/mcp (stateless=%s)",
|
||||
settings.host, settings.http_port,
|
||||
settings.host, settings.http_port,
|
||||
settings.mcp_stateless,
|
||||
)
|
||||
# Run the MCP session manager for the duration of the app lifetime
|
||||
async with mcp.session_manager.run():
|
||||
yield
|
||||
# Shutdown
|
||||
logger.info("local-mcp shutting down")
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
app = FastAPI(
|
||||
title="local-mcp",
|
||||
description="Localhost MCP server with instruction queue management UI",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# --- Global exception handler ---
|
||||
@app.exception_handler(Exception)
|
||||
async def global_exception_handler(request: Request, exc: Exception):
|
||||
logger.exception("Unhandled exception for %s %s", request.method, request.url)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"detail": "Internal server error", "error": str(exc)},
|
||||
)
|
||||
|
||||
# --- API routers ---
|
||||
app.include_router(routes_status.router)
|
||||
app.include_router(routes_instructions.router)
|
||||
app.include_router(routes_config.router)
|
||||
|
||||
# --- MCP streamable-HTTP transport mounted at /mcp ---
|
||||
# mcp_asgi_app was pre-built in mcp_server.py; session manager is
|
||||
# started explicitly in the lifespan above.
|
||||
app.mount("/mcp", mcp_asgi_app)
|
||||
|
||||
# --- Static files for the web UI ---
|
||||
app.mount("/static", StaticFiles(directory="static", html=False), name="static")
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
def serve_index():
|
||||
return FileResponse("static/index.html")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(
|
||||
"main:app",
|
||||
host=settings.host,
|
||||
port=settings.http_port,
|
||||
log_level=settings.log_level.lower(),
|
||||
reload=False,
|
||||
)
|
||||
Reference in New Issue
Block a user