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
174 lines
6.8 KiB
Plaintext
174 lines
6.8 KiB
Plaintext
---
|
|
title: Transforms Overview
|
|
sidebarTitle: Overview
|
|
description: Modify components as they flow through your server
|
|
icon: wand-magic-sparkles
|
|
---
|
|
|
|
import { VersionBadge } from '/snippets/version-badge.mdx'
|
|
|
|
<VersionBadge version="3.0.0" />
|
|
|
|
Transforms modify components as they flow from providers to clients. When a client asks "what tools do you have?", the request passes through each transform in the chain. Each transform can modify the components before passing them along.
|
|
|
|
## Mental Model
|
|
|
|
Think of transforms as filters in a pipeline. Components flow from providers through transforms to reach clients:
|
|
|
|
```
|
|
Provider → [Transform A] → [Transform B] → Client
|
|
```
|
|
|
|
When listing components, transforms receive sequences and return transformed sequences—a pure function pattern. When getting a specific component by name, transforms use a middleware pattern with `call_next`, working in reverse: mapping the client's requested name back to the original, then transforming the result.
|
|
|
|
## Built-in Transforms
|
|
|
|
FastMCP provides several transforms for common use cases:
|
|
|
|
- **[Namespace](/servers/transforms/namespace)** - Prefix component names to prevent conflicts when composing servers
|
|
- **[Tool Transformation](/servers/transforms/tool-transformation)** - Rename tools, modify descriptions, reshape arguments
|
|
- **[Enabled](/servers/visibility)** - Control which components are visible at runtime
|
|
- **[Tool Search](/servers/transforms/tool-search)** - Replace large tool catalogs with on-demand search
|
|
- **[Resources as Tools](/servers/transforms/resources-as-tools)** - Expose resources to tool-only clients
|
|
- **[Prompts as Tools](/servers/transforms/prompts-as-tools)** - Expose prompts to tool-only clients
|
|
- **[Code Mode (Experimental)](/servers/transforms/code-mode)** - Replace many tools with programmable `search` + `execute`
|
|
|
|
## Server vs Provider Transforms
|
|
|
|
Transforms can be added at two levels, each serving different purposes.
|
|
|
|
### Provider-Level Transforms
|
|
|
|
Provider transforms apply to components from a specific provider. They run first, modifying components before they reach the server level.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.providers import FastMCPProvider
|
|
from fastmcp.server.transforms import Namespace, ToolTransform
|
|
from fastmcp.tools.tool_transform import ToolTransformConfig
|
|
|
|
sub_server = FastMCP("Sub")
|
|
|
|
@sub_server.tool
|
|
def process(data: str) -> str:
|
|
return f"Processed: {data}"
|
|
|
|
# Create provider and add transforms
|
|
provider = FastMCPProvider(sub_server)
|
|
provider.add_transform(Namespace("api"))
|
|
provider.add_transform(ToolTransform({
|
|
"api_process": ToolTransformConfig(description="Process data through the API"),
|
|
}))
|
|
|
|
main = FastMCP("Main", providers=[provider])
|
|
# Tool is now: api_process with updated description
|
|
```
|
|
|
|
When using `mount()`, the returned provider reference lets you add transforms directly.
|
|
|
|
```python
|
|
main = FastMCP("Main")
|
|
mount = main.mount(sub_server, namespace="api")
|
|
mount.add_transform(ToolTransform({...}))
|
|
```
|
|
|
|
### Server-Level Transforms
|
|
|
|
Server transforms apply to all components from all providers. They run after provider transforms, seeing the already-transformed names.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.transforms import Namespace
|
|
|
|
mcp = FastMCP("Server", transforms=[Namespace("v1")])
|
|
|
|
@mcp.tool
|
|
def greet(name: str) -> str:
|
|
return f"Hello, {name}!"
|
|
|
|
# All tools become v1_toolname
|
|
```
|
|
|
|
Server-level transforms are useful for API versioning or applying consistent naming across your entire server.
|
|
|
|
### Transform Order
|
|
|
|
Transforms stack in the order they're added. The first transform added is innermost (closest to the provider), and subsequent transforms wrap it.
|
|
|
|
```python
|
|
from fastmcp.server.providers import FastMCPProvider
|
|
from fastmcp.server.transforms import Namespace, ToolTransform
|
|
from fastmcp.tools.tool_transform import ToolTransformConfig
|
|
|
|
provider = FastMCPProvider(server)
|
|
provider.add_transform(Namespace("api")) # Applied first
|
|
provider.add_transform(ToolTransform({ # Sees namespaced names
|
|
"api_verbose_name": ToolTransformConfig(name="short"),
|
|
}))
|
|
|
|
# Flow: "verbose_name" -> "api_verbose_name" -> "short"
|
|
```
|
|
|
|
When a client requests "short", the transforms reverse the mapping: ToolTransform maps "short" to "api_verbose_name", then Namespace strips the prefix to find "verbose_name" in the provider.
|
|
|
|
## Custom Transforms
|
|
|
|
Create custom transforms by subclassing `Transform` and overriding the methods you need.
|
|
|
|
```python
|
|
from collections.abc import Sequence
|
|
from fastmcp.server.transforms import Transform, GetToolNext
|
|
from fastmcp.tools.tool import Tool
|
|
|
|
class TagFilter(Transform):
|
|
"""Filter tools to only those with specific tags."""
|
|
|
|
def __init__(self, required_tags: set[str]):
|
|
self.required_tags = required_tags
|
|
|
|
async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
|
|
return [t for t in tools if t.tags & self.required_tags]
|
|
|
|
async def get_tool(self, name: str, call_next: GetToolNext) -> Tool | None:
|
|
tool = await call_next(name)
|
|
if tool and tool.tags & self.required_tags:
|
|
return tool
|
|
return None
|
|
```
|
|
|
|
The `Transform` base class provides default implementations that pass through unchanged. Override only the methods relevant to your transform.
|
|
|
|
Each component type has two methods with different patterns:
|
|
|
|
| Method | Pattern | Purpose |
|
|
|--------|---------|---------|
|
|
| `list_tools(tools)` | Pure function | Transform the sequence of tools |
|
|
| `get_tool(name, call_next)` | Middleware | Transform lookup by name |
|
|
| `list_resources(resources)` | Pure function | Transform the sequence of resources |
|
|
| `get_resource(uri, call_next)` | Middleware | Transform lookup by URI |
|
|
| `list_resource_templates(templates)` | Pure function | Transform the sequence of templates |
|
|
| `get_resource_template(uri, call_next)` | Middleware | Transform template lookup by URI |
|
|
| `list_prompts(prompts)` | Pure function | Transform the sequence of prompts |
|
|
| `get_prompt(name, call_next)` | Middleware | Transform lookup by name |
|
|
|
|
List methods receive sequences directly and return transformed sequences. Get methods use `call_next` for routing flexibility—when a client requests "new_name", your transform maps it back to "original_name" before calling `call_next()`.
|
|
|
|
```python
|
|
class PrefixTransform(Transform):
|
|
def __init__(self, prefix: str):
|
|
self.prefix = prefix
|
|
|
|
async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
|
|
return [t.model_copy(update={"name": f"{self.prefix}_{t.name}"}) for t in tools]
|
|
|
|
async def get_tool(self, name: str, call_next: GetToolNext) -> Tool | None:
|
|
# Reverse the prefix to find the original
|
|
if not name.startswith(f"{self.prefix}_"):
|
|
return None
|
|
original = name[len(self.prefix) + 1:]
|
|
tool = await call_next(original)
|
|
if tool:
|
|
return tool.model_copy(update={"name": name})
|
|
return None
|
|
```
|