Fix breaking change: restore output_schema=False compatibility (#1482)

This commit is contained in:
Jeremiah Lowin 2025-08-13 18:07:50 -04:00 committed by GitHub
commit 905a7872c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 242 additions and 66 deletions

View file

@ -73,7 +73,10 @@
{
"group": "Essentials",
"icon": "cube",
"pages": ["servers/server", "deployment/running-server"]
"pages": [
"servers/server",
"deployment/running-server"
]
},
{
"group": "Core Components",
@ -116,7 +119,10 @@
{
"group": "Essentials",
"icon": "cube",
"pages": ["clients/client", "clients/transports"]
"pages": [
"clients/client",
"clients/transports"
]
},
{
"group": "Core Operations",
@ -142,7 +148,10 @@
{
"group": "Authentication",
"icon": "user-shield",
"pages": ["clients/auth/oauth", "clients/auth/bearer"]
"pages": [
"clients/auth/oauth",
"clients/auth/bearer"
]
}
]
},
@ -188,12 +197,17 @@
},
{
"anchor": "What's New",
"pages": ["updates", "changelog"]
"pages": [
"updates",
"changelog"
]
},
{
"anchor": "Community",
"icon": "users",
"pages": ["community/showcase"]
"pages": [
"community/showcase"
]
}
]
},

View file

@ -7,7 +7,7 @@ sidebarTitle: transports
## Functions
### `infer_transport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L904" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `infer_transport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L903" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
infer_transport(transport: ClientTransport | FastMCP | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str) -> ClientTransport
@ -161,61 +161,61 @@ transports like Python, Node, Uvx, etc.
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]
```
#### `connect` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L357" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `connect` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L356" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
connect(self, **session_kwargs: Unpack[SessionKwargs]) -> ClientSession | None
```
#### `disconnect` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L408" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `disconnect` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L407" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
disconnect(self)
```
#### `close` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L423" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `close` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L422" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
close(self)
```
### `PythonStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L432" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `PythonStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L431" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running Python scripts.
### `FastMCPStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L478" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `FastMCPStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L477" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running FastMCP servers using the FastMCP CLI.
### `NodeStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L505" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `NodeStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L504" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running Node.js scripts.
### `UvStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L547" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `UvStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L546" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running commands via the uv tool.
### `UvxStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L604" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `UvxStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L603" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running commands via the uvx tool.
### `NpxStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L668" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `NpxStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L667" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running commands via the npx tool.
### `FastMCPTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L730" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `FastMCPTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L729" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
In-memory transport for FastMCP servers.
@ -228,13 +228,13 @@ tests or scenarios where client and server run in the same runtime.
**Methods:**
#### `connect_session` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L749" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `connect_session` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L748" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]
```
### `MCPConfigTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L784" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `MCPConfigTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L783" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for connecting to one or more MCP servers defined in an MCPConfig.
@ -287,7 +287,7 @@ async with client:
**Methods:**
#### `connect_session` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L856" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `connect_session` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L855" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]

View file

