mrkeyoor.com_
Wed 05 Aug 19:56 UTC
PyPIAI / MLupdated 05 Aug 2026

fastmcp

FastMCP is the Prefect-maintained Python framework for the Model Context Protocol, the standard that connects LLMs to tools and data. You write a plain Python function, decorate it with @mcp.tool, and FastMCP generates the JSON schema, validation, and protocol plumbing so Claude, Cursor, or any MCP client can call it. It covers all three sides of MCP: servers (tools, resources, prompts), clients (connect to any server by URL with transport negotiation and auth handled), and apps (interactive UIs rendered in the conversation). FastMCP 1.0 was folded into the official MCP Python SDK in 2024; the standalone project kept moving and is now on its own 3.x line with far more features than the SDK version.

Verdict

The default choice for building MCP servers in Python: the decorator API is genuinely pleasant and the ecosystem momentum is real. Pin your major version and budget time for upgrade guides, because this project moves faster than the protocol it implements.

API stability3/5Three major versions since 2024 and five official upgrade guides. The core decorator API is stable, but defaults, transports, and auth interfaces have churned between majors.
Docs5/5gofastmcp.com is thorough, with guides, API reference, upgrade paths, and llms.txt exports. Better documented than the official SDK.
Maintenance5/5Backed by Prefect with a paid product (Horizon) on top, near-daily commits, and 243 open issues on a very large user base.
Ecosystem5/5The README claims some version of FastMCP powers 70% of MCP servers across languages, and 1.0 ships inside the official SDK. It is the center of gravity for Python MCP.

Use it if

  • You are building an MCP server in Python and want to expose existing functions as tools without hand-writing JSON schemas or protocol lifecycle code
  • You need a programmatic MCP client, for example to test your own server in-process or to script calls against a remote server from Python
  • You want production concerns handled: HTTP transport, OAuth and bearer auth, server composition via mounting, and middleware are built in rather than DIY
  • You are already on the official MCP SDK's FastMCP 1.x and keep hitting missing features; the standalone project is where active development happens
Skip it if

Setup reality

pip install fastmcp (the team recommends uv) needs Python 3.10+ and pulls the mcp SDK plus pydantic, httpx, and a CLI. The confusing part is naming: `from mcp.server.fastmcp import FastMCP` (official SDK, old 1.x code) and `from fastmcp import FastMCP` (this project) are different packages with diverging APIs, and tutorials mix them freely. Decorator syntax changed across majors (@mcp.tool() vs @mcp.tool both work now), stdio vs HTTP transport trips people up when moving from Claude Desktop to a hosted server, and if you came from 2.x expect to read the v3 upgrade guide because defaults moved.

Patterns

Minimal server with one toolcreate-server-with-tool

from fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.tool
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

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

The docstring becomes the tool description and type hints become the JSON schema. mcp.run() defaults to stdio transport, which is what Claude Desktop and most local clients expect.

Serve over HTTP instead of stdiorun-http-server

from fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.tool
def ping() -> str:
    return "pong"

if __name__ == "__main__":
    mcp.run(transport="http", host="0.0.0.0", port=8000)

HTTP transport serves at /mcp by default. Clients connect to http://host:8000/mcp, not the bare root URL; forgetting the path is a common first failure.

Expose read-only data as a resourcedefine-resource

from fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.resource("data://config")
def get_config() -> dict:
    return {"env": "prod", "debug": False}

Resources are for data the client reads, not actions. The URI scheme is arbitrary but must be a valid URI; returning a dict serializes to JSON automatically.

Parameterized resource templateresource-template

from fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.resource("users://{user_id}/profile")
def user_profile(user_id: str) -> dict:
    return {"id": user_id, "name": "Ada"}

Placeholders in the URI must match the function parameter names exactly or registration fails at import time.

Reusable prompt templatedefine-prompt

from fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.prompt
def review_code(code: str) -> str:
    return f"Review this code for bugs and style:\n\n{code}"

Prompts are user-invoked templates, not tools; clients surface them as slash commands or menu items.

Log and report progress from inside a toolcontext-logging-progress

from fastmcp import FastMCP, Context

mcp = FastMCP("Demo")

@mcp.tool
async def crunch(items: list[str], ctx: Context) -> int:
    await ctx.info(f"processing {len(items)} items")
    for i, _ in enumerate(items):
        await ctx.report_progress(progress=i + 1, total=len(items))
    return len(items)

Just add a Context-typed parameter and FastMCP injects it; it is excluded from the tool's public schema. Context methods are async, so the tool must be async too.

Call a remote server from Pythonclient-call-tool

import asyncio
from fastmcp import Client

async def main():
    async with Client("http://localhost:8000/mcp") as client:
        tools = await client.list_tools()
        result = await client.call_tool("add", {"a": 2, "b": 3})
        print(result)

asyncio.run(main())

Client infers the transport from what you pass: an http URL uses HTTP, a .py path spawns a stdio subprocess, a server object runs in-memory.

Test a server without a network or subprocessin-memory-testing

import asyncio
from fastmcp import FastMCP, Client

mcp = FastMCP("Demo")

@mcp.tool
def add(a: int, b: int) -> int:
    return a + b

async def test_add():
    async with Client(mcp) as client:
        result = await client.call_tool("add", {"a": 1, "b": 2})
        assert result.data == 3

asyncio.run(test_add())

Passing the server object to Client runs the full protocol in-memory. This is the intended pytest pattern and needs no ports or fixtures.

Compose servers by mountingmount-subserver

from fastmcp import FastMCP

weather = FastMCP("Weather")

@weather.tool
def forecast(city: str) -> str:
    return f"Sunny in {city}"

main = FastMCP("Main")
main.mount(weather, prefix="weather")
# tool is now exposed as weather_forecast

Mounting prefixes tool names with underscore separators. Older tutorials show mount(prefix, server) argument order; current versions take the server first.

Return a controlled error to the clienttool-error-handling

from fastmcp import FastMCP
from fastmcp.exceptions import ToolError

mcp = FastMCP("Demo")

@mcp.tool
def divide(a: float, b: float) -> float:
    if b == 0:
        raise ToolError("Cannot divide by zero.")
    return a / b

ToolError messages are always sent to the client. Other exceptions may be masked depending on the server's error-masking settings, so use ToolError for anything the LLM should read.

Run any server file with the fastmcp CLIrun-with-cli

# server.py contains: mcp = FastMCP("Demo")
fastmcp run server.py:mcp

# or serve over HTTP on a port
fastmcp run server.py:mcp --transport http --port 8000

The CLI ignores your __main__ block and calls run() itself with the flags you give it, which surprises people who set transport in code.

Alternatives

PackageRegistryPick it when
mcpPyPIYou want the official Anthropic-governed Python SDK with the slowest-moving, most conservative API
fastapi-mcpPyPIYou already have a FastAPI app and just want its endpoints exposed as MCP tools
@modelcontextprotocol/sdknpmYour server or client is TypeScript, not Python