fastmcp/docs/development/upgrade-guide.mdx
Jeremiah Lowin 78cc19b029 Auto-route Docket-dependent components through Docket execution
Replace tool timeout parameter with Docket's native Timeout dependency.
Components declaring Docket deps (Timeout, Retry, etc.) are now:
- Registered with Docket at startup
- Auto-routed through Docket even for foreground calls

Known limitations to discuss:
- Context not available in Docket workers
- Registration bug: register_with_docket still checks supports_tasks()
- Error propagation: ToolError wrapped in McpError
- Performance: every call goes through Redis+worker
2026-01-22 09:42:37 -05:00

250 lines
6 KiB
Text

---
title: Upgrade Guide
sidebarTitle: Upgrade Guide
description: Migration instructions for upgrading between FastMCP versions
icon: up
---
This guide covers breaking changes and migration steps when upgrading FastMCP.
## v3.0.0
Most servers need only one change: update your import from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP`. The sections below cover less common breaking changes.
### Breaking Changes
#### WSTransport Removed
Use `StreamableHttpTransport` instead.
#### Auth Provider Environment Variables Removed
Auth providers no longer auto-load configuration. Read them explicitly:
```python
import os
auth = GitHubProvider(
client_id=os.environ["GITHUB_CLIENT_ID"],
client_secret=os.environ["GITHUB_CLIENT_SECRET"],
)
```
#### Component enable()/disable() Moved to Server
These methods moved from component objects to the server:
```python
# Before
tool = await server.get_tool("my_tool")
tool.disable()
# After
server.disable(names={"my_tool"}, components=["tool"])
```
#### Listing Methods Return Lists
`get_tools()`, `get_resources()`, `get_prompts()`, and `get_resource_templates()` now return lists instead of dicts:
```python
# Before
tools = await server.get_tools()
tool = tools["my_tool"]
# After
tools = await server.get_tools()
tool = next((t for t in tools if t.name == "my_tool"), None)
```
#### Prompts Use Message Class
Use `Message` instead of `mcp.types.PromptMessage`:
```python
# Before
from mcp.types import PromptMessage, TextContent
@mcp.prompt
def my_prompt() -> PromptMessage:
return PromptMessage(role="user", content=TextContent(type="text", text="Hello"))
# After
from fastmcp.prompts import Message
@mcp.prompt
def my_prompt() -> Message:
return Message("Hello")
```
#### Context State Methods Are Async
`ctx.set_state()` and `ctx.get_state()` are now async. State persists across the session:
```python
# Before
ctx.set_state("key", "value")
value = ctx.get_state("key")
# After
await ctx.set_state("key", "value")
value = await ctx.get_state("key")
```
#### Server Banner Environment Variable Renamed
`FASTMCP_SHOW_CLI_BANNER` is now `FASTMCP_SHOW_SERVER_BANNER`.
#### Metadata Namespace Renamed
The FastMCP metadata namespace changed from `_fastmcp` to `fastmcp`, and metadata is now always included. The `include_fastmcp_meta` parameter has been removed from `FastMCP()` and `to_mcp_tool()`—remove any usage of this parameter.
```python
# Before
tags = tool.meta.get("_fastmcp", {}).get("tags", [])
# After
tags = tool.meta.get("fastmcp", {}).get("tags", [])
```
### Behavior Changes
#### Decorators Return Functions
Decorators now return your original function instead of a component object. This means functions stay callable for testing:
```python
@mcp.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
greet("World") # Works! Returns "Hello, World!"
```
If you relied on the old behavior (treating `greet` as a `FunctionTool`), set `FASTMCP_DECORATOR_MODE=object` for v2 compatibility.
### Deprecated Features
These still work but emit warnings. Update when convenient.
#### mount() prefix → namespace
```python
# Deprecated
main.mount(subserver, prefix="api")
# New
main.mount(subserver, namespace="api")
```
#### include_tags/exclude_tags → enable()/disable()
```python
# Deprecated
mcp = FastMCP("server", exclude_tags={"internal"})
# New
mcp = FastMCP("server")
mcp.disable(tags={"internal"})
```
#### tool_serializer → ToolResult
Return `ToolResult` from your tools for explicit serialization control instead of using the `tool_serializer` parameter.
#### add_tool_transformation() → add_transform()
```python
# Deprecated
mcp.add_tool_transformation("name", config)
# New
from fastmcp.server.transforms import ToolTransform
mcp.add_transform(ToolTransform({"name": config}))
```
#### FastMCP.as_proxy() → create_proxy()
```python
# Deprecated
proxy = FastMCP.as_proxy("http://example.com/mcp")
# New
from fastmcp.server import create_proxy
proxy = create_proxy("http://example.com/mcp")
```
## v3.0.0b2 (from v3.0.0b1)
### Breaking Changes
#### Tool `timeout` Parameter Removed
The `timeout` parameter on `@mcp.tool()` has been removed. Use Docket's `Timeout` dependency instead:
```python
# Before (v3.0.0b1)
@mcp.tool(timeout=30.0)
async def fetch_data(url: str) -> dict:
...
# After (v3.0.0b2)
from datetime import timedelta
from docket import Timeout
@mcp.tool
async def fetch_data(
url: str,
timeout: Timeout = Timeout(timedelta(seconds=30)),
) -> dict:
...
```
Benefits of the new approach:
- **Unified execution**: Timeouts work identically for foreground and background tasks
- **Additional capabilities**: Access to `Retry`, `ExponentialRetry`, `ConcurrencyLimit`
- **Auto-routing**: Components with Docket dependencies automatically route through Docket
Note: This is only breaking from beta 1 → beta 2. The `timeout` parameter didn't exist before beta 1.
## v2.14.0
### OpenAPI Parser Promotion
The experimental OpenAPI parser is now standard. Update imports:
```python
# Before
from fastmcp.experimental.server.openapi import FastMCPOpenAPI
# After
from fastmcp.server.openapi import FastMCPOpenAPI
```
### Removed Deprecated Features
- `BearerAuthProvider` → use `JWTVerifier`
- `Context.get_http_request()` → use `get_http_request()` from dependencies
- `from fastmcp import Image` → use `from fastmcp.utilities.types import Image`
- `FastMCP(dependencies=[...])` → use `fastmcp.json` configuration
- `FastMCPProxy(client=...)` → use `client_factory=lambda: ...`
- `output_schema=False` → use `output_schema=None`
## v2.13.0
### OAuth Token Key Management
The OAuth proxy now issues its own JWT tokens. For production, provide explicit keys:
```python
auth = GitHubProvider(
client_id=os.environ["GITHUB_CLIENT_ID"],
client_secret=os.environ["GITHUB_CLIENT_SECRET"],
base_url="https://your-server.com",
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=RedisStore(host="redis.example.com"),
)
```
See [OAuth Token Security](/deployment/http#oauth-token-security) for details.