On this page
Python MCP SDK vs FastMCP: Which to Use and How to Log (Nov 2026)

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 FastMCP in mcp 1.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 FastMCP class to MCPServer, 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:

  1. Pin the old version for now. pip install "mcp<2" (or uv 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.
  2. Move to the official v2 API. Change the import to from mcp.server import MCPServer and work through the migration guide. Expect more than one line: transport options moved from the constructor to run(), protocol types now use snake_case names, and ctx.fastmcp is now ctx.mcp_server.
  3. Switch to standalone FastMCP. Install fastmcp>=4 and change the import to from 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 421 error until you list your hostname in transport_security=.
  • FastMCP: off by default. Set FASTMCP_HTTP_HOST_ORIGIN_PROTECTION=auto to protect servers bound to localhost, or true plus FASTMCP_HTTP_ALLOWED_HOSTS when 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 stdout that 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>.log file. 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

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.