FastMCP 4 is stable and generally available.

pip install fastmcp -U

Earlier this summer, MCP released the 2026-07-28 revision of the protocol, one of the more exciting protocol updates recently for MCP enjoyers (and haters!).

FastMCP 4 now supports this updated protocol and clients on older protocol versions continue to work against upgraded servers.

Most FastMCP 3 applications upgrade without code changes.

The new protocol

There are two headlines for MCP server authors and operators:

First, requests are now independent.

The pre-07-28 protocol was state-ful in that it kept a session per client — an initialize handshake, an Mcp-Session-Id header, and server-side connection state — so load balancers had to route each client to the same replica. The new revision removes protocol-level sessions: each request carries its protocol version and capabilities, and any replica can answer it.

If your application does need state across requests, FastMCP provides UserSession (keyed by the authenticated user) and SessionId (an explicit session argument on your tools), backed by storage you configure.

Second, the protocol added capabilities that previously had no sanctioned home:

  • interactive tools: A tool can return a request for more input — approval, a missing field — instead of a final answer. The client collects the answer and calls the tool again; the tool re-runs from the top with the responses available on the context. Each round is a complete request-response, so nothing is held open in between.

  • background tasks: The tasks extension (io.modelcontextprotocol/tasks) moves long-running work out of the request path. In FastMCP that's @mcp.tool(task=True) plus registering the extension, shipped in the optional fastmcp-tasks package on the same Docket engine as FastMCP 3. FastMCP clients poll task status automatically, so a background tool is called like any other tool (or use call_tool_task() to get the task handle and manage it yourself)

  • extensions: The protocol now has a first-class extension model: an extension advertises a negotiated capability, adds request methods, and can intercept tool calls. FastMCP's add_extension() builds on it and adds a lifespan, so an extension manages its own setup and teardown. Background tasks are implemented this way, outside FastMCP core.

  • argument completion: A completion handler suggests values for prompt and resource-template inputs, and it receives the argument values already supplied, so suggestions can depend on earlier choices.

  • auth for agents and services: Identity assertion (SEP-990, in beta) lets an agent act for a user without a browser flow: a trusted identity provider signs an assertion, and the server exchanges it for a short-lived token that flows through the normal authorization context. Alongside it: provider-neutral role checks, insufficient-scope challenges that name the missing scopes, and client-credentials auth for callers with no user behind them.

  • infrastructure hooks: A server can set a cache hint — TTL and public/private scope — that opting-in clients and proxies honor. Modern clients also send Mcp-Method and Mcp-Name headers on every request, so gateways can route by MCP method without parsing JSON-RPC bodies, and individual tool arguments can be mirrored into headers with the x-mcp-header schema extension.

Old clients keep working

Your users' clients upgrade on their own schedules, and plenty of deployed clients will speak the old protocol for a long time. FastMCP 4 servers handle this for you: when a client connects, the server negotiates the best protocol version they share. New clients get the new protocol, old clients get the old one, and server metadata looks the same to your code either way.

The FastMCP client does the same in reverse — Client(url) probes for the new protocol and falls back to the old handshake if that's all the server speaks.

Exciting plumbing

Dependency injection, via 's uncalled-for, can bind a dependency to arguments of the call it serves:

from fastmcp import FastMCP
from fastmcp.dependencies import CallArgument, Depends

mcp = FastMCP("Accounts")


def get_account(user_id: str) -> dict:
    return {"id": user_id, "plan": "pro"}


@mcp.tool
async def show_account(
    owner: str,
    account: dict = Depends(
        get_account,
        user_id=CallArgument("owner")
    ),
) -> str:
    return f"{account['id']} is on {account['plan']}"

The client sees one argument, owner, the dependency never appears in the tool schema. This works in regular tools and background tasks.

ClientGroup manages one client per server, with collision-checked tool namespacing and call routing. Each client in the group negotiates its own protocol version, so a group can span a new-protocol server, an old-protocol server, and a local subprocess.

Smaller improvements:

  • typed outputs serialize through Pydantic

  • OpenAPI parameter examples flow into generated schemas

  • every tool gets a usable title for clients that require one

  • HTTP startup is faster, lightweight imports no longer pull in the MCP and CLI stacks

  • Python 3.14 compatibility fixes landed during the betas.

Four five betas, or... five four betas?

FastMCP 4 spent five weeks in five betas used by real applications: multi-server gateways, agent frameworks, production servers, and lots of dogfooding. 23 contributors landed more than 80 pull requests and closed roughly 50 issues.

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 and deprecations

In the new protocol there is no live connection for a server to call back into mid-request, so v4 removes server-initiated sampling and roots from the server API.

ctx.elicit() still works on old-protocol connections; on the new protocol, use interactive tools instead. (Clients can still register roots; a modern server asks for them through the interactive pattern.)

v4 removes the APIs deprecated during FastMCP 3, adopts the SDK's snake_case field names for direct MCP model access (a compatibility bridge maps the old camelCase names and warns), and moves background tasks into fastmcp-tasks.

v4 deprecates inferring stdio transports from strings (i.e. passing Client("server.py") ) — pass a Path to run local code, keep strings for URLs. The old form warns, and will be removed in FastMCP 5.

The upgrade guide covers every change and includes a copyable prompt for auditing an application with a coding agent.

Get started

pip install fastmcp -U

Have an issue or an idea? Open an issue or a PR

Happy (context) engineering!