@ -7,7 +7,7 @@ sidebarTitle: tool
## Functions
### `default_serializer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L55" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `default_serializer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L58" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
default_serializer(data: Any) -> str
@ -15,17 +15,17 @@ default_serializer(data: Any) -> str
## Classes
### `ToolResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L59" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ToolResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L62" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
**Methods:**
#### `to_mcp_result` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L90" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `to_mcp_result` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L93" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
to_mcp_result(self) -> list[ContentBlock] | tuple[list[ContentBlock], dict[str, Any]]
```
### `Tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L98" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `Tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L101" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Internal tool registration info.
@ -33,34 +33,34 @@ Internal tool registration info.
**Methods:**
#### `enable` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L116" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `enable` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L119" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
enable(self) -> None
```
#### `disable` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L124" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `disable` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L127" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
disable(self) -> None
```
#### `to_mcp_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L132" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `to_mcp_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L135" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
to_mcp_tool(self, **overrides: Any) -> MCPTool
```
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L157" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L160" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
from_function(fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT = NotSet, serializer: Callable[[Any], str] | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool
from_function(fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: Callable[[Any], str] | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool
```
Create a Tool from a function.
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L185" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L188" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@ -75,26 +75,26 @@ implemented by subclasses.
(list of ContentBlocks, dict of structured output).
#### `from_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L198" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `from_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L201" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
from_tool(cls, tool: Tool) -> TransformedTool
```
### `FunctionTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L232" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `FunctionTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L235" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
**Methods:**
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L236" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L239" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
from_function(cls, fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT = NotSet, serializer: Callable[[Any], str] | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool
from_function(cls, fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: Callable[[Any], str] | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool
```
Create a Tool from a function.
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L284" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L297" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@ -103,11 +103,11 @@ run(self, arguments: dict[str, Any]) -> ToolResult
Run the tool with arguments.
### `ParsedFunction` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L330" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ParsedFunction` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L343" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
**Methods:**
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L338" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L351" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
from_function(cls, fn: Callable[..., Any], exclude_args: list[str] | None = None, validate: bool = True, wrap_non_object_output_schema: bool = True) -> ParsedFunction

View file

@ -7,7 +7,7 @@ sidebarTitle: tool_transform
## Functions
### `forward` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L35" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `forward` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L37" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
forward(**kwargs) -> ToolResult
@ -36,7 +36,7 @@ tool has args `a` and `b`, and an `transform_args` was provided that maps `x` to
- `TypeError`: If provided arguments don't match the transformed schema.
### `forward_raw` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L65" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `forward_raw` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L67" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
forward_raw(**kwargs) -> ToolResult
@ -62,7 +62,7 @@ y=2)` will call the parent tool with `x=1` and `y=2`.
- `RuntimeError`: If called outside a transformed tool context.
### `apply_transformations_to_tools` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L904" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `apply_transformations_to_tools` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L915" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
apply_transformations_to_tools(tools: dict[str, Tool], transformations: dict[str, ToolTransformConfig]) -> dict[str, Tool]
@ -75,7 +75,7 @@ are left unchanged.
## Classes
### `ArgTransform` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L92" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ArgTransform` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L94" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Configuration for transforming a parent tool's argument.
@ -137,7 +137,7 @@ ArgTransform(name="new_name", description="New desc", default=None, type=int)
```
### `ArgTransformConfig` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L206" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ArgTransformConfig` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L208" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A model for requesting a single argument transform.
@ -145,7 +145,7 @@ A model for requesting a single argument transform.
**Methods:**
#### `to_arg_transform` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L224" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `to_arg_transform` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L226" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
to_arg_transform(self) -> ArgTransform
@ -154,7 +154,7 @@ to_arg_transform(self) -> ArgTransform
Convert the argument transform to a FastMCP argument transform.
### `TransformedTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L230" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `TransformedTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L232" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A tool that is transformed from another tool.
@ -171,7 +171,7 @@ inherited from the parent tool but can be overridden or disabled.
**Methods:**
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L257" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L259" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@ -190,10 +190,10 @@ functions.
- ToolResult object containing content and optional structured output.
#### `from_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L362" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `from_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L364" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
from_tool(cls, tool: Tool, name: str | None = None, title: str | None | NotSetT = NotSet, description: str | None | NotSetT = NotSet, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | None | NotSetT = NotSet, output_schema: dict[str, Any] | None | NotSetT = NotSet, serializer: Callable[[Any], str] | None | NotSetT = NotSet, meta: dict[str, Any] | None | NotSetT = NotSet, enabled: bool | None = None) -> TransformedTool
from_tool(cls, tool: Tool, name: str | None = None, title: str | None | NotSetT = NotSet, description: str | None | NotSetT = NotSet, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | None | NotSetT = NotSet, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: Callable[[Any], str] | None | NotSetT = NotSet, meta: dict[str, Any] | None | NotSetT = NotSet, enabled: bool | None = None) -> TransformedTool
```
Create a transformed tool from a parent tool.
@ -272,7 +272,7 @@ async def custom_output(**kwargs) -> ToolResult:
```
### `ToolTransformConfig` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L858" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ToolTransformConfig` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L869" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Provides a way to transform a tool.
@ -280,7 +280,7 @@ Provides a way to transform a tool.
**Methods:**
#### `apply` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L890" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `apply` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L901" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
apply(self, tool: Tool) -> TransformedTool

View file

@ -346,8 +346,7 @@ class StdioTransport(ClientTransport):
) -> AsyncIterator[ClientSession]:
try:
await self.connect(**session_kwargs)
assert self._session is not None
yield self._session
yield cast(ClientSession, self._session)
finally:
if not self.keep_alive:
await self.disconnect()

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import inspect
import warnings
from collections.abc import Callable
from dataclasses import dataclass
from typing import (
@ -8,6 +9,7 @@ from typing import (
Annotated,
Any,
Generic,
Literal,
TypeVar,
get_type_hints,
)
@ -18,6 +20,7 @@ from mcp.types import ContentBlock, TextContent, ToolAnnotations
from mcp.types import Tool as MCPTool
from pydantic import Field, PydanticSchemaGenerationError
import fastmcp
from fastmcp.server.dependencies import get_context
from fastmcp.utilities.components import FastMCPComponent
from fastmcp.utilities.json_schema import compress_schema
@ -162,7 +165,7 @@ class Tool(FastMCPComponent):
tags: set[str] | None = None,
annotations: ToolAnnotations | None = None,
exclude_args: list[str] | None = None,
output_schema: dict[str, Any] | None | NotSetT = NotSet,
output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet,
serializer: Callable[[Any], str] | None = None,
meta: dict[str, Any] | None = None,
enabled: bool | None = None,
@ -204,7 +207,7 @@ class Tool(FastMCPComponent):
description: str | None | NotSetT = NotSet,
tags: set[str] | None = None,
annotations: ToolAnnotations | None | NotSetT = NotSet,
output_schema: dict[str, Any] | None | NotSetT = NotSet,
output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet,
serializer: Callable[[Any], str] | None = None,
meta: dict[str, Any] | None | NotSetT = NotSet,
transform_args: dict[str, ArgTransform] | None = None,
@ -242,7 +245,7 @@ class FunctionTool(Tool):
tags: set[str] | None = None,
annotations: ToolAnnotations | None = None,
exclude_args: list[str] | None = None,
output_schema: dict[str, Any] | None | NotSetT = NotSet,
output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet,
serializer: Callable[[Any], str] | None = None,
meta: dict[str, Any] | None = None,
enabled: bool | None = None,
@ -255,16 +258,26 @@ class FunctionTool(Tool):
raise ValueError("You must provide a name for lambda functions")
if isinstance(output_schema, NotSetT):
output_schema = parsed_fn.output_schema
final_output_schema = parsed_fn.output_schema
elif output_schema is False:
output_schema = None
# Handle False as deprecated synonym for None (deprecated in 2.11.4)
if fastmcp.settings.deprecation_warnings:
warnings.warn(
"Passing output_schema=False is deprecated. Use output_schema=None instead.",
DeprecationWarning,
stacklevel=2,
)
final_output_schema = None
else:
# At this point output_schema is not NotSetT and not False, so it must be dict | None
final_output_schema = output_schema
# Note: explicit schemas (dict) are used as-is without auto-wrapping
# Validate that explicit schemas are object type for structured content
if output_schema is not None and isinstance(output_schema, dict):
if output_schema.get("type") != "object":
if final_output_schema is not None and isinstance(final_output_schema, dict):
if final_output_schema.get("type") != "object":
raise ValueError(
f'Output schemas must have "type" set to "object" due to MCP spec limitations. Received: {output_schema!r}'
f'Output schemas must have "type" set to "object" due to MCP spec limitations. Received: {final_output_schema!r}'
)
return cls(
@ -273,7 +286,7 @@ class FunctionTool(Tool):
title=title,
description=description or parsed_fn.description,
parameters=parsed_fn.input_schema,
output_schema=output_schema,
output_schema=final_output_schema,
annotations=annotations,
tags=tags or set(),
serializer=serializer,

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import inspect
import warnings
from collections.abc import Callable
from contextvars import ContextVar
from dataclasses import dataclass
from typing import Annotated, Any, Literal
from typing import Annotated, Any, Literal, cast
import pydantic_core
from mcp.types import ToolAnnotations
@ -12,6 +13,7 @@ from pydantic import ConfigDict
from pydantic.fields import Field
from pydantic.functional_validators import BeforeValidator
import fastmcp
from fastmcp.tools.tool import ParsedFunction, Tool, ToolResult, _convert_to_content
from fastmcp.utilities.components import _convert_set_default_none
from fastmcp.utilities.json_schema import compress_schema
@ -369,7 +371,7 @@ class TransformedTool(Tool):
transform_fn: Callable[..., Any] | None = None,
transform_args: dict[str, ArgTransform] | None = None,
annotations: ToolAnnotations | None | NotSetT = NotSet,
output_schema: dict[str, Any] | None | NotSetT = NotSet,
output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet,
serializer: Callable[[Any], str] | None | NotSetT = NotSet,
meta: dict[str, Any] | None | NotSetT = NotSet,
enabled: bool | None = None,
@ -471,8 +473,8 @@ class TransformedTool(Tool):
if output_schema is NotSet:
# Use smart fallback: try custom function, then parent
if transform_fn is not None:
assert parsed_fn is not None
final_output_schema = parsed_fn.output_schema
# parsed fn is not none here
final_output_schema = cast(ParsedFunction, parsed_fn).output_schema
if final_output_schema is None:
# Check if function returns ToolResult - if so, don't fall back to parent
return_annotation = inspect.signature(
@ -484,16 +486,25 @@ class TransformedTool(Tool):
final_output_schema = tool.output_schema
else:
final_output_schema = tool.output_schema
elif output_schema is False:
# Handle False as deprecated synonym for None (deprecated in 2.11.4)
if fastmcp.settings.deprecation_warnings:
warnings.warn(
"Passing output_schema=False is deprecated. Use output_schema=None instead.",
DeprecationWarning,
stacklevel=2,
)
final_output_schema = None
else:
assert isinstance(output_schema, dict | None)
final_output_schema = output_schema
final_output_schema = cast(dict | None, output_schema)
if transform_fn is None:
# User wants pure transformation - use forwarding_fn as the main function
final_fn = forwarding_fn
final_schema = schema
else:
assert parsed_fn is not None
# parsed fn is not none here
parsed_fn = cast(ParsedFunction, parsed_fn)
# User provided custom function - merge schemas
final_fn = transform_fn

View file

@ -0,0 +1,139 @@
"""Test deprecated output_schema=False behavior (deprecated in 2.11.4)."""
import warnings
import pytest
from fastmcp import FastMCP
from fastmcp.tools import Tool
class TestDeprecatedOutputSchemaFalse:
"""Test that output_schema=False is deprecated but still works."""
async def test_tool_decorator_output_schema_false_deprecated(self):
"""Test that @mcp.tool(output_schema=False) shows deprecation warning."""
mcp = FastMCP()
with pytest.warns(
DeprecationWarning, match="output_schema=False is deprecated"
):
@mcp.tool(output_schema=False) # type: ignore[arg-type]
def simple_tool() -> int:
"""A simple tool."""
return 42
# Verify the tool was created with None as output_schema
tool = mcp._tool_manager._tools["simple_tool"]
assert tool.output_schema is None
async def test_tool_from_function_output_schema_false_deprecated(self):
"""Test that Tool.from_function(output_schema=False) shows deprecation warning."""
def my_function() -> str:
"""A simple function."""
return "hello"
with pytest.warns(
DeprecationWarning, match="output_schema=False is deprecated"
):
tool = Tool.from_function(my_function, output_schema=False) # type: ignore[arg-type]
# Verify the tool was created with None as output_schema
assert tool.output_schema is None
async def test_tool_from_tool_output_schema_false_deprecated(self):
"""Test that Tool.from_tool(output_schema=False) shows deprecation warning."""
# Create a parent tool
def parent_function() -> dict[str, str]:
"""A parent function."""
return {"status": "ok"}
parent_tool = Tool.from_function(parent_function)
with pytest.warns(
DeprecationWarning, match="output_schema=False is deprecated"
):
transformed_tool = Tool.from_tool(parent_tool, output_schema=False) # type: ignore[arg-type]
# Verify the tool was created with None as output_schema
assert transformed_tool.output_schema is None
async def test_output_schema_false_functionality_preserved(self):
"""Test that output_schema=False still works functionally like output_schema=None."""
mcp = FastMCP()
# Create two tools - one with False, one with None
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
@mcp.tool(output_schema=False) # type: ignore[arg-type]
def tool_with_false() -> dict[str, str]:
"""Tool with output_schema=False."""
return {"result": "false"}
@mcp.tool(output_schema=None)
def tool_with_none() -> dict[str, str]:
"""Tool with output_schema=None."""
return {"result": "none"}
# Both should have None as output_schema
assert mcp._tool_manager._tools["tool_with_false"].output_schema is None
assert mcp._tool_manager._tools["tool_with_none"].output_schema is None
# Both should work the same way
result_false = await mcp._tool_manager._tools["tool_with_false"].run({})
result_none = await mcp._tool_manager._tools["tool_with_none"].run({})
# Both should return structured content for dict-like objects
assert result_false.structured_content == {"result": "false"}
assert result_none.structured_content == {"result": "none"}
async def test_output_schema_false_with_scalar_return(self):
"""Test that output_schema=False works with scalar returns (no structured content)."""
mcp = FastMCP()
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
@mcp.tool(output_schema=False) # type: ignore[arg-type]
def scalar_tool() -> int:
"""Tool returning a scalar."""
return 42
tool = mcp._tool_manager._tools["scalar_tool"]
assert tool.output_schema is None
result = await tool.run({})
# Scalar values don't produce structured content
assert result.structured_content is None
assert len(result.content) == 1
assert result.content[0].text == "42" # type: ignore[attr-defined]
async def test_transform_with_output_schema_false(self):
"""Test that transformation with output_schema=False still works."""
# Create a parent tool
def parent_function(x: int) -> dict[str, int]:
"""A parent function."""
return {"value": x * 2}
parent_tool = Tool.from_function(parent_function)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
# Transform with output_schema=False
transformed = Tool.from_tool(
parent_tool,
name="doubled",
output_schema=False, # type: ignore[arg-type]
)
assert transformed.output_schema is None
# Tool should still work
result = await transformed.run({"x": 5})
assert result.structured_content == {"value": 10}