FastMCP is an open-source Python framework for building Model Context Protocol servers and clients, maintained by Prefect. The name causes confusion because it also used to be the class inside the official MCP Python SDK. That built-in version is gone. In mcp 2.x it was renamed MCPServer, so from mcp.server.fastmcp import FastMCP now fails. What's left under the name is the standalone fastmcp package, now at version 4, which sits on top of the SDK and adds composition, proxying, a client, auth providers and deployment tooling.

This guide covers what that framework actually is today, how it differs from the SDK's own server class, what changed in FastMCP 4, and when you don't need it at all. Every snippet was run on November 4, 2026 against fastmcp 4.1.6 and mcp 2.3.0, the current releases that day. If you just want to build your first server step by step with the official SDK, our Python MCP server tutorial does that. This post is about the framework.

Which FastMCP are you looking at?

A 1908 black-and-white photograph of driver Jules Goux seated in his Lion-Peugeot race car, number 34, beside a mechanic, with spectators and a grandstand behind them at Dieppe
Jules Goux in his Lion-Peugeot at the 1908 Grand Prix des Voiturettes at Dieppe. Agence Rol, Bibliothèque nationale de France, via Wikimedia Commons, public domain.

Search results mix up four different things. Check which one a tutorial means before you copy its imports:

What you seeWhat it isStatus in November 2026
from mcp.server.fastmcp import FastMCPFastMCP 1.0, the high-level server bundled in the official mcp package, version 1.xGone in mcp 2.x. The 1.x line still gets releases if you pin mcp<2
from mcp.server import MCPServerThe SDK's renamed and rebuilt successor to that classCurrent. mcp 2.3.0, released October 2
from fastmcp import FastMCPThe standalone framework from PrefectHQ (Apache-2.0)Current as of November 4. 4.1.6 released November 3, built on mcp 2.3 or later
fastmcp on npmAn unrelated TypeScript framework by punkpeye (MIT). Prefect's TypeScript port is @prefecthq/fastmcp-tsBoth active, both separate from the Python project

The lineage explains the overlap. FastMCP started as a standalone project in late 2024. Its 1.0 was merged into the official Python SDK soon after and first shipped in mcp 1.2.0 in January 2025. The standalone repo kept going and became a much bigger framework, while the SDK kept a smaller version of the same API. When the SDK shipped v2 on July 28, 2026, it rebuilt that class and gave it a new name. So the two now share a family resemblance and a protocol layer, but they're different products with different scope.

Why from mcp.server.fastmcp stopped working

If an older tutorial's first line just failed with ModuleNotFoundError: No module named 'mcp.server.fastmcp', your environment pulled in mcp 2.x. Nobody had to choose that. An unpinned dependency or a fresh lockfile is enough. The error message itself now tells you about the rename and links the SDK's migration guide. You have three ways forward:

# mcp 1.x tutorial code (breaks on mcp 2.x with ModuleNotFoundError)
from mcp.server.fastmcp import FastMCP

# Option 1: the official SDK v2, same idea, new name
from mcp.server import MCPServer

# Option 2: the standalone framework, usually just this line
from fastmcp import FastMCP
  • Pin mcp<2 if you need the old code running today and will port it later.
  • Port to MCPServer if you want to stay on the official SDK alone. It's more than a rename: the import path changed, transport options like host and port moved from the constructor to run(), and protocol types now use snake_case fields. The decorators are still called as @mcp.tool(), unlike FastMCP's bare @mcp.tool. Our Python tutorial is written for this path.
  • Switch the import to fastmcp. FastMCP's own upgrade guide says that for most 1.0 servers this one line is the whole migration, because the standalone project kept the API that 1.0 introduced. The exceptions it lists are auth (three constructor arguments become one auth= provider), host and port moving to run(), and four Context methods whose signatures changed.

A FastMCP server, start to finish

Install it with uv or pip. It needs Python 3.10 or newer:

uv add fastmcp            # or: pip install fastmcp
fastmcp version           # prints FastMCP, MCP SDK and Python versions

A server is a FastMCP instance plus decorated functions. Type hints become the input schema and the docstring becomes the tool description:

