Skip to content

feat: add FastMcpBootstrapper#105

Merged
lesnik512 merged 19 commits into
mainfrom
feat/fastmcp-bootstrapper
Jun 1, 2026
Merged

feat: add FastMcpBootstrapper#105
lesnik512 merged 19 commits into
mainfrom
feat/fastmcp-bootstrapper

Conversation

@lesnik512
Copy link
Copy Markdown
Member

@lesnik512 lesnik512 commented Jun 1, 2026

Summary

  • Add FastMcpBootstrapper and FastMcpConfig that wire the lite-bootstrap instrument stack (Sentry, Pyroscope, structlog logging with an MCP protocol-level access-log middleware, JSON health endpoint, Prometheus metrics endpoint) onto a user-supplied FastMCP instance.
  • Mirrors the instrument set merged upstream in microbootstrap PR #141 while following the project's framework-bootstrapper conventions: frozen-dataclass config composed via multiple inheritance, optional-import guards via import_checker, instruments_types ClassVar registry, and single-file bootstrapper module mirroring faststream_bootstrapper.py.
  • Adds three new pyproject extras: fastmcp (depends on fastmcp), fastmcp-metrics (adds prometheus-client>=0.20), and fastmcp-all rollup (parity with fastapi-all / litestar-all / faststream-all).
  • Teardown is wired automatically via FastMCP's Provider.lifespan hook. The bootstrapper registers an internal _TeardownProvider from its __init__, and the provider's lifespan async-cm runs bootstrapper.teardown() on ASGI shutdown. FastMCP.lifespan itself is a read-only bound method (the real hook is _lifespan, captured at constructor time only), so add_provider is the only public post-construction lifecycle hook — that's what we use. Users don't need to call teardown() themselves.

Spec: docs/superpowers/specs/2026-06-01-fastmcp-bootstrapper-design.md
Plan: docs/superpowers/plans/2026-06-01-fastmcp-bootstrapper.md

Test plan

  • just test — full suite passes (19 new fastmcp tests + 129 existing)
  • just lint — clean (ruff, eof-fixer, ty)
  • Coverage on lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py is 100% via 19 dedicated tests (health route, prometheus route, MCP middleware logging, teardown via direct call and via ASGI lifespan, missing-dependency regression for sentry/structlog/prometheus_client, not-ready when fastmcp is absent)
  • Manual: install lite-bootstrap[fastmcp-all] in a scratch project, boot a FastMCP app, hit /health/ and /metrics, shut down via ASGI lifespan and confirm teardown runs
  • Manual: install without prometheus-client / without structlog; confirm UserWarning + clean boot

🤖 Generated with Claude Code

lesnik512 and others added 14 commits June 1, 2026 22:05
Design captured for replacing InstrumentNotReadyWarning with a pre-
instantiation `is_configured` classmethod check + structured
skipped_instruments introspection + single INFO summary log.

The current post-PR-#86 behavior fires a warning every time an
instrument's is_ready() returns False — typically a user-config
opt-out, not an anomaly. Every service that uses a subset of available
instruments emits multiple warnings on bootstrap; tests can't suppress
without breaking the warning assertions. The redesign moves the
config check before check_dependencies, makes it a classmethod
(preserves PR #88's no-instantiation-on-missing-dep constraint), and
removes the warning entirely. dep-missing still warns (genuine
deployment surprise).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The original design aimed to wrap FastMCP.lifespan with combine_lifespans
to wire teardown automatically. Implementation surfaced that
FastMCP.lifespan is a read-only bound method on AggregateProvider and the
runtime hook is the private _lifespan attribute (set at constructor time
only). The spec's documented fallback — "teardown is manual" — is now the
adopted approach.

Updates:
- Spec: §"Teardown wiring risk" → §"Teardown is manual"; tests list
  drops ASGI-lifespan and user-lifespan-preserve tests, adds
  test_fastmcp_teardown_resets_is_bootstrapped.
- Plan: Task 4 module skeleton no longer mutates lifespan; Task 5
  reduced to single teardown-reset assertion; integrations page
  documents how users wire teardown themselves.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eadyError

Final-review followup: assert on the project's typed exception rather
than bare RuntimeError so a future change to the base class hierarchy
doesn't silently weaken the test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
lite_bootstrap/__init__.py 100.00% <100.00%> (ø)
...te_bootstrap/bootstrappers/fastmcp_bootstrapper.py 100.00% <100.00%> (ø)
lite_bootstrap/import_checker.py 100.00% <100.00%> (ø)
tests/test_fastmcp_bootstrap.py 100.00% <100.00%> (ø)
tests/test_faststream_bootstrap.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

lesnik512 and others added 3 commits June 2, 2026 00:03
Earlier work concluded teardown had to be manual because FastMCP.lifespan
is a read-only bound method. Re-investigation surfaced FastMCP.add_provider,
which DOES accept post-construction providers and invokes each provider's
lifespan async context manager during ASGI startup/shutdown.

A small _TeardownProvider(Provider) wraps bootstrapper.teardown() and is
registered automatically from FastMcpBootstrapper.__init__. Users no
longer need to call teardown() manually — it runs when the ASGI
application that serves application.http_app() shuts down.

Restores test_fastmcp_teardown_runs_via_asgi_lifespan with a hand-rolled
ASGI lifespan driver. Drops the "manual teardown" section from the
integrations doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Matches the fastapi-all / litestar-all / faststream-all naming pattern.
fastmcp-all = [lite-bootstrap[fastmcp,fastmcp-metrics,sentry,logging,pyroscope]],
so users can install the whole stack with a single extra rather than
composing five extras by hand.

Updates docs/introduction/installation.md to list `fastmcp-all` in the
"all" row and drops the now-obsolete "no rollup" note above the table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A recent faststream release tightened AsgiFastStream's typed signature
to BrokerUsecase (no None). The wo_broker test path passed broker=None
positionally, which started failing CI with both a ty error and a
runtime AttributeError on _update_fd_config.

build_faststream_config now omits the broker argument entirely when no
broker is provided. test_faststream_bootstrap_health_check_wo_broker
keeps its semantics (verifying the brokerless health-check path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 self-assigned this Jun 1, 2026
lesnik512 and others added 2 commits June 2, 2026 00:06
…ngInstrument

LoggingInstrument.check_dependencies (the parent) already returns
is_structlog_installed, and BaseBootstrapper._register_or_skip filters
the instrument out before instantiation when it returns False. Reaching
FastMcpLoggingInstrument.bootstrap() therefore implies structlog is
installed; the inner guard is unreachable defensive code.

Surfaced by codecov flagging it as uncovered on PR #105.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… _TeardownProvider

Three of the spec/plan sections still described the short-lived
"manual teardown" fallback that was reverted in commit b786781.
Specifically:

- spec §"Teardown is manual" → §"Teardown via Provider.lifespan"
- spec Tests list: restore test_fastmcp_teardown_runs_via_asgi_lifespan
- plan locked-decisions: replace "Teardown: manual" with the
  add_provider wiring; mention fastmcp-all rollup
- plan Task 5: explain the direct + lifespan test pair

Also adds a one-line comment to _TeardownProvider explaining that
FastMCP exposes no on_shutdown-style API and Provider.lifespan is the
only public post-construction hook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 merged commit 2479127 into main Jun 1, 2026
8 checks passed
@lesnik512 lesnik512 deleted the feat/fastmcp-bootstrapper branch June 1, 2026 21:28
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