FastMCP 4 makes stateful MCP applications work on the sessionless 2026-07-28 protocol while one deployment continues serving handshake-era clients. Tools can ask follow-up questions across requests, preserve authenticated user state, and move long-running work into background tasks without sticky sessions or a continuously connected client.
The engine underneath changed completely. MCP Python SDK v2 rewrote the protocol layer end to end — protocol types moved into a standalone mcp_types package (still importable as mcp.types), model fields use snake_case in Python, and the server request-handling model was replaced. FastMCP absorbs nearly all of it, so most FastMCP 3 servers upgrade untouched.
🌐 Every protocol era — modern and handshake-era clients are served simultaneously, negotiated per connection. Modern requests can be handled by any replica behind an ordinary load balancer.
💬 Interactive tools — tools ask follow-up questions across complete request-response rounds. Shared request-state keys let any replica resume the next round after load balancing or a worker restart.
💾 State without a session — UserSession and SessionId give tools explicit per-user state on a protocol that deliberately has none (SEP-2567), stored server-side and keyed to the authenticated user.
⏳ Background tasks — the io.modelcontextprotocol/tasks extension (SEP-2663), shipped in the new fastmcp-tasks package on the same Docket engine FastMCP 3 used. @mcp.tool(task=True) is still the whole authoring surface.
🧩 Server extensions — add_extension() turns capability-negotiated protocol features (SEP-2133) into a supported plugin surface instead of surgery on core.
🔐 Enterprise auth — complete server-side identity assertion (SEP-990), plus require_roles, incremental-authorization step-up challenges (SEP-2350), DCR application_type (SEP-837), and routable transport headers for gateways (SEP-2243).
⌨️ Argument completion — a @mcp.completion handler answers autocomplete for prompt arguments and resource-template parameters, and can narrow suggestions using the arguments already supplied.
Breaking changes. Server-initiated sampling and roots are removed from the server API: both pushed a request down a live connection, which the sessionless protocol no longer has, and a method that only works against old clients is a trap. Elicitation continues in a request-shaped form, and generation belongs in your server — call an LLM directly. The 3.x-era compatibility shims are also gone. The upgrade guide walks through every change.
Install the beta by pinning it explicitly:
uv add "fastmcp==4.0.0b1"This is a beta released for testing ahead of 4.0. Pin an exact version and expect sharp edges.
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
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
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
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
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 🦾
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
Full Changelog: v3.4.5...v4.0.0b1