from fastmcp import FastMCP

mcp = FastMCP("orders")


@mcp.tool
def order_status(order_id: str) -> dict:
    """Look up the shipping status of an order."""
    return {"order_id": order_id, "status": "shipped", "carrier": "UPS"}


if __name__ == "__main__":
    mcp.run()

mcp.run() with no arguments speaks stdio, which is what desktop clients launch. The CLI lets you poke at it without writing a client, and serve it over Streamable HTTP without touching the code:

fastmcp list server.py
fastmcp call server.py order_status order_id=A-1001
fastmcp run server.py --transport http --port 8000   # serves http://localhost:8000/mcp

FastMCP names the Streamable HTTP transport "http", where the SDK calls it "streamable-http". That's a frequent copy-paste error when moving code between the two.

One small difference showed up in our test. We wrote the same tool, returning a plain dict, once with FastMCP and once with the SDK's MCPServer. FastMCP advertised an output schema and returned structuredContent alongside the text. MCPServer returned the JSON as text only, with no output schema, because the return type was a bare dict. With the SDK, annotate it more precisely (say dict[str, str]) or return a Pydantic model if you want structured output. Our guide to MCP tools explains why structured results matter to clients.

What the framework adds over the SDK

For a single server with a handful of tools, MCPServer and FastMCP feel almost the same. The gap shows up once you have more than one server, need real auth, or want to call servers from Python. These are the parts people usually adopt FastMCP for.

Composition and namespaces

mount() combines servers into one. Each child keeps its own file and tests, and the parent exposes all of their tools. A namespace prefixes every name so two children can't collide:

from fastmcp import FastMCP
from server import mcp as orders

billing = FastMCP("billing")


@billing.tool
def order_status(invoice_id: str) -> str:
    """Return whether an invoice is paid."""
    return "paid"


app = FastMCP("support-desk")
app.mount(orders, namespace="orders")
app.mount(billing, namespace="billing")

Both children define order_status, and the parent serves them as orders_order_status and billing_order_status. The mount is live, so a tool added to a child later shows up through the parent without remounting.

Proxying other servers

create_proxy() wraps any MCP server, local or remote, as a FastMCP server you can run or mount. The same call bridges transports, so you can expose a remote HTTP server over stdio or put a stdio server behind a URL:

from fastmcp import FastMCP
from fastmcp.server import create_proxy

gateway = FastMCP("gateway")
gateway.mount(create_proxy("http://127.0.0.1:8000/mcp"), namespace="orders")

if __name__ == "__main__":
    gateway.run()

We ran the order server over HTTP on port 8000 and called it through this gateway as orders_order_status. The proxy is lazy: creating it or starting the server doesn't contact the backend. By default, each request gets its own backend session, so concurrent clients don't share state.

One default deserves attention. The proxy forwards incoming HTTP headers, including Authorization, to HTTP backends. If your gateway authenticates its own users, set forward_incoming_headers=False on the backend's ProxyClient. Otherwise you're passing your users' tokens to a server they were never issued for. That's the token passthrough problem from our MCP security checklist. If you're building a full aggregation layer, our MCP gateway guide covers the trade-offs.

A Python client for any MCP server

fastmcp.Client picks its transport from what you pass it. A server object connects in memory, a Path launches a stdio subprocess, and a URL connects over HTTP. If you only need the client, pip install "fastmcp-slim[client]" skips the server stack. To be fair to the SDK, mcp v2 added its own high-level Client too. FastMCP's adds a one-line auth="oauth" (the SDK has an OAuth provider too, but you wire it into the transport yourself), multi-server config dicts, and ClientGroup, which merges several servers into one namespaced tool catalog.

The in-memory transport is the best testing story in Python MCP. You can test a server with pytest and no network listener:

import pytest
from fastmcp import Client
from server import mcp
from compose import app


@pytest.fixture
async def client():
    async with Client(mcp) as c:
        yield c


async def test_order_status(client):
    result = await client.call_tool("order_status", {"order_id": "A-1001"})
    assert result.data["status"] == "shipped"


async def test_namespaces():
    async with Client(app) as c:
        names = {t.name for t in await c.list_tools()}
    assert names == {"orders_order_status", "billing_order_status"}

