Trim fastmcp.types to FastMCP-unique types (#4584)

* Trim fastmcp.types to FastMCP-unique types only

fastmcp.types re-exported 29 mcp_types symbols verbatim, which was
pointless indirection users had to discover. It now holds only Textarea,
the one type FastMCP actually defines; everything else imports from
mcp_types directly. These mirrors were added during unreleased SDK v2
migration work and never shipped, so this is not a breaking change.

* Keep historical mcp.types import in v2/v3 migration examples
This commit is contained in:
Jeremiah Lowin 2026-07-21 09:45:41 -04:00 committed by GitHub
commit 611a35861d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 60 additions and 165 deletions

View file

@ -216,7 +216,7 @@ import qrcode
from fastmcp import FastMCP
from fastmcp.apps import AppConfig, ResourceCSP
from fastmcp.tools import ToolResult
from fastmcp.types import ImageContent
from mcp_types import ImageContent
mcp = FastMCP("QR Code Server")

View file

@ -21,7 +21,7 @@ Request a rendered prompt with `get_prompt()`:
async with client:
# Simple prompt without arguments
result = await client.get_prompt("welcome_message")
# result -> fastmcp.types.GetPromptResult
# result -> mcp_types.GetPromptResult
# Access the generated messages
for message in result.messages:
@ -143,5 +143,5 @@ For complete control, use `get_prompt_mcp()` which returns the full MCP protocol
```python
async with client:
result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
# result -> fastmcp.types.GetPromptResult
# result -> mcp_types.GetPromptResult
```

View file

@ -106,5 +106,5 @@ For complete control, use `read_resource_mcp()` which returns the full MCP proto
```python
async with client:
result = await client.read_resource_mcp("resource://example")
# result -> fastmcp.types.ReadResourceResult
# result -> mcp_types.ReadResourceResult
```

View file

@ -181,7 +181,7 @@ Install the Google Gemini handler with `pip install 'fastmcp[gemini]'`.
When you provide a `sampling_handler`, FastMCP automatically advertises full sampling capabilities to the server, including tool support. To disable tool support for simpler handlers:
```python
from fastmcp.types import SamplingCapability
from mcp_types import SamplingCapability
client = Client(
"my_mcp_server.py",

View file

@ -80,7 +80,7 @@ async with client:
Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). FastMCP exclusive.
</ResponseField>
<ResponseField name=".content" type="list[fastmcp.types.ContentBlock]">
<ResponseField name=".content" type="list[mcp_types.ContentBlock]">
Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.).
</ResponseField>
@ -173,7 +173,7 @@ For complete control, use `call_tool_mcp()` which returns the raw MCP protocol o
```python
async with client:
result = await client.call_tool_mcp("my_tool", {"param": "value"})
# result -> fastmcp.types.CallToolResult
# result -> mcp_types.CallToolResult
if result.is_error:
print(f"Tool failed: {result.content}")

View file

@ -1426,7 +1426,7 @@ Prompt functions now use `Message` instead of `mcp.types.PromptMessage`:
```python
# v2.x
from fastmcp.types import PromptMessage, TextContent
from mcp.types import PromptMessage, TextContent
@mcp.prompt
def my_prompt() -> PromptMessage:

View file

@ -28,9 +28,13 @@ The `mcp.types` module no longer exists. Any `from mcp.types import X` or `impor
### `fastmcp.types` is the stable home — Bridged
<Note>
Superseded before release — see "`fastmcp.types` trimmed to FastMCP-unique types only" below. This section documents the re-export set as it existed mid-migration; none of it ever shipped.
</Note>
FastMCP re-exports the protocol types users are most likely to touch from `fastmcp.types`, sourced from `mcp_types` (the `mcp` root package lacks most of them):
```python
```python test="skip"
from fastmcp.types import TextContent, Tool, ToolAnnotations, ErrorData
```
@ -38,6 +42,18 @@ The re-export set is deliberately limited to names that trace to a documented us
*Verify:* `fastmcp_slim/fastmcp/types.py` `__all__`.
### `fastmcp.types` trimmed to FastMCP-unique types only — Absorbed (post-review cleanup)
The re-export set above never shipped in a release, so it was cut before 4.0 rather than deprecated. `fastmcp.types` now holds only types FastMCP defines itself — `Textarea` — and every bare `mcp_types` mirror (`TextContent`, `Tool`, `ToolAnnotations`, `ErrorData`, and the rest of the 29-name list) is gone. Code that imported those from `fastmcp.types` now imports them from `mcp_types` directly:
```python
from mcp_types import TextContent, Tool, ToolAnnotations, ErrorData
```
Because `fastmcp.types.__all__` was `["Textarea"]` as of the last stable release (v3.4.4) and the mirrors were added only in this unreleased migration work, removing them breaks no released user — there is no bridge or deprecation warning to write.
*Verify:* `fastmcp_slim/fastmcp/types.py` `__all__` (back down to `["Textarea"]`).
### camelCase field reads are bridged — Bridged (deprecated)
Objects FastMCP hands back — results of `client.list_tools()`, `client.call_tool_mcp()`, `client.read_resource()`, and the parameter objects passed to sampling and elicitation handlers — are SDK v2 objects with snake_case fields. A compatibility bridge installed at import time routes the old camelCase names to their snake_case fields, warning once per read:
@ -405,7 +421,7 @@ A FastMCP server can emit SEP-2549 freshness hints so a caching client (`fastmcp
### Elicitation on the modern protocol (SEP-2322), guard form — New (opt-in feature)
A tool can gather client input across rounds on a `2026-07-28` call by returning an `InputRequiredResult` (see [Elicitation on the modern protocol](/servers/elicitation#elicitation-on-the-modern-protocol)). Each round is a complete request→response cycle: the tool re-runs per round and reads the client's answers off two new `Context` properties, `ctx.input_responses` (`None` on the first round) and `ctx.request_state` (the echoed opaque state) — thin passthroughs matching the SDK's mcpserver semantics. This is the modern-era elicitation path the earlier per-feature matrix flagged as "MRTR rewrite pending"; it mirrors the SDK's base guard model exactly (tool re-runs, checks whether answers are present, returns to ask for more), with no FastMCP-invented resolver or annotation layer. `fastmcp.types` gains `InputRequiredResult`, `ElicitRequest`, and `ElicitRequestFormParams` for authoring these requests. The `request_state` channel is sealed by the framework, not the author: FastMCP installs the SDK's `RequestStateBoundary` middleware on its low-level server, which seals every outgoing `request_state` and unseals and verifies every inbound echo before a tool runs — so a tool only ever sees plaintext and a tampered, expired, or foreign token is rejected with a frozen wire error. `FastMCP(request_state_security=RequestStateSecurity(keys=[...]))` supplies shared keys for multi-replica deployments; omitted, each process seals under an ephemeral key (correct single-process). Returning this result on a handshake-era (≤ 2025-11-25) connection raises a clear era error naming the mismatch rather than failing as a generic invalid result. The client half (`fastmcp.Client` at `mode="auto"`) drives the loop through its existing elicitation/sampling/roots handlers, capped by `input_required_max_rounds`.
A tool can gather client input across rounds on a `2026-07-28` call by returning an `InputRequiredResult` (see [Elicitation on the modern protocol](/servers/elicitation#elicitation-on-the-modern-protocol)). Each round is a complete request→response cycle: the tool re-runs per round and reads the client's answers off two new `Context` properties, `ctx.input_responses` (`None` on the first round) and `ctx.request_state` (the echoed opaque state) — thin passthroughs matching the SDK's mcpserver semantics. This is the modern-era elicitation path the earlier per-feature matrix flagged as "MRTR rewrite pending"; it mirrors the SDK's base guard model exactly (tool re-runs, checks whether answers are present, returns to ask for more), with no FastMCP-invented resolver or annotation layer. For authoring these requests, `InputRequiredResult`, `ElicitRequest`, and `ElicitRequestFormParams` import from `mcp_types`. The `request_state` channel is sealed by the framework, not the author: FastMCP installs the SDK's `RequestStateBoundary` middleware on its low-level server, which seals every outgoing `request_state` and unseals and verifies every inbound echo before a tool runs — so a tool only ever sees plaintext and a tampered, expired, or foreign token is rejected with a frozen wire error. `FastMCP(request_state_security=RequestStateSecurity(keys=[...]))` supplies shared keys for multi-replica deployments; omitted, each process seals under an ephemeral key (correct single-process). Returning this result on a handshake-era (≤ 2025-11-25) connection raises a clear era error naming the mismatch rather than failing as a generic invalid result. The client half (`fastmcp.Client` at `mode="auto"`) drives the loop through its existing elicitation/sampling/roots handlers, capped by `input_required_max_rounds`.
*Verify:* `fastmcp_slim/fastmcp/server/context.py` (`input_responses`/`request_state` properties), `fastmcp_slim/fastmcp/server/low_level.py` (`RequestStateBoundary` install), `fastmcp_slim/fastmcp/server/server.py` (`request_state_security` param), `fastmcp_slim/fastmcp/server/mixins/mcp_operations.py` (`_on_call_tool` input-required passthrough + era gate), `fastmcp_slim/fastmcp/tools/base.py` (`InputRequiredToolResult`), `tests/server/test_mrtr_guards.py`.

View file

@ -171,7 +171,7 @@ Prompt functions now use FastMCP's `Message` class instead of `mcp.types.PromptM
```python
# Before
from fastmcp.types import PromptMessage, TextContent
from mcp.types import PromptMessage, TextContent
@mcp.prompt
def my_prompt() -> PromptMessage:

View file

@ -43,21 +43,15 @@ fastmcp.settings.mcp_camelcase_compat = False
See [Settings](/more/settings) for the full reference.
### Imports have a stable home
### Protocol types moved to `mcp_types`
The `mcp.types` module no longer exists. FastMCP re-exports the protocol types you're most likely to use — `TextContent`, `ImageContent`, `Tool`, `ErrorData`, `Icon`, `PromptMessage`, `SamplingMessage`, `ToolAnnotations`, and around two dozen others — from `fastmcp.types`. Update your imports to point there:
The `mcp.types` module no longer exists. Every protocol type — `TextContent`, `ImageContent`, `Tool`, `ErrorData`, `Icon`, `PromptMessage`, `SamplingMessage`, `ToolAnnotations`, notification and request wrapper types like `ToolListChangedNotification`, and everything else — now lives in the standalone `mcp_types` package. Update your imports to point there:
```python
from fastmcp.types import TextContent, Tool, ToolAnnotations
from mcp_types import TextContent, Tool, ToolAnnotations
```
For protocol types FastMCP does not re-export (notification and request wrapper types like `ToolListChangedNotification` or `ServerNotification`), import them from `mcp_types` directly:
```python
import mcp_types
notification = mcp_types.ToolListChangedNotification()
```
`fastmcp.types` still exists, but holds only types FastMCP defines itself (currently just `Textarea`, used to render a multiline textarea in form-based UIs) — it does not re-export protocol types.
### `McpError` has an alias
@ -90,7 +84,7 @@ Three things are on you.
ModuleNotFoundError: No module named 'mcp.types'
```
The raw message gives no hint toward the fix, so if you see it after upgrading, this is why. Switch to `from fastmcp.types import X` for the common types, or `import mcp_types` for the rest.
The raw message gives no hint toward the fix, so if you see it after upgrading, this is why. Switch to `from mcp_types import X`.
**`McpError` construction.** The v1 pattern of wrapping an `ErrorData` and passing it positionally fails under SDK v2 with:

View file

@ -51,9 +51,9 @@ Also: if prompts return raw dicts like `{"role": "user", "content": "..."}`, the
The MCP SDK's FastMCP 1.0 silently coerced dicts; standalone FastMCP requires typed returns.
STEP 4 — OTHER MCP IMPORTS (only if importing from mcp.* directly):
FastMCP now builds on MCP SDK v2, which removed the `mcp.types` module — protocol types live in the standalone `mcp_types` package. FastMCP re-exports the common ones from `fastmcp.types`. Update any `from mcp.types import X` to `from fastmcp.types import X` (or `import mcp_types`). Prefer FastMCP's own APIs where equivalents exist:
- fastmcp.types.TextContent for tool returns → just return plain Python values (str, int, dict, etc.)
- fastmcp.types.ImageContent → fastmcp.utilities.types.Image
FastMCP now builds on MCP SDK v2, which removed the `mcp.types` module — protocol types live in the standalone `mcp_types` package. Update any `from mcp.types import X` to `from mcp_types import X`. Prefer FastMCP's own APIs where equivalents exist:
- mcp_types.TextContent for tool returns → just return plain Python values (str, int, dict, etc.)
- mcp_types.ImageContent → fastmcp.utilities.types.Image
- from mcp.server.stdio import stdio_server → not needed, mcp.run() handles transport
STEP 5 — DECORATORS (only if treating decorated functions as objects):
@ -113,7 +113,7 @@ def debug(error: str) -> list[Message]:
### Other `mcp.*` Imports
FastMCP now builds on MCP SDK v2. The `mcp.types` module no longer exists — protocol types moved to a standalone `mcp_types` package, and the field names were renamed from camelCase to snake_case (`inputSchema` → `input_schema`, `mimeType` → `mime_type`, and so on). FastMCP re-exports the types you're most likely to use from `fastmcp.types`, so update `from mcp.types import X` to `from fastmcp.types import X`. For the full picture, see [Upgrading from FastMCP 3](/getting-started/upgrading/from-fastmcp-3).
FastMCP now builds on MCP SDK v2. The `mcp.types` module no longer exists — protocol types moved to a standalone `mcp_types` package, and the field names were renamed from camelCase to snake_case (`inputSchema` → `input_schema`, `mimeType` → `mime_type`, and so on). Update `from mcp.types import X` to `from mcp_types import X`. For the full picture, see [Upgrading from FastMCP 3](/getting-started/upgrading/from-fastmcp-3).
Where FastMCP provides its own API for the same thing, it's worth switching over:
@ -124,7 +124,7 @@ Where FastMCP provides its own API for the same thing, it's worth switching over
| `mcp.types.PromptMessage(...)` | `from fastmcp.prompts import Message` |
| `from mcp.server.stdio import stdio_server` | Not needed — `mcp.run()` handles transport |
For protocol types without a FastMCP equivalent, import them from `fastmcp.types` when re-exported there, otherwise from `mcp_types` directly.
For protocol types without a FastMCP equivalent, import them from `mcp_types` directly.
### Decorated Functions

View file

@ -95,7 +95,7 @@ The connector must be explicitly enabled in each chat session through Developer
Use `annotations=ToolAnnotations(readOnlyHint=True)` to skip confirmation prompts for read-only tools:
```python
from fastmcp.types import ToolAnnotations
from mcp_types import ToolAnnotations
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
def get_status() -> str:

View file

@ -412,7 +412,7 @@ The following tool books a flight across three rounds: it asks for a destination
```python
from fastmcp import FastMCP, Context
from fastmcp.types import InputRequiredResult, ElicitRequest, ElicitRequestFormParams
from mcp_types import InputRequiredResult, ElicitRequest, ElicitRequestFormParams
mcp = FastMCP("Booking Server")

View file

@ -15,7 +15,7 @@ Icons provide visual representations for your MCP servers and components, helpin
Icons use the standard MCP Icon type from the MCP protocol specification. Each icon specifies a source URL or data URI, and optionally includes MIME type, size, and theme information.
```python
from fastmcp.types import Icon
from mcp_types import Icon
icon = Icon(
src="https://example.com/icon.png",
@ -37,7 +37,7 @@ Add icons and a website URL to your server for display in client applications. M
```python
from fastmcp import FastMCP
from fastmcp.types import Icon
from mcp_types import Icon
mcp = FastMCP(
name="WeatherService",
@ -66,7 +66,7 @@ Icons can be added to individual tools, resources, resource templates, and promp
### Tool Icons
```python
from fastmcp.types import Icon
from mcp_types import Icon
@mcp.tool(
icons=[Icon(src="https://example.com/calculator-icon.png")]
@ -121,7 +121,7 @@ Supply two icons with complementary `theme` values and the client picks the one
```python
from fastmcp import FastMCP
from fastmcp.types import Icon
from mcp_types import Icon
mcp = FastMCP(
name="WeatherService",
@ -135,7 +135,7 @@ mcp = FastMCP(
The same field works on tools, resources, resource templates, and prompts:
```python
from fastmcp.types import Icon
from mcp_types import Icon
@mcp.tool(
icons=[
@ -155,7 +155,7 @@ Omitting `theme` means the icon is assumed suitable for any theme. That's the ri
For small icons or when you want to embed the icon directly without external dependencies, use data URIs. This approach eliminates the need for hosting and ensures the icon is always available.
```python
from fastmcp.types import Icon
from mcp_types import Icon
from fastmcp.utilities.types import Image
# SVG icon as data URI
@ -175,7 +175,7 @@ def my_tool() -> str:
FastMCP provides the `Image` utility class to convert local image files into data URIs.
```python
from fastmcp.types import Icon
from mcp_types import Icon
from fastmcp.utilities.types import Image
# Generate a data URI from a local image file

View file

@ -102,7 +102,7 @@ Use model preferences when different tasks benefit from different model characte
For requests that need conversational context, construct a list of `SamplingMessage` objects representing the conversation history. Each message has a `role` ("user" or "assistant") and `content` (a `TextContent` object).
```python
from fastmcp.types import SamplingMessage, TextContent
from mcp_types import SamplingMessage, TextContent
from fastmcp import FastMCP, Context
mcp = FastMCP()
@ -372,7 +372,7 @@ Use `sample_step()` when you need to:
By default, `sample_step()` executes any tool calls and includes the results in the history. Call it in a loop, passing the updated history each time, until a stop condition is met.
```python
from fastmcp.types import SamplingMessage
from mcp_types import SamplingMessage
from fastmcp import FastMCP, Context
mcp = FastMCP()
@ -425,7 +425,7 @@ The contents of `step.history` depend on `execute_tools`:
Set `execute_tools=False` to handle tool execution yourself. When disabled, `step.history` contains the user message and the assistant's response with tool calls—but no tool results. You execute the tools and append the results as a user message.
```python
from fastmcp.types import SamplingMessage, ToolResultContent, TextContent
from mcp_types import SamplingMessage, ToolResultContent, TextContent
from fastmcp import FastMCP, Context
mcp = FastMCP()

View file

@ -723,7 +723,7 @@ For complete control over tool responses, return a `ToolResult` object. This giv
```python
from fastmcp.tools.tool import ToolResult
from fastmcp.types import TextContent
from mcp_types import TextContent
@mcp.tool
def advanced_tool() -> ToolResult:
@ -944,7 +944,7 @@ Annotations serve several purposes in client applications:
You can add annotations to a tool using the `annotations` parameter in the `@mcp.tool` decorator. FastMCP accepts either a plain dict or `ToolAnnotations`; the examples below use `ToolAnnotations` for consistency and stronger editor/type support.
```python
from fastmcp.types import ToolAnnotations
from mcp_types import ToolAnnotations
@mcp.tool(
annotations=ToolAnnotations(
@ -983,7 +983,7 @@ Mark a tool as read-only when it retrieves data, performs calculations, or check
```python
from fastmcp import FastMCP
from fastmcp.types import ToolAnnotations
from mcp_types import ToolAnnotations
mcp = FastMCP("Data Server")

View file

@ -11,10 +11,10 @@ from functools import wraps
from typing import Any
import yaml
from mcp_types import TextContent
from fastmcp import Client, FastMCP
from fastmcp.tools import ToolResult
from fastmcp.types import TextContent
def with_serializer(serializer: Callable[[Any], str]):

View file

@ -9,10 +9,11 @@ It illustrates the pattern:
import asyncio
from mcp_types import TextContent
from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.server import create_proxy
from fastmcp.types import TextContent
class EchoService:

View file

@ -1,7 +1,7 @@
from mcp_types import ToolAnnotations
from phue import Bridge
from fastmcp import FastMCP
from fastmcp.types import ToolAnnotations
from smart_home.lights.server import lights_mcp
from smart_home.settings import settings

View file

@ -7,12 +7,12 @@
from typing import Annotated, Any, Literal, TypedDict
from mcp_types import ToolAnnotations
from phue.exceptions import PhueException
from pydantic import Field
from typing_extensions import NotRequired
from fastmcp import FastMCP
from fastmcp.types import ToolAnnotations
from smart_home.lights.hue_utils import _get_bridge, handle_phue_error

View file

@ -22,10 +22,11 @@ Requires the `docket` extra (included in dev dependencies).
import asyncio
from dataclasses import dataclass
from mcp_types import TextContent
from fastmcp import Context, FastMCP
from fastmcp.client import Client
from fastmcp.server.elicitation import AcceptedElicitation
from fastmcp.types import TextContent
mcp = FastMCP("Task Elicitation Demo")

View file

@ -21,11 +21,10 @@ from pathlib import Path
from typing import Annotated
import cyclopts
from mcp_types import GetTaskResult
from mcp_types import GetTaskResult, TextContent
from rich.console import Console
from fastmcp.client import Client
from fastmcp.types import TextContent
console = Console()
app = cyclopts.App(name="tasks-client", help="FastMCP Tasks Example Client")

View file

@ -22,93 +22,6 @@ from __future__ import annotations
from typing import Annotated
from mcp_types import (
Annotations as Annotations,
)
from mcp_types import (
AudioContent as AudioContent,
)
from mcp_types import (
BlobResourceContents as BlobResourceContents,
)
from mcp_types import (
CallToolResult as CallToolResult,
)
from mcp_types import (
Completion as Completion,
)
from mcp_types import (
ContentBlock as ContentBlock,
)
from mcp_types import (
CreateMessageResult as CreateMessageResult,
)
from mcp_types import (
ElicitRequest as ElicitRequest,
)
from mcp_types import (
ElicitRequestFormParams as ElicitRequestFormParams,
)
from mcp_types import (
EmbeddedResource as EmbeddedResource,
)
from mcp_types import (
ErrorData as ErrorData,
)
from mcp_types import (
GetPromptResult as GetPromptResult,
)
from mcp_types import (
Icon as Icon,
)
from mcp_types import (
ImageContent as ImageContent,
)
from mcp_types import (
InputRequiredResult as InputRequiredResult,
)
from mcp_types import (
Prompt as Prompt,
)
from mcp_types import (
PromptMessage as PromptMessage,
)
from mcp_types import (
ReadResourceResult as ReadResourceResult,
)
from mcp_types import (
Resource as Resource,
)
from mcp_types import (
ResourceLink as ResourceLink,
)
from mcp_types import (
ResourceTemplate as ResourceTemplate,
)
from mcp_types import (
Root as Root,
)
from mcp_types import (
SamplingCapability as SamplingCapability,
)
from mcp_types import (
SamplingMessage as SamplingMessage,
)
from mcp_types import (
TextContent as TextContent,
)
from mcp_types import (
TextResourceContents as TextResourceContents,
)
from mcp_types import (
Tool as Tool,
)
from mcp_types import (
ToolAnnotations as ToolAnnotations,
)
from mcp_types import (
ToolResultContent as ToolResultContent,
)
from pydantic import Field
Textarea = Annotated[str, Field(json_schema_extra={"format": "textarea"})]
@ -119,34 +32,5 @@ Produces `"format": "textarea"` in the JSON Schema, which
"""
__all__ = [
"Annotations",
"AudioContent",
"BlobResourceContents",
"CallToolResult",
"Completion",
"ContentBlock",
"CreateMessageResult",
"ElicitRequest",
"ElicitRequestFormParams",
"EmbeddedResource",
"ErrorData",
"GetPromptResult",
"Icon",
"ImageContent",
"InputRequiredResult",
"Prompt",
"PromptMessage",
"ReadResourceResult",
"Resource",
"ResourceLink",
"ResourceTemplate",
"Root",
"SamplingCapability",
"SamplingMessage",
"TextContent",
"TextResourceContents",
"Textarea",
"Tool",
"ToolAnnotations",
"ToolResultContent",
]