fastmcp
FrameworkPrefectHQ/fastmcp
Fast, Pythonic framework for building MCP servers and clients for LLMs.
Overview
FastMCP is a Python framework that simplifies building servers and clients for the Model Context Protocol (MCP). It provides automatic schema generation, validation, and documentation for tools, resources, and prompts. Designed for LLM integration, it powers the majority of MCP servers across languages.
README Preview
\n\n\n\n\n \n \n \n\n\n# FastMCP 🚀\n\nMove fast and make things.\n\n*Made with 💙 by [Prefect](https://www.prefect.io/)*\n\n[](https://gofastmcp.com)\n[](https://discord.gg/uu8dJCgttd)\n[](https://pypi.org/project/fastmcp)\n[](https://github.com/PrefectHQ/fastmcp/actions/workflows/run-tests.yml)\n[](https://github.com/PrefectHQ/fastmcp/blob/main/LICENSE)\n\n\n\n\n---\n\nThe [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) connects LLMs to tools and data. FastMCP gives you everything you need to go from prototype to production:\n\n```python\nfrom fastmcp import FastMCP\n\nmcp = FastMCP("Demo 🚀")\n\n@mcp.tool\ndef add(a: int, b: int) -> int:\n """Add two numbers"""\n return a + b\n\nif __name__ == "__main__":\n mcp.run()\n```\n\n## Why FastMCP\n\nBuilding an effective MCP application is harder than it looks. FastMCP handles all of it. Declare a tool with a Python function, and the schema, validation, and documentation are generated automatically. Connect to a server with a URL, and transport negotiation, authentication, and protocol lifecycle are managed for you. You focus on your logic, and the MCP part just works: **with FastMCP, best practices are built in.**\n\n**That's why FastMCP is the standard framework for working with MCP.** FastMCP 1.0 was incorporated into the official MCP Python SDK in 2024. Today, the actively maintained standalone project is downloaded a million times a day, and some version of FastMCP powers 70% of MCP servers across all languages.\n\nFastMCP has three pillars:\n\n\n\n\n\n\nServers\n\nExpose tools, resources, and prompts to LLMs.\n\n\n\n\nApps\n\nGive your tools interactive UIs rendered directly in the conversation.\n\n\n\n\nClients\n\nConnect to any MCP server — local or remote, programmatic or CLI.\n\n\n\n\n**[Servers](https://gofastmcp.com/servers/server)** wrap your Python functions into MCP-compliant tools, resources, and prompts. **[Clients](https://gofastmcp.com/clients/client)** connect to any servFAQ (5)
TroubleshootingHow do I fix "'str' object has no attribute 'matches'" in FastMCP tools/list after calling /tools/{name}/disable?version=v1?
This is caused by the component-manager route passing the raw ?version= query string to Provider.enable/disable, which expects a VersionSpec object. The broken string is then used by the visibility transform, causing every later tools/list to fail.
Temporary workarounds:
1. Call the route without the query parameter:
curl -X POST http://localhost:8000/tools/mytool/disable2. If the bad state has already been stored, restart/recreate the FastMCP server.
3. If you must target a version, call the provider API directly with a
VersionSpec instead of using the HTTP route.
Permanent fix: upgrade to a FastMCP release containing the component-manager fix, or patch the route handler to convert the parameter before passing it to the provider:
raw_version = request.query_params.get("version")
version = VersionSpec(eq=raw_version) if raw_version else None
await provider.disable(name=name, version=version)Apply the same conversion in the corresponding enable route. The issue was reproduced on FastMCP
4.0.11.dev1+0ebabc0c.TroubleshootingWhy does FastMCP's client_supports_extension return False when client_capabilities.extensions contains the extension?
This affects fastmcp 4.0.5 with mcp 2.2.0 and protocol version 2026-07-28. client_supports_extension() returns early when session.client_params is None, but clientInfo is optional in per-request envelopes, so client_params may be absent even though session.client_capabilities.extensions is populated. As a workaround, inspect capabilities directly instead of calling client_supports_extension():
from fastmcp.server.dependencies import get_context
ctx = get_context()
caps = ctx.session.client_capabilities
supported = bool(
caps is not None
and caps.extensions is not None
and "io.modelcontextprotocol/ui" in caps.extensions
)
The permanent fix is to make client_supports_extension() use session.client_capabilities rather than requiring session.client_params to exist. Upgrade FastMCP once that fix is released.
TroubleshootingWhy does a FastMCP task-enabled tool return a generic "Error calling tool" instead of a validation error when an argument is missing or unknown?
This occurs in FastMCP 4.0.12.dev because task submission validated argument types but did not validate the full call shape before enqueueing the task. Missing required or unexpected arguments were only detected when the worker executed the function, producing an opaque task error, especially when mask_error_details=True.
Upgrade to a FastMCP release containing the fix "Reject missing and unexpected arguments at task submission":
pip install -U fastmcp fastmcp-tasks
After the upgrade, task-enabled tools validate arguments at submission time and return the same Pydantic validation error as synchronous tools, such as Missing required argument or an unexpected-argument error.
As a temporary diagnostic workaround, set mask_error_details=False or validate the payload against the tool's schema before calling the task-enabled tool.
TroubleshootingHow do I fix SkillsDirectoryProvider UnicodeDecodeError when a SKILL.md file is not valid UTF-8?
In FastMCP 4.0.11.dev, one invalid UTF-8 SKILL.md can prevent SkillsDirectoryProvider from loading any skills. Convert the offending file to UTF-8 or remove it from the skills directory. For a UTF-16 file, run: iconv -f UTF-16 -t UTF-8 bad/SKILL.md -o bad/SKILL.md.utf8 && mv bad/SKILL.md.utf8 bad/SKILL.md. With Python, you can detect and rewrite it using Path.read_text(encoding="utf-16").encode("utf-8"). After conversion, restart the provider; valid skills should load normally.
TroubleshootingWhy does a FastMCP parameterless tool's description include the Returns section?
This is a docstring parsing bug in FastMCP 3.4.7. parse_docstring() only uses a parser's extracted description when it also finds a parameters section. For a tool with no parameters, parsing falls back to the raw docstring, so Returns:, Yields:, and Raises: sections leak into the client-visible description. The fix updates the parser to keep the best extracted description even when no parameters are found; upgrade to a FastMCP release containing the fix for issue #4952. As a workaround, set the description explicitly:
@mcp.tool(description="Return current status.")
async def status() -> dict[str, bool]:
"""Return current status.
Returns:
A dict containing the current status.
"""
return {"ok": True}
Alternatively, remove the Returns:/Yields:/Raises: sections from the tool docstring until you upgrade.