We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide13 min read

FastMCP, the fastest route to an MCP server in Python

FastMCP builds MCP servers and clients in Python from decorators. Versions, server composition, OpenAPI integration, authentication, and testing.

FastMCP, the fastest route to an MCP server in Python

FastMCP turns an ordinary Python function into a tool available to a model. The argument schema comes from type annotations, the description from the docstring, and the transport layer, validation, and protocol compliance sit inside the library. You write the logic, the rest is attached.

Where two versions came from and why it matters

The project's history explains the confusion you will meet when hunting for material.

The first version of FastMCP proved convenient enough that it was contributed into the official Python development kit back in 2024. Code using mcp.server.fastmcp is that version, but the import path stayed on the 1.x line, which since late July 2026 receives security fixes only. In version two of the official kit the class is called MCPServer and lives in mcp.server.mcpserver, so older examples found online stop working after an upgrade.

The project kept developing as a separate package, though, and that one today carries features the built in version lacks: server composition, generating a server from an OpenAPI description, and proxying calls to other servers. A client and authentication used to be on that list, but version two of the official kit, released on 28 July 2026, added both, so they no longer decide the choice.

A practical pointer when choosing: for a simple server with a few tools, the version in the official kit suffices. For anything heading to production or serving many clients, reach for the separate package, since you would otherwise write the missing features yourself.

It also helps to know the name circulates in two ecosystems, and in TypeScript it sits on more than one thing at once. The same team publishes its own @prefecthq/fastmcp-ts library, and separately from it the npm registry holds a fastmcp package developed by different people. They are neither the same tool nor compatible interfaces, so when hunting for material, check which language and which package is meant, since code examples from one will not transfer to the other.

The stable release from late July 2026 carries version 3.4.5, and a day later the first beta of version four appeared. Its development direction responds directly to a change in the protocol itself, covered shortly.

Your first server

Code
Bash
pip install fastmcp
Code
Python
from fastmcp import FastMCP

mcp = FastMCP("orders")

@mcp.tool
def order_status(number: str) -> dict:
    """Returns order status by number in ORD-12345 format.

    Call this when the user asks about a specific order and supplied its number.
    """
    return db.orders.fetch(number)

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

That is the entire server. The argument type annotation supplies the schema, the docstring supplies the description, and the decorator registers the tool. The model sees exactly what you wrote in the docstring, so the first sentence should say what the function does and the second when to call it.

Resources and prompt templates work the same way with a different decorator.

Code
Python
@mcp.resource("terms://{section}")
def terms(section: str) -> str:
    """Contents of the named terms of service section."""
    return read_section(section)

The difference between the two is substantive rather than cosmetic. A tool has an effect, so the client usually asks the user for approval. A resource is a read, so the client can fetch it unaided. The article on the MCP protocol covers that split in more depth.

Composition and proxying

Two features set this package apart from a minimal implementation, and both solve problems that appear around your third server.

Composition lets you assemble one server from several smaller ones. Instead of thirty tools in one file you have three thematic modules, each tested separately, combined at startup. On a larger project that is the difference between maintainable code and one file nobody wants to open.

Proxying lets you expose somebody else's server under your own address, filtering tools or adding authentication along the way. That is practical when sharing a server with a team when the server itself has no access control.

Before wrapping somebody else's server, though, check whether it already carries restrictions of its own. The GitHub server splits its tools into toolsets switched by an environment variable and has a read only mode, so a proxy would mostly add another piece to maintain. The gain shows up with servers that expose everything at once and never ask who is asking.

Code
Python
from fastmcp import FastMCP

main = FastMCP("company")
main.mount(orders_server, namespace="orders")
main.mount(customers_server, namespace="customers")

When composing, settle a naming convention early. The namespace given at mount time becomes part of the tool name the model sees, so orders_status reads better than srv1_get. The older name of that argument, prefix, still works but raises a deprecation warning. Renaming later means every saved client configuration stops matching.

A separate convenience is generating a server from an existing OpenAPI description. If your API is documented, you get a tool set without writing them by hand, which at fifty endpoints saves a day of work. Do review and trim the result, though, since automatic descriptions drawn from API documentation rarely tell the model when to use a tool.

Protocol statelessness and what follows from it

The protocol specification of 28 July 2026 removed sessions from the protocol layer. That is good news for deployment, since a server scales like an ordinary stateless service, but it raises a question for applications that need state.

That is precisely what version four addresses: building stateful applications on a sessionless protocol. State stops belonging to a connection and becomes something you store deliberately and tie to an identifier carried in the request.

Practically that means one shift in thinking. Previously you could hold conversation context in the memory of the process serving a connection. Now it has to live somewhere every instance can reach: a database, a key value store, or a token passed by the client.

With existing servers, check whether they rely on connection durability. If they do, migration is usually simple but demands a deliberate decision about where state should live.

Authentication and deployment

A local server needs no authentication, since the client launches it as a subprocess. A remote server always needs it, and the library supplies mechanisms rather than leaving it to you.

Configuration covers identity providers and access control at the level of individual tools, so the same server can expose different function sets to different users. That matters with data changing tools, where reads can be broadly available and writes narrowly.

On the deployment side there is publishing from a repository with branch previews and quick rollback, plus a private registry of servers inside an organisation. That last element solves a problem arriving sooner than expected: six months in, nobody knows how many servers run in the company or who owns them.

The alternative is self deployment on any platform serving HTTP, Cloudflare for instance, or containers. Protocol statelessness means you need no client pinning to an instance.