Set asyncio_mode = "auto" for pytest-asyncio and both tests pass as written.

Auth providers

A FastMCP server defaults to auth=None. On HTTP, that means anyone who can reach the URL can call every tool. FastMCP ships one auth= parameter that takes a provider object. The common ones:

  • JWTVerifier validates tokens you already issue, against a JWKS endpoint.
  • RemoteAuthProvider delegates to an identity provider that supports dynamic client registration, such as WorkOS AuthKit or Descope.
  • OAuthProxy bridges providers that don't support dynamic registration. GitHub, Google, Azure and others have ready-made subclasses.
  • OAuthProvider makes your server its own authorization server. FastMCP's docs say to use it only if you have the expertise to run OAuth securely.

The SDK gives you the building blocks (a token verifier and resource metadata), but you assemble them yourself. If you're putting a server on the internet with SSO, this is the biggest time saver. For the protocol side, see how MCP authorization works.

Everything else

  • OpenAPI and FastAPI generation. FastMCP.from_openapi() turns a spec into tools. FastMCP's own docs warn that curated servers perform significantly better than auto-converted ones, and suggest generation for bootstrapping rather than mirroring a whole API. Our MCP vs API post explains why.
  • Transforms and middleware. Rename tools, reshape their arguments, hide components per client, or replace a big catalog with on-demand tool search.
  • Background tasks. @mcp.tool(task=True) with the optional fastmcp-tasks package implements the MCP tasks extension. The default backend is in-memory and single-process. Durable work needs Redis or Valkey.
  • CLI and installs. Beyond run, list and call, fastmcp install writes config for Claude Code, Claude Desktop, Cursor, Gemini CLI and Goose. fastmcp run --reload restarts the server on file changes.
  • Hosting. Prefect runs Horizon, a hosted platform for FastMCP servers. FastMCP's docs describe a free personal tier. You don't need it. A FastMCP server is an ASGI app you can deploy anywhere you run Python.

What changed in FastMCP 4

FastMCP 4.0 shipped on August 31, 2026, about a month after the MCP spec's 2026-07-28 revision made the protocol sessionless. Our MCP architecture guide covers that change in detail. The short version for FastMCP users:

  • One server, both protocol eras. A FastMCP 4 server negotiates per connection. In our test, the default client got 2026-07-28 from a FastMCP HTTP server, and Client(url, mode="legacy") got 2025-11-25 from the same server.
  • Server-pushed requests are gone. ctx.sample(), ctx.sample_step() and ctx.list_roots() were removed outright, on every protocol era. ctx.elicit() still works on handshake-era connections but raises on modern ones. The replacement is to return an InputRequiredResult and let the client call the tool again with the answer.
  • State moved out of the connection. On modern connections, ctx.set_state doesn't persist between calls and on_initialize middleware never runs. UserSession stores per-user state server-side instead, and it requires auth.
  • Most FastMCP 3 servers run unchanged, according to the project. Some imports moved, notably the old proxy and OpenAPI modules.

The upgrade guide is frank about one case. If your server exists to borrow the caller's model through sampling, and you hold no model key of your own, it says staying on FastMCP 3.x is "the honest answer until that changes." As of November 4, the 3.4 line is still getting patch releases (3.4.11 came out October 20).

Whichever version you use, pin it exactly. FastMCP's versioning policy allows breaking changes in minor releases when the protocol demands them, and its own docs recommend fastmcp==x.y.z over a range.

FastMCP or the official SDK?

Both are good choices, and they share the same protocol layer underneath, since FastMCP 4 depends on mcp 2.3 or later. The question is how much framework you want to carry.

Official SDK (MCPServer)FastMCP
One server, a few tools, stdioEnough. Fewer dependenciesWorks, but more than you need
Old mcp.server.fastmcp codeA real portUsually a one-line import change
Several servers behind one endpointBuild it yourselfmount() with namespaces
Wrapping or bridging other serversBuild it yourselfcreate_proxy(), fastmcp-remote
Internet-facing server with SSOBuilding blocksReady-made providers for common IdPs
Calling servers from PythonHas a high-level ClientClient plus OAuth helpers and ClientGroup
Release cadenceSteady. Minor releases every two to four weeks since JulyFast. Seven stable releases across the 3.4 and 4.1 lines between October 19 and November 3
GovernanceOfficial, under the modelcontextprotocol GitHub orgOpen source, maintained by Prefect

