A server in production has to answer three questions: is it up, what is it doing, and when something breaks, why. This tutorial adds the three observability pillars to a FastMCP server — structured logs, a health check, and metrics — using only the standard library plus two FastMCP primitives (middleware and custom routes). The stack is Mac-native: uv, make, and pytest.
The design keeps instrumentation out of the tools:
Logging is a JSON formatter on the root logger, written to stderr. Metrics are a small in-process registry fed by one piece of middleware, so every tool is measured without touching its code. Health and metrics are exposed twice: as MCP resources (readable over any transport, including stdio) and as HTTP routes (/health, /metrics) for load balancers and Prometheus. The stdio trap. The stdio transport uses stdout for the MCP protocol itself. Anything else written to stdout — a stray print, a log line — corrupts the stream and breaks the client. All logging here goes to stderr, which is safe on both transports. This is the single most common way a working server mysteriously fails once a client connects to it over stdio.
...