Testing

This is the area where the separate package delivers most and, simultaneously, the one people use least.

The client built into the library lets you call your own server inside a test without launching a separate process. The test looks like an ordinary unit test while covering the whole path: tool registration, argument validation, and response shape.

Code
Python
from fastmcp import Client

async def test_status_returns_data():
    async with Client(mcp) as client:
        result = await client.call_tool("order_status", {"number": "ORD-12345"})
        assert "status" in result.data

Three things deserve testing. Whether the tool appears in the list, since a typo in the decorator throws no error. Whether validation rejects bad arguments, since the model will send them. Whether the response keeps a stable shape, since the agent builds later steps on it.

Separately, run the inspector tool before attaching the server to an assistant. It shows the tool list as the model will see it and lets you call each one by hand. That step catches bad descriptions faster than any test.

Call context and callbacks to the client

A tool running in isolation from whoever called it suffices for simple cases. A serious integration needs access to information about the call and the ability to speak back to the client.

Context injected into the function grants access to the caller's identity, to logging events visible on the client side, and to progress reporting during longer operations.

Code
Python
from fastmcp import FastMCP, Context

mcp = FastMCP("reports")

@mcp.tool
async def build_report(month: str, ctx: Context) -> str:
    """Generates a sales report for the given month in YYYY-MM format."""
    await ctx.info(f"Collecting data for {month}")
    data = await fetch_data(month)
    await ctx.report_progress(progress=50, total=100)
    return assemble_report(data)

Progress reporting matters practically, since without it an operation taking a minute looks to the user like a freeze. The client then shows a progress bar instead of silence.

A separate mechanism asks the user something mid run. If a tool needs information it did not receive in its arguments, it can request it rather than guessing or returning an error. That solves the familiar problem of operations needing confirmation or a choice among several matching records.

A third option asks the client to call a model. The server needs no provider key of its own, since it uses the one the calling application already holds. That simplifies configuration and puts the cost where it is billed anyway.

FastMCP against the alternatives

ApproachAdvantageDrawbackPick it when
FastMCP as a separate packageComposition, OpenAPI, proxying, testing toolsMore dependencies, fast moving versionsProduction server, many clients
The version in the official kitFewer dependencies, a client and authentication since version twoNo composition, OpenAPI, or proxyingSimple local server
Implementing from scratchFull control, no dependenciesYou handle the protocol and its changesUnusual environment or another language
A vendor supplied serverNo work at allScope set by the vendorIntegration with a popular service

When choosing between packages, weigh the pace of change. The separate package releases often, which gives quick access to new capabilities and calls for pinning a version in your dependency file if you would rather avoid surprises at build time.

Before writing anything, check whether a server for your service already exists. Many platforms publish their own, and writing another repeats work somebody already did.

From script to server, the usual path

Most servers come about the same way, and knowing that path shortens a first attempt.

It starts with a script doing something repetitive: pulling data from an internal system, generating a summary, checking a status. You run it by hand, and on every run you remind yourself what arguments it takes.

The first step wraps the function in a decorator and writes the docstring so a model understands when to use it. That is literally a few minutes, and the effect is immediate: you stop remembering syntax, because you describe the task in words.

The second step arrives once there are several tools. Then it pays to split them thematically and compose one server rather than piling functions into one file. While you are there, add error handling returning a readable message, since an agent copes with "no permission for this resource" and does not cope with an abort.

The third step shares the server with a team. That ends the local edition and starts a remote deployment with authentication and restricted permissions. It is also the moment tool descriptions stop being your private business, since a model reads them for several people.

The fourth step, which few consider at the start, is pruning. After six months some tools go unused and others do the same thing under two names. A quarterly review keeps the set at a size where the model still picks accurately.

Common mistakes

The first is docstrings written for a developer rather than for a model. The model reads the same text, so a sentence saying when to call the tool matters more than a description of the implementation.

The second is returning a raw API response. Three hundred lines of JSON instead of five fields raise cost and lower the accuracy of the agent's later steps.

The third is no error handling. An exception stops the agent with no information about what went wrong, while a readable message lets it try differently.

The fourth is exposing a remote server without authentication. Whatever you gave the model, you give to anyone who knows the address.

The fifth is keeping state in process memory. Under a sessionless protocol the next request may reach a different instance, so state has to live somewhere all of them can reach.

FAQ

Is FastMCP the same as the official development kit?

Partly. The project's first version was contributed into the official Python kit in 2024 and lives there as a module, though version two of the kit renamed the class to MCPServer. The separate package develops further and adds server composition, OpenAPI integration, and proxying that the built in version lacks. A client and authentication the official kit grew on its own.

Which version should I use?

For a simple local server with a few tools, the version in the official kit suffices. For a production server, a remote one, or one serving many users, reach for the separate package, since otherwise you will write those same features yourself.

Do I need a specific Python version?

The library requires Python 3.10 or newer. On older projects that can be a genuine obstacle, so check it before planning an integration.

What does protocol statelessness change?

A remote server holds no session, so every request can be served by a different instance. That eases deployment and scaling, while application state has to be stored deliberately, in a database or a token, rather than in process memory. Version four of the library addresses exactly that scenario.

How do I attach a finished server to an assistant?

You state in the client configuration how to launch it or where to find it, and the same server then works in the Claude app, in Cursor, and in agent libraries such as LangChain. That is the point of the protocol: you write the integration once.

Documentation sits at gofastmcp.com, and the source code in the GitHub repository, which the project runs under the Prefect banner.