FastMCP 4 is stable. Five betas, five weeks, 23 contributors, and more than 80 pull requests later — the new protocol engine held up under real gateways, agent frameworks, and production servers, and most FastMCP 3 applications upgrade without code changes.
This is the FastMCP release for the new MCP. On July 28, MCP released the 2026-07-28 protocol revision and the rewritten Python SDK v2 shipped the same day. FastMCP 4 is built on both: modern requests are sessionless and self-contained, so any replica behind an ordinary load balancer can answer them, and one FastMCP 4 deployment negotiates the best protocol version per connection — new clients get the new protocol, old clients keep working, and Client(url) does the same negotiation from the other side.
The new protocol's capabilities come through FastMCP's usual high-level surfaces:
- Interactive tools return a request for input — approval, a missing field — and re-run with the client's answers available on the context.
- Background tasks (
@mcp.tool(task=True)) run outside the request path via theio.modelcontextprotocol/tasksextension, shipped in the optionalfastmcp-taskspackage on the same Docket engine as FastMCP 3. - Extensions register with
add_extension(): a negotiated capability, additive request methods, tool-call interception, and a lifespan. Tasks are built this way, outside core. - Argument completion for prompt and resource-template inputs, with already-supplied arguments visible to the handler.
- Auth for agents and services: identity assertion (SEP-990, beta), provider-neutral role checks, insufficient-scope challenges that name the missing scopes, and client-credentials auth.
- Infrastructure hooks: server-level cache hints, and
Mcp-Method/Mcp-Namerouting headers so gateways can route without parsing JSON-RPC.
The framework grew alongside the protocol: dependency injection can bind a dependency to arguments of the call it serves (Depends(get_account, user_id=CallArgument("owner"))) while keeping it out of the tool schema, and ClientGroup manages one client per server with collision-checked namespacing — each member negotiating its own protocol version.
The beta period motivated a bunch of correctness work. Most of it was auth: hardened OAuth consent flows, issuer validation, and JWT verification, plus proxies that strip cookies and connection-owned headers at trust boundaries. The rest was durability and compatibility — encrypted task snapshots, serialized event-store writes, response caching handling empty results, errors, and versioned components, and dozens of smaller fixes from CodeMode to Python 3.14 compat.
Breaking changes: server-initiated sampling and roots are removed (no live connection exists to call back into mid-request), ctx.elicit() is old-protocol-only, FastMCP 3's deprecated APIs are gone, MCP model fields are snake_case (with a warning compatibility bridge for the old names), and background tasks moved to fastmcp-tasks. Passing a bare string like Client("server.py") to run local code is deprecated in favor of Path, for removal in FastMCP 5.
The upgrade guide covers every change and includes a copyable prompt for auditing an application with a coding agent.
Happy (context) engineering!
What's Changed
New Features 🎉
- Migrate to MCP Python SDK v2 by @jlowin in #4437
- Teach fastmcp.Client the modern protocol: mode negotiation, MRTR driver, response cache by @jlowin in #4450
- Forward-port Hugging Face auth provider by @jlowin in #4475
- Add server-side identity assertion (SEP-990 ID-JAG) by @jlowin in #4483
- Add guard-mode multi-round-trip tools (SEP-2322) by @jlowin in #4544
- Add FastMCP-native server extension API (SEP-2133) by @jlowin in #4602
- Add stateless session state (UserSession / SessionId) by @jlowin in #4604
- Add background tasks via the io.modelcontextprotocol/tasks extension (SEP-2663) by @jlowin in #4603
Breaking Changes ⚠️
- Emit one SERVER span per request and adopt spec-correct error codes by @jlowin in #4445
- Remove 3.x deprecated module shims and dead parameters by @jlowin in #4447
- Remove 3.0-deprecated FastMCP server methods by @jlowin in #4451
- Remove 3.x deprecated parameters and object-mode decorators by @jlowin in #4453
- Migrate to MCP SDK v2.0.0b2 (httpx2) by @jlowin in #4503
- Fix typos by @szepeviktor in #4498
- Stop proxies from validating backend results or mutating shared transports by @jlowin in #4552
- Surface resource, prompt, and proxy errors on the modern protocol by @jlowin in #4579
- Negotiate the best mutual protocol era by default by @jlowin in #4572
- Remove server-initiated sampling and roots from the server API by @jlowin in #4648
- Remove 3.x-era compatibility shims by @jlowin in #4661
Enhancements ✨
- Deprecate ctx.sample and add clear errors for push features on 2026 connections by @jlowin in #4448
- Add server-level cache hints (SEP-2549) by @jlowin in #4464
- Add KeyValueResponseCacheStore for distributed client response caching by @jlowin in #4479
- Test lifespan fires once per process over HTTP by @jlowin in #4480
- Add telemetry off-switch and mcp.protocol.version span attribute by @jlowin in #4481
- Trace client task management requests by @jlowin in #4525
- Stabilize upgraded ty checks by @jlowin in #4526
- Improve DescopeProvider scope discovery and well-known URL support by @gaokevin1 in #4489
- Add examples/ to the ty static-analysis gate by @jlowin in #4466
- Expose telemetry attributes on span start by @zzstoatzz in #4487
- Fix-issue-4284 : Add Auth0MCPProvider for Auth0 Auth for MCP by @vijaydeepsinha in #4411
- Run FastMCP middleware for every inbound message by @jlowin in #4553
- Add 'prs welcome' label to waive the PR assignment gate by @jlowin in #4557
- Rename martian workflows to marvin by @jlowin in #4558
- Bump pinned Claude models to current versions by @jlowin in #4561
- Make the unit suite fast: in-process HTTP tests, no real sleeps, parallel Windows CI by @jlowin in #4554
- Mirror the frontend's protocol era on a proxy's backend connection by @jlowin in #4573
- Drop forked client protocol helpers in favor of the SDK's by @jlowin in #4574
- Bring the v4 developer notes up to date with what shipped by @jlowin in #4581
- Trim fastmcp.types to FastMCP-unique types by @jlowin in #4584
- Let a server answer argument-completion requests by @jlowin in #4582
- Add machine-to-machine client authentication by @jlowin in #4583
- Expose era-neutral client server metadata by @zzstoatzz in #4599
- Support routable transport headers for gateways (SEP-2243) by @jlowin in #4622
- Emit scope step-up challenges for incremental authorization (SEP-2350) by @jlowin in #4623
- Honor OAuth application_type in DCR (SEP-837) by @jlowin in #4621
- Drop stale label-noting instructions from CLAUDE.md by @jlowin in #4654
- Add require_roles auth check by @jlowin in #4656
- Add
valid_scopesparameter to OIDC proxy valid scopes by @Educg550 in #4660 - feat: Add telemetry interop mode for FastMCP by @strawgate in #4046
- Note that review comment threads should get an acknowledgement by @jlowin in #4678
- Soften the review-comment reply guidance by @jlowin in #4683
- Resolve review threads on fix, reply on decline by @jlowin in #4685
- Move to the stable MCP Python SDK 2.0.0 by @jlowin in #4655
- Add comprehensive Codex code review rules by @jlowin in #4710
- Give parallel Windows CI more timeout headroom by @zzstoatzz in #4680
- add language dropdown to docs sidebar by @znicholasbrown in #4716
- Route published docs through pull requests by @jlowin in #4713
- Improve HTTP server startup performance by @jlowin in #4729
- Avoid loading MCP and CLI stacks during lightweight imports by @jlowin in #4763
- Preserve legacy httpx compatibility without importing it by @jlowin in #4766
- Clarify review of closed contributor PRs by @jlowin in #4780
- Support EdDSA verification in JWTVerifier by @zzstoatzz in #4752
- Forward proxy server metadata across protocol eras by @jakekaplan in #4776
- feat(renovate): migrate to Renovate, retire Dependabot by @jamiezieziula in #4754
- Add Prefect Horizon authentication client and local state by @parkedwards in #4785
- Clarify auto-closed PR message by @jlowin in #4820
- Support CallArgument and Depends bindings from uncalled-for 0.4.0 by @chrisguidry in #4802
- Fix static analysis under newer ty releases by @zzstoatzz in #4831
- Cover CallArgument resolution in background tasks by @zzstoatzz in #4833
- Scalekit issuer updates backward compatibility by @AkshayParihar33 in #4798
- Add Prefect Horizon account commands by @parkedwards in #4786
- Cap anthropic dependency to <1 by @craigie-ant in #4864
- Remove obsolete Docket memory-server reset fixtures by @TyceHerrman in #4912
- add independent client groups by @zzstoatzz in #4904
- chore: add current maintainer alongside jlowin in hardcoded references by @zzstoatzz in #4953
Security 🔒
- Drive the FastMCP lifespan through the SDK session manager by @jlowin in #4446
- Route skill file access through SDK path-security primitives by @jlowin in #4449
- Screen templated resource parameters for path traversal by default by @jlowin in #4482
- [codex] Add OAuthProxy RFC 9207 issuer responses by @jlowin in #4438
- Apply app visibility where no host can by @jlowin in #4692
- Encrypt task context snapshots at rest by @chrisguidry in #4772
- Add audience pinning to GoogleTokenVerifier by @zzstoatzz in #4827
- Bump cryptography to 50.0.0 by @zzstoatzz in #4836
- Scope Marvin App token to each job's declared permissions by @zzstoatzz in #4834
- Exclude Cookie from forwarded HTTP headers by @zzstoatzz in #4843
- Bump cryptography to 50.0.0 in the testing_demo example by @zzstoatzz in #4844
- Bound CIMD cache growth by @jlowin in #4852
- client: deprecate inferring stdio transports from str, prefer Path by @zzstoatzz in #4941
Fixes 🐞
- Capture SharedContext for task-enabled Docket servers by @jlowin in #4443
- Fix stale mcp.types imports in examples by @jlowin in #4452
- Forward-port HTTP host guard compatibility by @jlowin in #4474
- Fix Azure scope fallback by @zzstoatzz in #4469
- fix(server): omit ScalarElicitationType wrapper title from elicitation schemas by @syf2211 in #4502
- Skip unsupported JWKS keys instead of failing the whole key set (#4515) by @earfman in #4517
- Don't mutate the caller's schema in compress_schema by @winklemad in #4492
- Forward upstream instructions through create_proxy by @verdie-g in #4512
- Serialize deep object query parameters by @jlowin in #4523
- Reject positional-only tool parameters by @jlowin in #4524
- Clarify PR-reopen flow and fix label-race that broke auto-reopen by @jlowin in #4518
- Clean up disconnected task sessions by @jlowin in #4519
- Handle expired OAuth client registrations by @jlowin in #4520
- Fix OAuth request annotation after httpx2 migration by @jlowin in #4534
- Fix docs banner contrast by @jlowin in #4522
- Preserve component metadata in response cache by @jlowin in #4521
- Clean up task sessions on connection exit by @jlowin in #4535
- Include scopes in auth challenges by @jlowin in #4527
- Make examples/ actually trigger the ty gate by @jlowin in #4541
- Add subject field to AccessToken initialization by @piaudonn in #4267
- Restore Mintlify's fixed banner positioning by @jlowin in #4542
- Fix #4292: SSRF guard breaks OAuth/JWKS fetches behind a corporate HTTP proxy by @endofcake in #4412
- Preserve telemetry attributes when a sampler does not forward them by @jlowin in #4539
- Speed up the unit test suite, and fix the task-notification race it surfaced by @jlowin in #4550
- Fix label triage applying no labels, and make blocked tool calls fail by @jlowin in #4555
- Fix AI workflow allowlists being destroyed by tokenization by @jlowin in #4560
- Make transformed tool
requiredorder deterministic by @Kludex in #4564 - Stop gather() from creating coroutines it may never schedule by @jlowin in #4559
- Restore upgraded dependency checks by @zzstoatzz in #4576
- Fix skill frontmatter parsing with UTF-8 BOM by @hxaxd in #4533
- Fix File helper extension handling by @VectorPeak in #4531
- Fix percent-encoded skill file names unreadable in resources mode by @jlowin in #4590
- Fix flaky stdio crash-recovery tests by @jlowin in #4594
- Bridge camelCase ToolAnnotations reads by @zzstoatzz in #4597
- Preserve raw CallToolResult tool returns by @LarryHu0217 in #4587
- Advertise only supported token endpoint auth methods in OAuthProxy metadata by @jlowin in #4608
- Fix OAuth proxy override typing by @zzstoatzz in #4612
- Pin burner-redis below the Windows-crashing 0.1.7 release by @jlowin in #4618
- fix : canonical mime type mapping from formats to remove inconsistency #4627 by @Aman071106 in #4628
- fix: accept callable roots handlers by @ShuyingZhang in #4639
- Pass the MCP conformance suite's draft and pending scenarios by @jlowin in #4650
- Use issuer_url for OAuth issuer identity by @jlowin in #4652
- Fix the ty failure blocking upgrade checks on main by @jlowin in #4657
- Bind CIMD assertion audience to the advertised token endpoint by @jlowin in #4659
- Record effective scopes on the OAuth transaction by @jlowin in #4670
- Copy schemas iteratively so deep nesting still compresses by @jlowin in #4671
- Fix OpenAPI allOf reference fields by @hxaxd in #4653
- Flatten OpenAPI discriminator subtypes into request bodies by @jlowin in #4677
- Let maintenance releases publish without fastmcp-tasks by @jlowin in #4676
- Read CLI-scanned MCP config files as UTF-8 explicitly by @jlowin in #4690
- Late-bind app tool names so UIs survive composition by @jlowin in #4682
- Always emit a tool title, derived from name when unset by @jlowin in #4694
- Fix self-referential connection error causes by @jakekaplan in #4720
- Write downloaded skill text as UTF-8 by @zzstoatzz in #4715
- Fix CodeMode tool error propagation by @YAO-001 in #4704
- Forward enable_cimd to Auth0, AWS Cognito and OCI providers by @StykMartin in #4719
- fix: preserve valid servers during CLI discovery by @ShuyingZhang in #4714
- Preserve string-compatible prompt arguments by @jlowin in #4730
- Canonicalize response cache arguments by @jlowin in #4753
- Serve empty list results from the response cache by @LHMQ878 in #4738
- Don't cache error results in ResponseCachingMiddleware by @SaiMouli3 in #4705
- Declare run-claude extra allowed tools input by @zzstoatzz in #4740
- Sanitize forwarded request metadata where the proxy copies it by @jakekaplan in #4770
- fix(context): move elicit overload docs inside the stubs so mypy sees the chain by @yonatangross in #4774
- Delegate typed tool output serialization to Pydantic by @jlowin in #4771
- Serialize the event store's stream list read-modify-write by @SaiMouli3 in #4758
- Fix partial parameter hints on Python 3.14 by @zzstoatzz in #4796
- fix(openapi): extract parameter-level example and examples by @doneman536 in #4793
- Keep earlier consent CSRF tokens valid within a transaction by @trevhud in #4818
- Fix StatefulProxyClient reconnection after session failure by @jlowin in #4829
- Fix instructions in MCP inspect output by @zzstoatzz in #4873
- fix: support Unicode tokens in BM25 search by @zzstoatzz in #4889
- fix: support modern protocol in multi-server clients by @zzstoatzz in #4893
- fix(ci): satisfy latest ty control-flow checks by @zzstoatzz in #4928
- fix(middleware): align response limits with output schemas by @zzstoatzz in #4931
- Accept any issuer scoped to the Descope project by @gaokevin1 in #4909
- Fix response cache partitioning for versioned components by @CoralGarden52 in #4948
Docs 📚
- Docs: forward-port v3.4.4 changelog entries by @jlowin in #4476
- Document icon theme support by @jlowin in #4537
- Add missing 4.0.0 version badge to Path Security docs by @jlowin in #4540
- Align server component docs by @strawgate in #4260
- Align CLI, deployment, and config docs by @strawgate in #4259
- Align client, Apps, and integration docs by @strawgate in #4261
- Fix stale MRTR/elicitation framing in client and upgrade docs by @jlowin in #4551
- docs: quote pip extras install examples by @RachGranville in #4568
- Document Windows CI parallelism and the subprocess_heavy marker by @jlowin in #4575
- Document v3->v4 removals and add upgrade-reality tests by @jlowin in #4585
- Archive v3 docs and publish v4 as the primary version by @jlowin in #4613
- Document targeted v4 prerelease installation by @zzstoatzz in #4598
- Fix stale Mac/Windows-vs-Linux OAuth key/storage docs by @jlowin in #4617
- v4 docs quality pass: stale task/era claims, broken links, polish by @jlowin in #4619
- whats-new: add the argument completion capability by @jlowin in #4620
- docs: fix ProxyProvider docstring example calling nonexistent with_namespace() by @andrew-stelmach-fleet in #4633
- Unpublish v4 development notes; prep docs for beta 1 by @jlowin in #4644
- Expand the FAQ for the v4 transition by @jlowin in #4649
- Document the issuer_url identity change for upgraders by @jlowin in #4658
- Cover require_roles in the v4 highlights by @jlowin in #4666
- Fix FAQ: sampling/roots/elicitation legacy-mode advice, SessionProvider registration by @jlowin in #4672
- Audit v4 docs: fix missing version badges, fill whats-new gaps by @jlowin in #4668
- Docs: add v3.4.5 changelog entries to main by @jlowin in #4674
- Split the SDK upgrade guides by SDK version by @jlowin in #4684
- Add v4.0.0b1 changelog and updates entries by @jlowin in #4681
- Use mcp.types directly instead of aliasing to mcp_types in docs by @jlowin in #4696
- Rewrite the v4 What's New page and document server extensions by @jlowin in #4698
- Improve the v4 docs by @jlowin in #4707
- Rewrite the FastMCP docs welcome page by @jlowin in #4709
- docs: clarify external OAuth consent mode by @jlowin in #4746
- Docs: mirror v3.4.6 changelog entries by @jlowin in #4764
- Document MCP protocol support and conformance by @jlowin in #4781
- Docs language dropdown by @znicholasbrown in #4801
- Docs: mirror v3.4.7 release notes by @jlowin in #4811
- docs: prepare FastMCP 4 beta 3 by @jlowin in #4840
- docs: add FastMCP 4 beta 3 release entries by @jlowin in #4841
- docs: fix idempotent tool hint wording by @Theater-ahyeon in #4875
- docs: prepare FastMCP 4 beta 4 by @zzstoatzz in #4914
- docs: fix beta 4 changelog MDX by @zzstoatzz in #4916
- docs: prepare FastMCP 4 beta 5 by @zzstoatzz in #4934
- docs: cover list_tools and restore client modules in the SDK reference by @zzstoatzz in #4936
- docs: transport inference trust contract; trim SDK session-group comparison by @zzstoatzz in #4940
- docs: add v4.0.0 changelog and updates entries by @zzstoatzz in #4955
Dependencies 📦
- chore(deps): bump mcp from 1.26.0 to 1.27.2 in /examples/testing_demo in the uv group across 1 directory by @dependabot[bot] in #4514
- chore(deps): bump actions/setup-node from 6 to 7 by @dependabot[bot] in #4546
- Bump actions/upload-artifact from 4 to 7 by @dependabot[bot] in #4640
- Bump actions/setup-python from 6 to 7 by @dependabot[bot] in #4641
- chore(deps): bump mcp from 1.27.2 to 1.28.1 in /examples/testing_demo in the uv group across 1 directory by @dependabot[bot] in #4614
Other Changes 🦾
- Test: HTTP lifespan fires once per process across sessions by @jlowin in #4470
- Fix static analysis with latest ty by @zzstoatzz in #4739
- Quote run-claude allowed tools argument by @zzstoatzz in #4741
- chore(deps): Update j178/prek-action action to v3 by @prefect-renovate[bot] in #4808
- chore(deps): Update dependency node to v24 by @prefect-renovate[bot] in #4807
- chore(deps): Update astral-sh/setup-uv action to v9 by @prefect-renovate[bot] in #4806
- Fix proxy forwarding of MCP transport headers by @jakekaplan in #4853
- Add FastMCP security report review skill by @zzstoatzz in #4859
New Contributors
- @syf2211 made their first contribution in #4502
- @earfman made their first contribution in #4517
- @winklemad made their first contribution in #4492
- @verdie-g made their first contribution in #4512
- @vijaydeepsinha made their first contribution in #4411
- @piaudonn made their first contribution in #4267
- @szepeviktor made their first contribution in #4498
- @endofcake made their first contribution in #4412
- @Kludex made their first contribution in #4564
- @RachGranville made their first contribution in #4568
- @hxaxd made their first contribution in #4533
- @VectorPeak made their first contribution in #4531
- @LarryHu0217 made their first contribution in #4587
- @andrew-stelmach-fleet made their first contribution in #4633
- @Aman071106 made their first contribution in #4628
- @ShuyingZhang made their first contribution in #4639
- @Educg550 made their first contribution in #4660
- @znicholasbrown made their first contribution in #4716
- @YAO-001 made their first contribution in #4704
- @StykMartin made their first contribution in #4719
- @LHMQ878 made their first contribution in #4738
- @SaiMouli3 made their first contribution in #4705
- @yonatangross made their first contribution in #4774
- @jamiezieziula made their first contribution in #4754
- @parkedwards made their first contribution in #4785
- @trevhud made their first contribution in #4818
- @prefect-renovate[bot] made their first contribution in #4808
- @craigie-ant made their first contribution in #4864
- @Theater-ahyeon made their first contribution in #4875
- @TyceHerrman made their first contribution in #4912
- @CoralGarden52 made their first contribution in #4948
Full Changelog: v3.4.7...v4.0.0