Our default advice hasn't changed from our MCP SDK guide. Start with the official SDK for a small server, and reach for FastMCP when you need one of the things in the right-hand column. If you're already holding old FastMCP 1.0 code, the standalone package is the shorter path forward.

Using a remote MCP server from FastMCP

FastMCP is just as useful on the consuming side. Take DialMCP, which is a remote MCP server at https://mcp.dialmcp.com/mcp that places real phone calls from your own verified number. It isn't built with FastMCP (it's TypeScript on Cloudflare Workers), but that doesn't matter to a client. MCP is the contract. This connects to it from Python:

import asyncio
from fastmcp import Client


async def main():
    async with Client("https://mcp.dialmcp.com/mcp", auth="oauth") as client:
        print(client.protocol_version)
        for tool in await client.list_tools():
            print(tool.name)


asyncio.run(main())

We ran this on November 4 up to the browser step. FastMCP found DialMCP's OAuth metadata, registered itself as a client, and opened an authorization URL with PKCE and the resource indicator, which is the flow the spec asks for. Finishing it requires signing in and verifying a phone number, which a script can't do for you. Two practical notes: tokens are kept in memory by default, so pass an encrypted token_storage if you don't want to sign in on every run. And DialMCP is built on the 2025-era session protocol, which FastMCP's client is designed to fall back to without any configuration.

For a desktop app that only launches local commands, fastmcp-remote bridges a remote URL to stdio and handles the OAuth sign-in in your browser:

{
  "mcpServers": {
    "dialmcp": {
      "command": "uvx",
      "args": ["fastmcp-remote", "https://mcp.dialmcp.com/mcp"]
    }
  }
}

Most current clients, including Claude, ChatGPT, Cursor and VS Code, add remote servers directly, so you usually won't need the bridge. Our client setup docs list the native route for each one.

Try a remote MCP server that does real work. DialMCP lets Claude, ChatGPT, Codex, Cursor or VS Code place real phone calls from your own verified number and hand back a transcript and a structured outcome. Add https://mcp.dialmcp.com/mcp as a connector and sign in. US and Canada, free during launch.

Connect DialMCP to your client

FAQ

What is FastMCP?

FastMCP is an open-source Python framework for building MCP servers, clients and apps, maintained by Prefect under the Apache-2.0 license. You write ordinary functions with type hints and decorate them, and FastMCP generates the schemas and handles the protocol. Beyond that it adds server composition, proxying, a client, auth providers and a CLI. The current major version is 4, built on the official MCP Python SDK v2.

Is FastMCP the same as the official MCP Python SDK?

No. FastMCP 1.0 was bundled into the official mcp package starting with mcp 1.2.0 in January 2025, but in mcp 2.x that class was rebuilt and renamed MCPServer. The standalone fastmcp package is a separate project that depends on the official SDK and adds framework features on top of it.

Why does from mcp.server.fastmcp import FastMCP fail?

You have mcp 2.x installed, where the module no longer exists. Either pin mcp<2, port your code to MCPServer from mcp.server, or install the standalone package and change the import to from fastmcp import FastMCP, which is often the only change a FastMCP 1.0 server needs.

Should I use FastMCP or the MCP SDK?

For one small server, the official SDK is enough and has fewer dependencies. Choose FastMCP when you need to combine several servers, proxy or bridge other servers, add SSO through a ready-made auth provider, or call servers from Python with OAuth handled for you. Pin an exact FastMCP version either way, since minor releases can include breaking changes.

Does FastMCP support the 2026-07-28 MCP spec?

Yes. FastMCP 4 serves both the sessionless 2026-07-28 protocol and the older session-based versions from one server, negotiating per connection. To get there it removed server-pushed sampling and roots from the Context, and ctx.elicit() raises on modern connections, so tools ask for input by returning an InputRequiredResult instead.