The FastMCP 3 beta has been running for a few weeks now, and we’ve been busy. The architecture (providers, transforms, components) has held up well under real-world usage, which is exactly what a beta period is for. But we haven’t been sitting around waiting for bug reports.
Beta 2 ships a substantial batch of new features that round out the framework for a stable release. The biggest theme is the CLI: FastMCP can now discover, query, and invoke tools on any MCP server from the terminal, and even generate standalone CLI scripts from tool schemas. We’ve also landed CIMD (the DCR replacement), protocol-level MCP Apps support, response size limiting, and background task elicitation.
For a refresher on the beta 1 architecture and features, see the What’s New in FastMCP 3.0 post.
Get Started
pip install fastmcp==3.0.0b2The CLI
Four new commands that collectively make fastmcp useful for a lot more than running servers.
fastmcp list and fastmcp call
You can now query and invoke tools on any MCP server directly from the terminal. Remote URLs, local Python files, MCPConfig JSON files, arbitrary stdio commands. Anything.
# Discover tools on a serverfastmcp list http://localhost:8000/mcpfastmcp list server.py
# Call a toolfastmcp call server.py greet name=Worldfastmcp call http://localhost:8000/mcp search query=hello limit=5Tool arguments are auto-coerced using the tool’s JSON schema, so limit=5 becomes an integer automatically. JSON objects work as positional args. OAuth fires automatically for HTTP targets that require it.
This alone is transformative for development. Instead of wiring up a client to test your server, you just call it. But the real power comes when you combine it with discovery.
fastmcp discover
fastmcp discover scans your editor configs (Claude Desktop, Claude Code, Cursor, Gemini CLI, Goose) and project-level mcp.json files for MCP server definitions. Once discovered, you reference servers by name:
# See all configured servers across your editorsfastmcp discover
# Use a server by namefastmcp list weatherfastmcp call weather get_forecast city=London
# Disambiguate with source:namefastmcp call cursor:weather get_forecast city=LondonEvery server you’ve configured in any editor is now one command away, regardless of transport.
The repo also ships a CLI skill so your coding assistant already knows how to use these commands. Drop it into your agent’s skills directory and it can discover, list, and call MCP tools on your behalf.
fastmcp generate-cli
This is the feature that made me grin when I first saw it working. fastmcp generate-cli connects to any MCP server, reads its tool schemas, and writes a standalone Python CLI script where every tool becomes a typed subcommand with flags, help text, and tab completion.
# Generate from any serverfastmcp generate-cli weather my_weather_cli.py
# Use the generated scriptpython my_weather_cli.py call-tool get_forecast --city London --days 3python my_weather_cli.py list-toolsThe insight: MCP tool schemas already contain everything a CLI framework needs. Parameter names, types, descriptions, required/optional status. The generator maps JSON Schema directly into cyclopts commands. The generated script embeds the resolved transport, so it’s self-contained. Users don’t need to know about MCP or FastMCP to use it.
This is how MCP tools escape the chatbot. Your agent’s tools become anyone’s CLI.
MCP Apps
MCP Apps is the spec extension that lets MCP servers deliver interactive UIs via sandboxed iframes. Beta 2 adds SDK-level support: extension negotiation, typed UI metadata on tools and resources, and the ui:// resource scheme.
from fastmcp import FastMCPfrom fastmcp.server.apps import ToolUI, ResourceUI
mcp = FastMCP("My Server")
# Register a UI bundle as a resource@mcp.resource("ui://dashboard/view.html")def dashboard_html() -> str: return Path("./dist/index.html").read_text()
# Tool with a UI — clients render an iframe alongside the result@mcp.tool(ui=ToolUI(resource_uri="ui://dashboard/view.html"))async def list_users() -> list[dict]: return [{"id": "1", "name": "Alice"}]
# App-only tool — visible to the UI but hidden from the model@mcp.tool(ui=ToolUI( resource_uri="ui://dashboard/view.html", visibility=["app"]))async def delete_user(id: str) -> dict: return {"deleted": True}This is the foundation. We’re shipping the protocol-level support (CSP, permissions, extension negotiation) so that when MCP clients start rendering apps, FastMCP servers are ready. The higher-level component DSL, the in-repo renderer, and the FastMCPApp class are coming in future betas.
Tools can detect whether the connected client supports apps at runtime via ctx.client_supports_extension(), which means you can serve rich structured data to app-capable clients and fall back to text for everyone else.
CIMD: Client Authentication Without DCR
CIMD (Client ID Metadata Documents) replaces Dynamic Client Registration for OAuth-authenticated MCP servers. Instead of clients registering dynamically with each server via a POST endpoint, they host a static JSON document at an HTTPS URL. That URL becomes the client’s client_id, and servers verify identity through domain ownership.
from fastmcp import Clientfrom fastmcp.client.auth import OAuth
async with Client( "https://mcp-server.example.com/mcp", auth=OAuth( client_metadata_url="https://myapp.example.com/oauth/client.json", ),) as client: await client.ping()We also ship CLI tools for generating and validating CIMD documents:
# Generate a CIMD documentfastmcp auth cimd create --name "My App" \ --redirect-uri "http://localhost:*/callback" \ --client-id "https://myapp.example.com/oauth/client.json"
# Validate a hosted documentfastmcp auth cimd validate https://myapp.example.com/oauth/client.jsonCIMD is enabled by default on OAuthProxy and all its provider subclasses (GitHub, Google, Azure, etc.). The server-side implementation includes SSRF-hardened document fetching with DNS pinning, dual redirect URI validation, HTTP cache-aware revalidation, and private_key_jwt assertion support.
ResponseLimitingMiddleware
Context window protection, built in. This middleware controls tool response sizes, preventing large outputs from overwhelming LLM context windows.
from fastmcp.server.middleware.response_limiting import ResponseLimitingMiddleware
# Limit all tool responses to 500KBmcp.add_middleware(ResponseLimitingMiddleware(max_size=500_000))
# Limit only specific toolsmcp.add_middleware(ResponseLimitingMiddleware( max_size=100_000, tools=["search", "fetch_data"],))Text responses are truncated at UTF-8 character boundaries. Structured responses (tools with output_schema) raise ToolError since truncation would corrupt the schema. Size metadata gets added to the result’s meta field for monitoring. This is the kind of production guard rail that saves you from a tool that returns a 10MB JSON blob and blows out your token budget.
Background Task Elicitation
Context now works transparently in background tasks running in Docket workers. Previously, tools running as background tasks couldn’t use ctx.elicit() because there was no active request context. Now, when a tool executes in a Docket worker, Context detects this and routes elicitation through Redis-based coordination: the task sets its status to input_required, sends a notification, and waits for the client to respond.
@mcp.tool(task=True)async def interactive_task(ctx: Context) -> str: # Works transparently in both foreground and background result = await ctx.elicit("Please provide additional input", str)
if isinstance(result, AcceptedElicitation): return f"You provided: {result.data}" return "Elicitation was declined"ctx.is_background_task and ctx.task_id are available for tools that need to branch on execution mode.
Everything Else
A few more things that shipped in beta 2:
fastmcp install goose: Generates a Goose deeplink URL and opens it, installing your server as a STDIO extension. Goose requiresuvxrather thanuv run, and the command handles the difference automatically.fastmcp install stdio: Generates fulluv runcommands for running FastMCP servers over stdio, making it easy to integrate with MCP clients that need a command string.- Expanded reload file watching: The
--reloadflag now watches JavaScript, TypeScript, HTML, CSS, config files, and media assets. Necessary for MCP Apps with frontend bundles. require_authremoved: Since configuring anAuthProvideralready rejects unauthenticated requests at the transport level,require_authwas redundant. Userequire_scopesinstead.
Beyond features, this release includes a wave of bug fixes and stability improvements across OAuth, transports, task execution, and the CLI. Seven new contributors joined FastMCP in this release alone, which is extraordinary for a beta. Full details in the release notes.
We’re getting close. The architecture is stable, the feature set is filling out, and the beta feedback has been exactly what we needed. If you’ve been waiting for the right time to try FastMCP 3, this is it.
Happy (context) engineering!
About This Beta
Install: pip install fastmcp==3.0.0b2
- Beta 1 Features: What’s New in FastMCP 3.0
- Full Documentation: gofastmcp.com
- GitHub: github.com/jlowin/fastmcp