Remove 3.x-era compatibility shims (#4661)

* Remove 3.x-era compatibility shims

* Require response_type in ctx.elicit()

* Name the utilities path for the two non-re-exported auth helpers

* Point sampling handler migration at its submodule
This commit is contained in:
Jeremiah Lowin 2026-07-27 14:59:43 -04:00 committed by GitHub
commit ea7fb8cb2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 150 additions and 285 deletions

View file

@ -178,9 +178,16 @@ The proxy, OpenAPI, and app integrations moved to their permanent homes, and the
| `fastmcp.experimental.server.openapi` | `fastmcp.server.providers.openapi` |
| `fastmcp.experimental.utilities.openapi` | `fastmcp.utilities.openapi` |
| `fastmcp.server.apps`, `fastmcp.server.app` | `fastmcp.apps` (e.g. `AppConfig`) or `fastmcp` (`FastMCPApp`) |
| `Tool` / `ToolResult` from `fastmcp.tools.tool` | `fastmcp.tools` |
| `Resource` from `fastmcp.resources.resource` | `fastmcp.resources` |
| `Prompt` / `Message` from `fastmcp.prompts.prompt` | `fastmcp.prompts` |
| `FunctionTool` / `ParsedFunction` / `tool` from `fastmcp.tools.tool` | `fastmcp.tools.function_tool` |
| `FunctionResource` / `resource` from `fastmcp.resources.resource` | `fastmcp.resources.function_resource` |
| `FunctionPrompt` / `prompt` from `fastmcp.prompts.prompt` | `fastmcp.prompts.function_prompt` |
| `OpenAISamplingHandler` from `fastmcp.experimental.sampling.handlers` | `fastmcp.client.sampling.handlers.openai` |
| `AuthCheck` / `AuthContext` / `require_scopes` / `require_roles` / `restrict_tag` / `run_auth_checks` from `fastmcp.server.auth.authorization` | `fastmcp.server.auth` |
| `run_auth_checks_with_shortfall` / `scope_requirements` from `fastmcp.server.auth.authorization` | `fastmcp.utilities.authorization` |
| `SkillsProvider` | `SkillsDirectoryProvider` from `fastmcp.server.providers.skills` |
Two renames in the same family are worth calling out because they have no compatibility alias. The response-caching wrapper models lost a spelling typo — `CachableToolResult`, `CachablePromptResult`, and their siblings became `CacheableToolResult`, `CacheablePromptResult`, etc. — so an import of the old spelling from `fastmcp.server.middleware.caching` raises `ImportError`. And `PromptToolMiddleware` / `ResourceToolMiddleware` are gone in favor of the `PromptsAsTools` / `ResourcesAsTools` transforms from `fastmcp.server.transforms` (the `ToolInjectionMiddleware` base class is retained).
@ -204,14 +211,25 @@ Two of these replacements are not exact behavioral swaps. `create_proxy` takes i
`import_server` → `mount` is the one row here that is not a mechanical swap, because the two never had the same semantics. `import_server` took a **one-time static snapshot** — it copied the child's tools, resources, and prompts at call time, with no live link, and did not run the child's lifespan or middleware. `mount` is a **live composition** — it holds a live link to the child and runs the child's lifespan and middleware. After switching, later changes to the child become visible through the parent, the child's lifespan runs with the parent's (entered when the server starts, held until it stops — not per request), and the child's middleware runs on the operations delegated to it. If you depended on the frozen-copy behavior (a stable snapshot, no child lifecycle), there is no drop-in replacement: register the child's components on the parent directly instead of composing the two servers.
### Removed tool and decorator parameters
### Removed parameters and settings
Two `@tool` parameters and two settings are gone:
Several parameters and settings that warned in 3.x are gone:
- **Tool `serializer=`** is removed from `@tool` / `mcp.tool()`, `Tool.from_function`, `Tool.from_tool`, and the OpenAPI tool. Return a `ToolResult` from your tool for full control over serialization instead.
- **Tool `exclude_args=`** is removed. Hide a parameter from the tool schema by injecting it instead: give it a `Depends(factory)` default (from `fastmcp.dependencies`), where `factory` is a callable returning the value the argument used to carry. An injected parameter never appears in the tool's schema, which is what `exclude_args` was for.
- **The `decorator_mode` setting** (`FASTMCP_DECORATOR_MODE`) and its `"object"` mode are removed. Decorators always return your original function with metadata attached; reach the component object through the server (`await mcp.get_tool("name")`) rather than off the decorated function.
- **`StreamableHttpTransport(sse_read_timeout=...)`** is removed — it was a no-op under the SDK v2 client. Set the read timeout through the public `Client(transport, timeout=...)` (a `timedelta` or float seconds), or reach for a custom `httpx_client_factory` when you need finer control. (`SSETransport` still accepts `sse_read_timeout`.)
- **`ctx.elicit()` now requires `response_type`.** Omitting it (or passing `None`) has warned since 3.2 and now raises `TypeError`. The empty-object schema it produced gave clients nothing to render, and some showed an empty, non-functional form. Pass a type describing what you expect back — `bool` is the right answer for a confirmation:
```python
# Before
result = await ctx.elicit("Approve this action?")
# After
result = await ctx.elicit("Approve this action?", response_type=bool)
```
This is the server-authoring API only. Client elicitation handlers still receive `response_type=None` for URL requests and for empty schemas sent by other servers — that contract is unchanged.
## Behavior changes to verify

View file

@ -187,9 +187,9 @@ async def confirm_purchase(ctx: Context) -> str:
These arguments only apply when FastMCP is adding the wrapper. For structured responses (`BaseModel`, dataclass, `TypedDict`), set the metadata on the individual fields via `Field(title=..., description=...)` — passing `response_title` or `response_description` alongside a model type raises `TypeError`.
### Empty Responses
### Confirmations
Passing `None` as the response type creates an empty-object schema and returns an accepted result with `data == {}`. This form is deprecated because some clients render empty forms poorly; prefer an explicit response type such as `bool` for confirmations.
`response_type` is required. When all you want is a yes/no answer, ask for a `bool` rather than an empty schema — an empty schema gives the client nothing to render, and some clients show an empty, non-functional form.
```python
@mcp.tool

View file

@ -722,7 +722,7 @@ Schema generation works for most common types including basic types, collections
For complete control over tool responses, return a `ToolResult` object. This gives you explicit control over all aspects of the tool's output: traditional content, structured data, and metadata.
```python
from fastmcp.tools.tool import ToolResult
from fastmcp.tools import ToolResult
from mcp_types import TextContent
@mcp.tool
@ -788,7 +788,7 @@ When you need custom serialization (like YAML, Markdown tables, or specialized f
```python
import yaml
from fastmcp import FastMCP
from fastmcp.tools.tool import ToolResult
from fastmcp.tools import ToolResult
mcp = FastMCP("MyServer")

View file

@ -250,7 +250,7 @@ Here's a minimal example:
from fastmcp.experimental.transforms.code_mode import CodeMode
from fastmcp.experimental.transforms.code_mode import GetToolCatalog, GetSchemas
from fastmcp.server.context import Context
from fastmcp.tools.tool import Tool
from fastmcp.tools import Tool
def list_all_tools(get_catalog: GetToolCatalog) -> Tool:
async def list_tools(ctx: Context) -> str:

View file

@ -118,7 +118,7 @@ Create custom transforms by subclassing `Transform` and overriding the methods y
```python
from collections.abc import Sequence
from fastmcp.server.transforms import Transform, GetToolNext
from fastmcp.tools.tool import Tool
from fastmcp.tools import Tool
class TagFilter(Transform):
"""Filter tools to only those with specific tags."""