On this page
The Python MCP SDK is the official mcp package. FastMCP is a separate,
bigger framework, fastmcp, built on top of it. For a small server, the
SDK's MCPServer class is enough. Pick FastMCP when you need its extras.
Either way, send logs to stderr, never to stdout.
Versions on 11 November 2026: mcp 2.3.0 (released 2 October) and fastmcp
4.1.7 (released 10 November). Both are on PyPI and both need Python 3.10 or
newer. FastMCP 4 runs on top of the official SDK v2, so installing it pulls
in mcp as well.
This guide compares the two, explains why old FastMCP imports suddenly break, shows the transports, and covers logging, the part that trips up most first servers. Everything below comes from the official SDK docs, the FastMCP docs, PyPI and each project's GitHub security advisories.
Python MCP SDK vs FastMCP at a glance
| Official Python MCP SDK | FastMCP | |
|---|---|---|
| Package | mcp |
fastmcp |
| Latest version | 2.3.0 (1.30.0 on the older v1 line) | 4.1.7 (3.4.11 on the 3.x line) |
| Made by | The Model Context Protocol project | Prefect |
| License | MIT | Apache-2.0 |
| Server class | from mcp.server import MCPServer |
from fastmcp import FastMCP |
| Tool decorator | @mcp.tool() |
@mcp.tool |
| Open in the MCP Inspector | uv run mcp dev server.py |
fastmcp dev inspector server.py |
| Transports | stdio (default), Streamable HTTP, SSE | stdio (default), HTTP (Streamable HTTP), SSE |
| HTTP Host/Origin check | On by default on localhost | Off unless you turn it on |
| PyPI downloads, last 7 days | 79.2 million | 14.4 million |
The download counts come from pypistats.org on 11 November 2026. The mcp
number includes every FastMCP install, because FastMCP depends on it.
What is FastMCP (the FastMCP SDK)?
FastMCP, sometimes written "fast MCP" or called the FastMCP SDK, is a Python framework for building MCP servers, MCP clients and small interactive apps that show inside a chat. You write an ordinary Python function with type hints and a docstring. FastMCP turns it into an MCP tool, builds the input schema from the type hints and checks every call against it.
The name causes confusion because it has meant two things:
- 2024: FastMCP 1.0 was folded into the official SDK. That's where
from mcp.server.fastmcp import FastMCPinmcp1.x came from. - 2025 to now: FastMCP carried on as its own project. Version 2.0 came out in April 2025, 3.0 in February 2026 and 4.0 in August 2026. It now lives under Prefect's GitHub organisation as PrefectHQ/fastmcp.
- July 2026: the official SDK 2.0 renamed its
FastMCPclass toMCPServer, so "FastMCP" now only means the standalone package.
So when a tutorial says "FastMCP", look at its import line. mcp.server.fastmcp
means an old official-SDK tutorial. from fastmcp import FastMCP means the
standalone framework.
What FastMCP adds on top of the SDK, going by its docs: combining several servers into one, building a server from an OpenAPI spec or a FastAPI app, ready-made sign-in integrations (Auth0, WorkOS AuthKit, PropelAuth and others), middleware, interactive app UIs and a full client with its own command-line tool. The FastMCP docs call it "the standard framework for working with MCP". That's their claim; the official SDK covers servers and clients too.
Why "No module named 'mcp.server.fastmcp'" appears
If your server worked last month and now fails with this, your install
picked up mcp 2.x:
ModuleNotFoundError: No module named 'mcp.server.fastmcp'
The official SDK 2.0 (28 July 2026) removed that module. The migration guide says the old path is gone, not just deprecated. An unpinned dependency, a fresh lockfile or a rebuilt container is enough to hit it. You have three ways out:
- Pin the old version for now.
pip install "mcp<2"(oruv add "mcp<2") brings the module back with no code changes. The SDK team says v1 stays in maintenance and keeps getting security fixes. Use 1.30.0 or later. - Move to the official v2 API. Change the import to
from mcp.server import MCPServerand work through the migration guide. Expect more than one line: transport options moved from the constructor torun(), protocol types now use snake_case names, andctx.fastmcpis nowctx.mcp_server. - Switch to standalone FastMCP. Install
fastmcp>=4and change the import tofrom fastmcp import FastMCP. FastMCP's upgrade guide says that for a FastMCP 1.0 server this is usually the only change, except for servers that use authentication.
If you publish a library that depends on mcp and aren't ready to migrate,
the SDK docs suggest an upper bound such as mcp>=1.28,<2.
Which one should you use?
Both write tools the same way: a typed function plus a docstring. Moving from one to the other later is mostly a change of imports, so this isn't a decision you're locked into.
Use the official SDK when:
- you want fewer dependencies and the reference code from the MCP project itself (SDK 2.0 shipped the same day as the 2026-07-28 spec)
- you're writing a few tools for yourself or your team
- your company prefers MIT-licensed code
Use FastMCP when:
- you need one of its extras: server composition, OpenAPI or FastAPI import, ready-made sign-in providers, middleware or app UIs
- you have FastMCP 1.0 code and want the shortest upgrade path
Whichever you pick, pin the version and keep it patched. Both projects had a busy autumn of security fixes, covered below.
The same server in both
Install one of them with uv:
uv add "mcp[cli]" # official SDK, with the mcp command-line tool
uv add fastmcp # FastMCP
The official SDK, following its own logging example:
import logging
from mcp.server import MCPServer
logger = logging.getLogger(__name__)
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
logger.info("Searching for %r", query)
return f"Found 3 books matching {query!r}."
if __name__ == "__main__":
mcp.run()
The same server in FastMCP:
from fastmcp import FastMCP
from fastmcp.utilities.logging import get_logger
logger = get_logger(__name__)
mcp = FastMCP("Bookshop")
@mcp.tool
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
logger.info("Searching for %r", query)
return f"Found 3 books matching {query!r}."
if __name__ == "__main__":
mcp.run()
Run either with uv run server.py. It prints nothing and waits: a stdio
server sits silent until an AI client talks to it. To try it by hand, open
it in the MCP Inspector with the command from the
table above. For a full walk-through, with the Claude setup and a security
checklist, see how to create an MCP server securely.
Transports: stdio, Streamable HTTP and SSE
A transport is how messages travel between your server and the AI client. Both packages default to stdio:
| Transport | Official SDK | FastMCP | Use it for |
|---|---|---|---|
| stdio | mcp.run() |
mcp.run() |
A server your AI client starts on your own machine |
| Streamable HTTP | mcp.run(transport="streamable-http") |
mcp.run(transport="http") |
A server that keeps running and that others connect to by URL |
| SSE | mcp.run(transport="sse") |
mcp.run(transport="sse") |
Old clients only. Streamable HTTP replaced it in the 2025-03-26 spec |
Over HTTP, both listen on 127.0.0.1 port 8000 and serve the MCP endpoint
at /mcp by default. Keep 127.0.0.1 unless you really need other machines
to connect. Our local MCP server guide explains
why, and what to use instead of opening a port.
One difference matters for safety. The checks on the Host and Origin
headers stop a web page in your browser from talking to a local server
(a "DNS rebinding" attack). They are set up differently:
- Official SDK: switched on by default for localhost. Once you deploy
behind a real hostname, every request gets a
421error until you list your hostname intransport_security=. - FastMCP: off by default. Set
FASTMCP_HTTP_HOST_ORIGIN_PROTECTION=autoto protect servers bound to localhost, ortrueplusFASTMCP_HTTP_ALLOWED_HOSTSwhen you deploy.
FastMCP logging: stderr, not stdout
Over stdio, the AI client reads MCP messages from your server's stdout. So stdout belongs to the protocol, and anything else printed there can break the connection. The MCP specification (2026-07-28) says it plainly:
The server MUST NOT write anything to its
stdoutthat is not a valid MCP message.
It also says the server "MAY write UTF-8 strings to stderr for any logging
purposes". So: logs go to stderr. In practice that means using Python's
logging module and never print().
Official SDK. Creating an MCPServer already sets up logging to stderr
at INFO level. For debug lines, use MCPServer("Bookshop", log_level="DEBUG").
If you set up logging yourself before creating the server, your setup wins.
The SDK also moves flushed stdout output to stderr while it runs. Its docs
warn that an unflushed print() can still land on the protocol stream when
the process exits.
FastMCP. FastMCP's own log lines go to stderr. Use
get_logger(__name__) from fastmcp.utilities.logging, as in the example
above, so your lines go through the same stderr handler. To change the level:
FASTMCP_LOG_LEVEL=DEBUG uv run server.py
Or pass it in code with mcp.run(log_level="DEBUG"). FastMCP formats logs
with the Rich library by default. Set FASTMCP_ENABLE_RICH_LOGGING=false
for plain lines, which are easier to search or send to a log collector.
If you'd rather use a plain logging.getLogger(__name__) with FastMCP, call
logging.basicConfig(level=logging.INFO) once at startup. FastMCP only sets
up its own fastmcp logger, so your logger needs a handler of its own.
basicConfig writes to stderr by default.
Logs the model sees, and logs it doesn't
Your log lines never reach the model. Only what a tool returns does. If the
model needs to know something, put it in the return value or raise an error
with a clear message.
FastMCP also has ctx.debug(), ctx.info(), ctx.warning() and
ctx.error(). They send log messages to the client over MCP itself, and
FastMCP(client_log_level="warning") filters them. Be careful about relying
on this: the 2026-07-28 spec deprecates MCP-level logging (SEP-2577), and
the official SDK docs no longer teach it. Use stderr for your own logs.
Where to read the logs
- Claude Desktop: each server's stderr goes to its own
mcp-server-<name>.logfile. Our MCP server not working guide shows where to find it. - HTTP servers: the terminal or service manager that runs the process.
- The MCP Inspector: shows the server's stderr in its Console tab while you test.
Never log tokens, API keys or whole request bodies. Log files get copied into bug reports and support tickets.
Keep it patched: 2026 security advisories
Both projects published a lot of security fixes in 2026. Here is what each project's GitHub security advisory list showed on 11 November 2026.
Official Python SDK (mcp): nine advisories in 2026, six of them rated
high. Most are in the HTTP transports, the OAuth sign-in code and the client.
Every one is fixed in 1.30.0 on v1 and 2.2.0 on v2. Use the latest,
2.3.0, if you can.
FastMCP (fastmcp): 16 advisories published in October 2026 alone: one
critical, five high, nine medium and one low. The critical one
(GHSA-gpgx-58w2-xwwf)
lets a malicious server run code inside a FastMCP client that calls one of
its tools. It affects 2.10.0 and later, and the advisory lists fixes only in
3.4.8 and 4.0.11.
- On 4.x, use 4.1.2 or later. 4.1.7 is current.
- On 3.x, use 3.4.10 or later.
- On 2.x (the last release was 2.14.7, in April 2026), upgrade. The critical client bug has no 2.x fix.
A pin that keeps you on a fixed line without surprise major upgrades:
uv add "mcp>=2.2,<3"
uv add "fastmcp>=4.1.2,<5"
Commit your lockfile too, so a rebuild doesn't quietly change versions. If you also run Node-based servers, the MCP TypeScript SDK vulnerabilities guide covers the same check for npm.
Start a Python MCP server in 5 steps
0 of 5 done
Tick these off as you go.
1. Pick one package
The official mcp for a small server, fastmcp if you need its extras. Check
that every tutorial you follow uses the same import.
2. Pin a patched version
mcp>=2.2,<3 or fastmcp>=4.1.2,<5, with the lockfile committed.
3. Write each tool as a typed function with a docstring
Keep the inputs narrow and give each tool the least access that works. The security checklist lists what to check.
4. Log with logging, never print()
Logs go to stderr. Set the level with log_level= or FASTMCP_LOG_LEVEL,
and keep secrets out of them.
5. Test in the MCP Inspector, then lock it down
Run uv run mcp dev server.py or fastmcp dev inspector server.py. If it
runs over HTTP, keep it on 127.0.0.1 and turn on the Host/Origin check.
Want a hand building one? We build custom MCP servers in Python and TypeScript, with sign-in, narrow tools and logs your team can read. Tell us what you need to connect.
Frequently asked questions
What is FastMCP?
FastMCP is an open-source Python framework (Apache-2.0, from Prefect) for building MCP servers, clients and app UIs. You write normal Python functions and FastMCP turns them into MCP tools. Its first version became part of the official Python SDK in 2024; the standalone project is now at version 4.
Is there a FastMCP SDK?
Yes, in the sense that fastmcp is a Python package you install with uv add fastmcp. It is a framework built on top of the official Python MCP SDK (mcp), not the official SDK itself. Its current version is 4.1.7.
Is FastMCP part of the official MCP SDK?
Not any more. FastMCP 1.0 was bundled in mcp 1.x as mcp.server.fastmcp.
The official SDK 2.0 renamed that class to MCPServer and removed the old
module. Today the standalone fastmcp package is a separate project that
runs on top of the official SDK.
Should I use FastMCP or the official Python MCP SDK?
For a few tools, the official SDK's MCPServer is enough and has fewer
dependencies. Use FastMCP if you need server composition, OpenAPI import,
ready-made sign-in providers or app UIs. Both use the same decorator style,
so switching later is mostly a change of imports.
Why do I get "No module named 'mcp.server.fastmcp'"?
Your install picked up mcp 2.0 or later, which removed that module. Pin
mcp<2 for a quick fix, or move to from mcp.server import MCPServer or
from fastmcp import FastMCP.
Where do FastMCP logs go?
To stderr. FastMCP's own logs and anything you log through
fastmcp.utilities.logging.get_logger() go there, never to stdout. In Claude
Desktop, each server's stderr lands in its mcp-server-<name>.log file.
How do I turn on FastMCP debug logging?
Set the environment variable FASTMCP_LOG_LEVEL=DEBUG, or call
mcp.run(log_level="DEBUG"). For the official SDK, pass
log_level="DEBUG" when you create the MCPServer.
Can I use print() in a Python MCP server?
Not in a stdio server. print() writes to stdout, which carries the MCP
messages, and a stray line can break the connection. Use the logging
module instead, which writes to stderr.
Which Python version do I need?
Python 3.10 or newer, according to the PyPI listings for mcp 2.3.0 and
fastmcp 4.1.7. The official SDK's README already asks for 3.11, so start a
new project on 3.11 or later.