FastMCP 4 is stable and generally available.

pip install fastmcp -U

Earlier this summer, MCP released the 2026-07-28 revision of the protocol, and the official Python SDK shipped v2, rebuilt around it.

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. A FastMCP 4 server handles 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.

the framework

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.

five four betas

FastMCP 4 spent five weeks and five betas under 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 betas resulted in a lot of correctness work: hardened OAuth consent flows, issuer validation, and JWT verification (including EdDSA); proxies that strip cookies and connection-owned headers at trust boundaries and reconnect after failed sessions; encrypted background-task snapshots; serialized event-store writes; response caching that handles equivalent arguments, empty results, errors, and versioned components correctly; and compatibility fixes across CodeMode, CLI discovery, OpenAPI, Unicode search, and Python 3.14 support.

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


Happy (context) engineering!