Production log noise: every container start was emitting 14 INFO lines
listing every tool name and full description, which is dev-time
debugging info that doesn't belong in INFO.
Now:
- INFO: one line per service with the count
'MCP service \042spark_executor\042: 14 tool(s) registered'
- DEBUG: per-tool name + description, prefixed with the service name
so multi-service deploys are grep-friendly
' spark_executor._prepare_submit_job_prepare_submit_job_post - ...'
Operators can flip SPARK_EXECUTOR_LOG_LEVEL=INFO for production (no
per-tool spam) or DEBUG for dev (full inventory).
146/146 still pass. Live verified: at INFO level only the count line
shows; at DEBUG level both the count and per-tool lines show.
The lifespan was hardcoded to mount just one MCP service
(spark_executor_app at /spark-executor-mcp). To add a second MCP
service, you'd have to edit the lifespan function body — error-prone
and not obvious where to add a new entry.
Refactor:
- New McpService dataclass (name, app, mount_path)
- MCP_SERVICES list at module top — adding a new service is one append
- Lifespan iterates MCP_SERVICES; the per-service mount + tool-list
logic is now a helper (_log_mcp_tools)
- Tool-list log now prefixes the service name:
'MCP tools registered (spark_executor, 14):'
instead of just '(14):', so multi-service startups are unambiguous
116/116 still pass. Live verified: gunicorn 2-worker startup logs
each service with its tool list and the source app version.
Future services just need a McpService entry — no lifespan edit. The
MCP_SERVICES list has a docstring example showing the one-liner.
main.py lifespan now enumerates every MCP tool with its description at
startup so operators can see at a glance what the server exposes.
The auto-generated '### Responses:' suffix that fastapi-mcp appends to
each tool description is truncated before logging to keep the startup
log scannable.
Docstrings added to all 12 route handlers in server.py so the MCP tool
descriptions are meaningful (without them, fastapi-mcp would emit empty
descriptions). Each route now carries a summary + description used by
the auto-generated OpenAPI schema.