60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
231 lines
7.6 KiB
Plaintext
231 lines
7.6 KiB
Plaintext
---
|
|
title: Tool Transformation
|
|
sidebarTitle: Tool Transformation
|
|
description: Modify tool schemas - rename, reshape arguments, and customize behavior
|
|
icon: wrench
|
|
---
|
|
|
|
import { VersionBadge } from '/snippets/version-badge.mdx'
|
|
|
|
<VersionBadge version="3.0.0" />
|
|
|
|
Tool transformation lets you modify tool schemas - renaming tools, changing descriptions, adjusting tags, and reshaping argument schemas. FastMCP provides two mechanisms that share the same configuration options but differ in timing.
|
|
|
|
**Deferred transformation** with `ToolTransform` applies modifications when tools flow through a transform chain. Use this for tools from mounted servers, proxies, or other providers where you don't control the source directly.
|
|
|
|
**Immediate transformation** with `Tool.from_tool()` creates a modified tool object right away. Use this when you have direct access to a tool and want to transform it before registration.
|
|
|
|
## ToolTransform
|
|
|
|
The `ToolTransform` class is a transform that modifies tools as they flow through a provider. Provide a dictionary mapping original tool names to their transformation configuration.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.transforms import ToolTransform
|
|
from fastmcp.tools.tool_transform import ToolTransformConfig
|
|
|
|
mcp = FastMCP("Server")
|
|
|
|
@mcp.tool
|
|
def verbose_internal_data_fetcher(query: str) -> str:
|
|
"""Fetches data from the internal database."""
|
|
return f"Results for: {query}"
|
|
|
|
# Rename the tool to something simpler
|
|
mcp.add_transform(ToolTransform({
|
|
"verbose_internal_data_fetcher": ToolTransformConfig(
|
|
name="search",
|
|
description="Search the database.",
|
|
)
|
|
}))
|
|
|
|
# Clients see "search" with the cleaner description
|
|
```
|
|
|
|
`ToolTransform` is useful when you want to modify tools from mounted or proxied servers without changing the original source.
|
|
|
|
## Tool.from_tool()
|
|
|
|
Use `Tool.from_tool()` when you have the tool object and want to create a transformed version for registration.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.tools import Tool, tool
|
|
from fastmcp.tools.tool_transform import ArgTransform
|
|
|
|
# Create a tool without registering it
|
|
@tool
|
|
def search(q: str, limit: int = 10) -> list[str]:
|
|
"""Search for items."""
|
|
return [f"Result {i} for {q}" for i in range(limit)]
|
|
|
|
# Transform it before registration
|
|
better_search = Tool.from_tool(
|
|
search,
|
|
name="find_items",
|
|
description="Find items matching your search query.",
|
|
transform_args={
|
|
"q": ArgTransform(
|
|
name="query",
|
|
description="The search terms to look for.",
|
|
),
|
|
},
|
|
)
|
|
|
|
mcp = FastMCP("Server")
|
|
mcp.add_tool(better_search)
|
|
```
|
|
|
|
The standalone `@tool` decorator (from `fastmcp.tools`) creates a Tool object without registering it to any server. This separates creation from registration, letting you transform tools before deciding where they go.
|
|
|
|
## Modification Options
|
|
|
|
Both mechanisms support the same modifications.
|
|
|
|
**Tool-level options:**
|
|
|
|
| Option | Description |
|
|
|--------|-------------|
|
|
| `name` | New name for the tool |
|
|
| `description` | New description |
|
|
| `title` | Human-readable title |
|
|
| `tags` | Set of tags for categorization |
|
|
| `annotations` | MCP ToolAnnotations |
|
|
| `meta` | Custom metadata dictionary |
|
|
| `enabled` | Whether the tool is visible to clients (default `True`) |
|
|
|
|
**Argument-level options** (via `ArgTransform` or `ArgTransformConfig`):
|
|
|
|
| Option | Description |
|
|
|--------|-------------|
|
|
| `name` | Rename the argument |
|
|
| `description` | New description for the argument |
|
|
| `default` | New default value |
|
|
| `default_factory` | Callable that generates a default (requires `hide=True`) |
|
|
| `hide` | Remove from client-visible schema |
|
|
| `required` | Make an optional argument required |
|
|
| `type` | Change the argument's type |
|
|
| `examples` | Example values for the argument |
|
|
|
|
## Hiding Arguments
|
|
|
|
Hide arguments to simplify the interface or inject values the client shouldn't control.
|
|
|
|
```python
|
|
from fastmcp.tools.tool_transform import ArgTransform
|
|
|
|
# Hide with a constant value
|
|
transform_args = {
|
|
"api_key": ArgTransform(hide=True, default="secret-key"),
|
|
}
|
|
|
|
# Hide with a dynamic value
|
|
import uuid
|
|
transform_args = {
|
|
"request_id": ArgTransform(hide=True, default_factory=lambda: str(uuid.uuid4())),
|
|
}
|
|
```
|
|
|
|
Hidden arguments disappear from the tool's schema. The client never sees them, but the underlying function receives the configured value.
|
|
|
|
<Warning>
|
|
`default_factory` requires `hide=True`. Visible arguments need static defaults that can be represented in JSON Schema.
|
|
</Warning>
|
|
|
|
## Renaming Arguments
|
|
|
|
Rename arguments to make them more intuitive for LLMs or match your API conventions.
|
|
|
|
```python
|
|
from fastmcp.tools import Tool, tool
|
|
from fastmcp.tools.tool_transform import ArgTransform
|
|
|
|
@tool
|
|
def search(q: str, n: int = 10) -> list[str]:
|
|
"""Search for items."""
|
|
return []
|
|
|
|
better_search = Tool.from_tool(
|
|
search,
|
|
transform_args={
|
|
"q": ArgTransform(name="query", description="Search terms"),
|
|
"n": ArgTransform(name="max_results", description="Maximum results to return"),
|
|
},
|
|
)
|
|
```
|
|
|
|
## Custom Transform Functions
|
|
|
|
For advanced scenarios, provide a `transform_fn` that intercepts tool execution. The function can validate inputs, modify outputs, or add custom logic while still calling the original tool via `forward()`.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.tools import Tool, tool
|
|
from fastmcp.tools.tool_transform import forward, ArgTransform
|
|
|
|
@tool
|
|
def divide(a: float, b: float) -> float:
|
|
"""Divide a by b."""
|
|
return a / b
|
|
|
|
async def safe_divide(numerator: float, denominator: float) -> float:
|
|
if denominator == 0:
|
|
raise ValueError("Cannot divide by zero")
|
|
return await forward(numerator=numerator, denominator=denominator)
|
|
|
|
safe_division = Tool.from_tool(
|
|
divide,
|
|
name="safe_divide",
|
|
transform_fn=safe_divide,
|
|
transform_args={
|
|
"a": ArgTransform(name="numerator"),
|
|
"b": ArgTransform(name="denominator"),
|
|
},
|
|
)
|
|
|
|
mcp = FastMCP("Server")
|
|
mcp.add_tool(safe_division)
|
|
```
|
|
|
|
The `forward()` function handles argument mapping automatically. Call it with the transformed argument names, and it maps them back to the original function's parameters.
|
|
|
|
For direct access to the original function without mapping, use `forward_raw()` with the original parameter names.
|
|
|
|
## Context-Aware Tool Factories
|
|
|
|
You can write functions that act as "factories," generating specialized versions of a tool for different contexts. For example, create a `get_my_data` tool for the current user by hiding the `user_id` parameter and providing it automatically.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.tools import Tool, tool
|
|
from fastmcp.tools.tool_transform import ArgTransform
|
|
|
|
# A generic tool that requires a user_id
|
|
@tool
|
|
def get_user_data(user_id: str, query: str) -> str:
|
|
"""Fetch data for a specific user."""
|
|
return f"Data for user {user_id}: {query}"
|
|
|
|
|
|
def create_user_tool(user_id: str) -> Tool:
|
|
"""Factory that creates a user-specific version of get_user_data."""
|
|
return Tool.from_tool(
|
|
get_user_data,
|
|
name="get_my_data",
|
|
description="Fetch your data. No need to specify a user ID.",
|
|
transform_args={
|
|
"user_id": ArgTransform(hide=True, default=user_id),
|
|
},
|
|
)
|
|
|
|
|
|
# Create a server with a tool customized for the current user
|
|
mcp = FastMCP("User Server")
|
|
current_user_id = "user-123" # e.g., from auth context
|
|
mcp.add_tool(create_user_tool(current_user_id))
|
|
|
|
# Clients see "get_my_data(query: str)" — user_id is injected automatically
|
|
```
|
|
|
|
This pattern is useful for multi-tenant servers where each connection gets tools pre-configured with their identity, or for wrapping generic tools with environment-specific defaults.
|