# coding=utf-8 """ @Time :2026/6/24 @Author :tao.chen """ from contextlib import asynccontextmanager from fastapi import FastAPI from common.factory import init_mcp_server from common.logging import logger from spark_executor import app as spark_executor_app @asynccontextmanager async def lifespan(app: "FastAPI"): logger.info(f"Startup {app.title}") spark_executor_mcp = init_mcp_server(spark_executor_app) spark_executor_mcp.mount_http(app, mount_path="/spark-executor-mcp") logger.info( f"MCP server mounted at /spark-executor-mcp " f"(source: {spark_executor_app.title} v{spark_executor_app.version})" ) # Enumerate every MCP tool + its description so operators can see at a # glance what the server exposes (useful for debugging / change review). tools = spark_executor_mcp.tools logger.info(f"MCP tools registered ({len(tools)}):") for i, tool in enumerate(tools, 1): desc = (tool.description or "").strip() # fastapi-mcp appends an auto-generated "### Responses: ..." block to # every tool description; drop it so the startup log stays scannable. if "### Responses" in desc: desc = desc.split("### Responses", 1)[0].rstrip() # Collapse internal newlines / extra spaces into one line. desc = " ".join(desc.split()) logger.info(f" {i:>2}. {tool.name} - {desc}") yield logger.info(f"Shutdown {app.title}") app = FastAPI(title="Main App", lifespan=lifespan) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)