Skip to content

feat: record FastMCP HTTP request and tool call metrics - #281

Merged
lesnik512 merged 1 commit into
mainfrom
feat/fastmcp-prometheus-metrics
Oct 9, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
feat/fastmcp-prometheus-metrics

Conversation

@lesnik512

@lesnik512 lesnik512 commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Builds on #280's _postprocess_http_apps hook (merged).

Why

FastMcpPrometheusInstrument only serves the registry, so a FastMCP service's /metrics has nothing but process_* and python_*. FastAPI, Litestar and FastStream all record request metrics. Ported from community-of-python/microbootstrap#155.

Changes

  • HTTP requests. Every ASGI application http_app() builds is instrumented with prometheus-fastapi-instrumentator, the library the FastAPI bootstrapper already uses (it needs only Starlette). Requests are labelled by route template (handler="/mcp", handler="/health/"), unknown paths are grouped into handler="none", and the metrics route is excluded by default. prometheus_instrumentator_params and prometheus_instrument_params are passed through under the same names as in FastAPIConfig.
  • Tool calls. FastMcpPrometheusMiddleware records fastmcp_tool_calls_total{tool, status} (success / error) and fastmcp_tool_call_duration_seconds{tool}. It is on by default, and fastmcp_prometheus_tool_metrics_enabled=False turns it off.
  • The fastmcp-metrics extra adds prometheus-fastapi-instrumentator>=8.0.1, the same floor as fastapi-metrics. The instrument now needs both packages; with either missing it is skipped with the usual dependency warning.
  • Docs: a "Prometheus FastMCP" configuration section and a metrics section in the FastMCP guide.

Registry notes

  • When http_requests_total and its siblings are already registered, metrics.default() does not reuse them. It catches the duplicate and returns None, so a second instrumented application records nothing. The instrument therefore builds the default metrics once, on the first http_app(), and .add()s that closure to every later application. Its parameters are the subset of instrumentator and instrument params that metrics.default accepts.
  • Across bootstrappers in one process, the global-registry limit is the same one FastAPI already has. The test module drops the instrumentator's collectors before each test for this reason.
  • The tool-call collectors reuse already-registered collectors by name, so a later bootstrap in the same process (or a module reload in tests) does not raise DuplicateTimeseries.

Tests

  • Requests are counted by route template, none is counted for unmatched paths, and the metrics route is not counted.
  • Two http_app() calls are both counted. This test fails without the shared default metrics.
  • prometheus_instrumentator_params reach the instrumentator.
  • Successful and failing tool calls and their durations are counted, and tool metrics can be turned off.
  • Teardown restores http_app. The instrument is skipped without the instrumentator.
  • just lint, ty 0.0.85 and just test-ci pass on top of feat!: trace Litestar through its own OpenTelemetryPlugin #279 and feat: trace FastMCP HTTP requests with OpenTelemetry #280 (358 passed, 100% coverage).
  • Floor smoke passes (fastmcp==3.0.0, prometheus-client==0.20.0, prometheus-fastapi-instrumentator==8.0.1, Python 3.11). At those floors, two http_app() applications each serving the health route and a 404 count 2 and 2, and a tool call counts 1.

@lesnik512
lesnik512 force-pushed the feat/fastmcp-prometheus-metrics branch from 9a557a3 to 2fe7c2b Compare October 9, 2026 19:18
Base automatically changed from feat/fastmcp-opentelemetry to main October 9, 2026 19:21
FastMcpPrometheusInstrument runs prometheus-fastapi-instrumentator on every ASGI
application http_app() builds, labelled by route and excluding the metrics route,
and a FastMCP middleware counts and times tool calls in fastmcp_tool_calls_total
and fastmcp_tool_call_duration_seconds. The fastmcp-metrics extra now requires
prometheus-fastapi-instrumentator.
@lesnik512
lesnik512 force-pushed the feat/fastmcp-prometheus-metrics branch from 2fe7c2b to 1eb1163 Compare October 9, 2026 19:21
@lesnik512
lesnik512 merged commit 41db5bd into main Oct 9, 2026
30 checks passed
@lesnik512
lesnik512 deleted the feat/fastmcp-prometheus-metrics branch October 9, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant