diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx
index 359b3dbd0..8e6cc2bf5 100644
--- a/docs/python-sdk/fastmcp-server-server.mdx
+++ b/docs/python-sdk/fastmcp-server-server.mdx
@@ -10,7 +10,7 @@ FastMCP - A more ergonomic interface for MCP servers.
## Functions
-### `add_resource_prefix`
+### `add_resource_prefix`
```python
add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str
@@ -28,18 +28,27 @@ Add a prefix to a resource URI.
**Examples:**
->>> add_resource_prefix("resource://path/to/resource", "prefix")
-"resource://prefix/path/to/resource" # with new style
->>> add_resource_prefix("resource://path/to/resource", "prefix")
-"prefix+resource://path/to/resource" # with legacy style
->>> add_resource_prefix("resource:///absolute/path", "prefix")
-"resource://prefix//absolute/path" # with new style
+With new style:
+```python
+add_resource_prefix("resource://path/to/resource", "prefix")
+"resource://prefix/path/to/resource"
+```
+With legacy style:
+```python
+add_resource_prefix("resource://path/to/resource", "prefix")
+"prefix+resource://path/to/resource"
+```
+With absolute path:
+```python
+add_resource_prefix("resource:///absolute/path", "prefix")
+"resource://prefix//absolute/path"
+```
**Raises:**
- `ValueError`: If the URI doesn't match the expected protocol\://path format
-### `remove_resource_prefix`
+### `remove_resource_prefix`
```python
remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str
@@ -58,18 +67,27 @@ Returns:
**Examples:**
->>> remove_resource_prefix("resource://prefix/path/to/resource", "prefix")
-"resource://path/to/resource" # with new style
->>> remove_resource_prefix("prefix+resource://path/to/resource", "prefix")
-"resource://path/to/resource" # with legacy style
->>> remove_resource_prefix("resource://prefix//absolute/path", "prefix")
-"resource:///absolute/path" # with new style
+With new style:
+```python
+remove_resource_prefix("resource://prefix/path/to/resource", "prefix")
+"resource://path/to/resource"
+```
+With legacy style:
+```python
+remove_resource_prefix("prefix+resource://path/to/resource", "prefix")
+"resource://path/to/resource"
+```
+With absolute path:
+```python
+remove_resource_prefix("resource://prefix//absolute/path", "prefix")
+"resource:///absolute/path"
+```
**Raises:**
- `ValueError`: If the URI doesn't match the expected protocol\://path format
-### `has_resource_prefix`
+### `has_resource_prefix`
```python
has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> bool
@@ -87,12 +105,21 @@ Check if a resource URI has a specific prefix.
**Examples:**
->>> has_resource_prefix("resource://prefix/path/to/resource", "prefix")
-True # with new style
->>> has_resource_prefix("prefix+resource://path/to/resource", "prefix")
-True # with legacy style
->>> has_resource_prefix("resource://other/path/to/resource", "prefix")
+With new style:
+```python
+has_resource_prefix("resource://prefix/path/to/resource", "prefix")
+True
+```
+With legacy style:
+```python
+has_resource_prefix("prefix+resource://path/to/resource", "prefix")
+True
+```
+With other path:
+```python
+has_resource_prefix("resource://other/path/to/resource", "prefix")
False
+```
**Raises:**
- `ValueError`: If the URI doesn't match the expected protocol\://path format
@@ -140,7 +167,7 @@ Run the FastMCP server. Note this is a synchronous function.
add_middleware(self, middleware: Middleware) -> None
```
-#### `custom_route`
+#### `custom_route`
```python
custom_route(self, path: str, methods: list[str], name: str | None = None, include_in_schema: bool = True)
@@ -161,7 +188,7 @@ Starlette's reverse URL lookup feature)
- `include_in_schema`: Whether to include in OpenAPI schema, defaults to True
-#### `add_tool`
+#### `add_tool`
```python
add_tool(self, tool: Tool) -> None
@@ -176,7 +203,7 @@ with the Context type annotation. See the @tool decorator for examples.
- `tool`: The Tool instance to register
-#### `remove_tool`
+#### `remove_tool`
```python
remove_tool(self, name: str) -> None
@@ -191,19 +218,19 @@ Remove a tool from the server.
- `NotFoundError`: If the tool is not found
-#### `tool`
+#### `tool`
```python
tool(self, name_or_fn: AnyFunction) -> FunctionTool
```
-#### `tool`
+#### `tool`
```python
tool(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionTool]
```
-#### `tool`
+#### `tool`
```python
tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool
@@ -227,12 +254,37 @@ This decorator supports multiple calling patterns:
- `name`: Optional name for the tool (keyword-only, alternative to name_or_fn)
- `description`: Optional description of what the tool does
- `tags`: Optional set of tags for categorizing the tool
-- `annotations`: Optional annotations about the tool's behavior (e.g. {"is_async"\: True})
+- `annotations`: Optional annotations about the tool's behavior
- `exclude_args`: Optional list of argument names to exclude from the tool schema
- `enabled`: Optional boolean to enable or disable the tool
+**Examples:**
-#### `add_resource`
+Register a tool with a custom name:
+```python
+@server.tool
+def my_tool(x: int) -> str:
+ return str(x)
+
+# Register a tool with a custom name
+@server.tool
+def my_tool(x: int) -> str:
+ return str(x)
+
+@server.tool("custom_name")
+def my_tool(x: int) -> str:
+ return str(x)
+
+@server.tool(name="custom_name")
+def my_tool(x: int) -> str:
+ return str(x)
+
+# Direct function call
+server.tool(my_function, name="custom_name")
+```
+
+
+#### `add_resource`
```python
add_resource(self, resource: Resource) -> None
@@ -244,7 +296,7 @@ Add a resource to the server.
- `resource`: A Resource instance to add
-#### `add_template`
+#### `add_template`
```python
add_template(self, template: ResourceTemplate) -> None
@@ -256,7 +308,7 @@ Add a resource template to the server.
- `template`: A ResourceTemplate instance to add
-#### `add_resource_fn`
+#### `add_resource_fn`
```python
add_resource_fn(self, fn: AnyFunction, uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> None
@@ -276,7 +328,7 @@ has parameters, it will be registered as a template resource.
- `tags`: Optional set of tags for categorizing the resource
-#### `resource`
+#### `resource`
```python
resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate]
@@ -305,8 +357,36 @@ has parameters, it will be registered as a template resource.
- `tags`: Optional set of tags for categorizing the resource
- `enabled`: Optional boolean to enable or disable the resource
+**Examples:**
-#### `add_prompt`
+Register a resource with a custom name:
+```python
+@server.resource("resource://my-resource")
+def get_data() -> str:
+ return "Hello, world!"
+
+@server.resource("resource://my-resource")
+async get_data() -> str:
+ data = await fetch_data()
+ return f"Hello, world! {data}"
+
+@server.resource("resource://{city}/weather")
+def get_weather(city: str) -> str:
+ return f"Weather for {city}"
+
+@server.resource("resource://{city}/weather")
+def get_weather_with_context(city: str, ctx: Context) -> str:
+ ctx.info(f"Fetching weather for {city}")
+ return f"Weather for {city}"
+
+@server.resource("resource://{city}/weather")
+async def get_weather(city: str) -> str:
+ data = await fetch_weather(city)
+ return f"Weather for {city}: {data}"
+```
+
+
+#### `add_prompt`
```python
add_prompt(self, prompt: Prompt) -> None
@@ -318,19 +398,19 @@ Add a prompt to the server.
- `prompt`: A Prompt instance to add
-#### `prompt`
+#### `prompt`
```python
prompt(self, name_or_fn: AnyFunction) -> FunctionPrompt
```
-#### `prompt`
+#### `prompt`
```python
prompt(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionPrompt]
```
-#### `prompt`
+#### `prompt`
```python
prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt
@@ -356,9 +436,11 @@ Decorator to register a prompt.
tags: Optional set of tags for categorizing the prompt
enabled: Optional boolean to enable or disable the prompt
- Example:
+ Examples:
+
+ ```python
@server.prompt
- def analyze_table(table_name: str) -> list\[Message]:
+ def analyze_table(table_name: str) -> list[Message]:
schema = read_table_schema(table_name)
return [
{
@@ -369,7 +451,7 @@ Decorator to register a prompt.
]
@server.prompt()
- def analyze_with_context(table_name: str, ctx: Context) -> list\[Message]:
+ def analyze_with_context(table_name: str, ctx: Context) -> list[Message]:
ctx.info(f"Analyzing table {table_name}")
schema = read_table_schema(table_name)
return [
@@ -381,7 +463,7 @@ Decorator to register a prompt.
]
@server.prompt("custom_name")
- def analyze_file(path: str) -> list\[Message]:
+ def analyze_file(path: str) -> list[Message]:
content = await read_file(path)
return [
{
@@ -397,14 +479,15 @@ Decorator to register a prompt.
]
@server.prompt(name="custom_name")
- def another_prompt(data: str) -> list\[Message]:
+ def another_prompt(data: str) -> list[Message]:
return [{"role": "user", "content": data}]
# Direct function call
server.prompt(my_function, name="custom_name")
+ ```
-#### `sse_app`
+#### `sse_app`
```python
sse_app(self, path: str | None = None, message_path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan
@@ -418,7 +501,7 @@ Create a Starlette app for the SSE server.
- `middleware`: A list of middleware to apply to the app
-#### `streamable_http_app`
+#### `streamable_http_app`
```python
streamable_http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan
@@ -431,7 +514,7 @@ Create a Starlette app for the StreamableHTTP server.
- `middleware`: A list of middleware to apply to the app
-#### `http_app`
+#### `http_app`
```python
http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, transport: Literal['http', 'streamable-http', 'sse'] = 'http') -> StarletteWithLifespan
@@ -448,7 +531,7 @@ Create a Starlette app using the specified HTTP transport.
- A Starlette application configured with the specified transport
-#### `mount`
+#### `mount`
```python
mount(self, server: FastMCP[LifespanResultT], prefix: str | None = None, as_proxy: bool | None = None) -> None
@@ -502,7 +585,7 @@ automatically determined based on whether the server has a custom lifespan
- `prompt_separator`: Deprecated. Separator character for prompt names.
-#### `from_openapi`
+#### `from_openapi`
```python
from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, **settings: Any) -> FastMCPOpenAPI
@@ -511,7 +594,7 @@ from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route
Create a FastMCP server from an OpenAPI specification.
-#### `from_fastapi`
+#### `from_fastapi`
```python
from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, **settings: Any) -> FastMCPOpenAPI
@@ -520,7 +603,7 @@ from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap]
Create a FastMCP server from a FastAPI application.
-#### `as_proxy`
+#### `as_proxy`
```python
as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy
@@ -528,13 +611,13 @@ as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any]
Create a FastMCP proxy server for the given backend.
-The ``backend`` argument can be either an existing :class:`~fastmcp.client.Client`
-instance or any value accepted as the ``transport`` argument of
-:class:`~fastmcp.client.Client`. This mirrors the convenience of the
-``Client`` constructor.
+The `backend` argument can be either an existing `fastmcp.client.Client`
+instance or any value accepted as the `transport` argument of
+`fastmcp.client.Client`. This mirrors the convenience of the
+`fastmcp.client.Client` constructor.
-#### `from_client`
+#### `from_client`
```python
from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPProxy
@@ -543,4 +626,4 @@ from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPPr
Create a FastMCP proxy server from a FastMCP client.
-### `MountedServer`
+### `MountedServer`
diff --git a/docs/python-sdk/fastmcp-tools-tool_transform.mdx b/docs/python-sdk/fastmcp-tools-tool_transform.mdx
index 9fe9c13a4..6a7ea8ceb 100644
--- a/docs/python-sdk/fastmcp-tools-tool_transform.mdx
+++ b/docs/python-sdk/fastmcp-tools-tool_transform.mdx
@@ -18,38 +18,58 @@ descriptions, add default values, or hide them from clients while passing consta
**Examples:**
-# Rename argument 'old_name' to 'new_name'
+Rename argument 'old_name' to 'new_name'
+```python
ArgTransform(name="new_name")
+```
-# Change description only
+Change description only
+```python
ArgTransform(description="Updated description")
+```
-# Add a default value (makes argument optional)
+Add a default value (makes argument optional)
+```python
ArgTransform(default=42)
+```
-# Add a default factory (makes argument optional)
+Add a default factory (makes argument optional)
+```python
ArgTransform(default_factory=lambda: time.time())
+```
-# Change the type
+Change the type
+```python
ArgTransform(type=str)
+```
-# Hide the argument entirely from clients
+Hide the argument entirely from clients
+```python
ArgTransform(hide=True)
+```
-# Hide argument but pass a constant value to parent
+Hide argument but pass a constant value to parent
+```python
ArgTransform(hide=True, default="constant_value")
+```
-# Hide argument but pass a factory-generated value to parent
+Hide argument but pass a factory-generated value to parent
+```python
ArgTransform(hide=True, default_factory=lambda: uuid.uuid4().hex)
+```
-# Make an optional parameter required (removes any default)
+Make an optional parameter required (removes any default)
+```python
ArgTransform(required=True)
+```
-# Combine multiple transformations
+Combine multiple transformations
+```python
ArgTransform(name="new_name", description="New desc", default=None, type=int)
+```
-### `TransformedTool`
+### `TransformedTool`
A tool that is transformed from another tool.
@@ -65,7 +85,7 @@ with transformed arguments.
**Methods:**
-#### `from_tool`
+#### `from_tool`
```python
from_tool(cls, tool: Tool, name: str | None = None, description: str | None = None, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> TransformedTool
@@ -81,9 +101,9 @@ argument names.
- `name`: New name for the tool. Defaults to parent tool's name.
- `transform_args`: Optional transformations for parent tool arguments.
Only specified arguments are transformed, others pass through unchanged\:
-- str\: Simple rename
-- ArgTransform\: Complex transformation (rename/description/default/drop)
-- None\: Drop the argument
+- Simple rename (str)
+- Complex transformation (rename/description/default/drop) (ArgTransform)
+- Drop the argument (None)
- `description`: New description. Defaults to parent's description.
- `tags`: New tags. Defaults to parent's tags.
- `annotations`: New annotations. Defaults to parent's annotations.
@@ -92,17 +112,28 @@ Only specified arguments are transformed, others pass through unchanged\:
**Returns:**
- TransformedTool with the specified transformations.
-Examples:
-- # Transform specific arguments only
-- Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged
-- # Custom function with partial transforms
-- async def custom(x: int, y: int) -> str:
-result = await forward(x=x, y=y)
-return f"Custom: {result}"
-- Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"})
-- # Using **kwargs (gets all args, transformed and untransformed)
-- async def flexible(**kwargs) -> str:
-result = await forward(**kwargs)
-return f"Got: {kwargs}"
-- Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"})
+**Examples:**
+
+# Transform specific arguments only
+```python
+Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged
+```
+
+# Custom function with partial transforms
+```python
+async def custom(x: int, y: int) -> str:
+ result = await forward(x=x, y=y)
+ return f"Custom: {result}"
+
+Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"})
+```
+
+# Using **kwargs (gets all args, transformed and untransformed)
+```python
+async def flexible(**kwargs) -> str:
+ result = await forward(**kwargs)
+ return f"Got: {kwargs}"
+
+Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"})
+```
diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py
index 3a7685bfe..e3a7dd611 100644
--- a/src/fastmcp/server/server.py
+++ b/src/fastmcp/server/server.py
@@ -363,6 +363,7 @@ class FastMCP(Generic[LifespanResultT]):
return await self._resource_manager.get_resource_templates()
async def get_resource_template(self, key: str) -> ResourceTemplate:
+ """Get a registered resource template by key."""
templates = await self.get_resource_templates()
if key not in templates:
raise NotFoundError(f"Unknown resource template: {key}")
@@ -403,9 +404,12 @@ class FastMCP(Generic[LifespanResultT]):
include_in_schema: Whether to include in OpenAPI schema, defaults to True
Example:
+ Register a custom HTTP route for a health check endpoint:
+ ```python
@server.custom_route("/health", methods=["GET"])
async def health_check(request: Request) -> Response:
return JSONResponse({"status": "ok"})
+ ```
"""
def decorator(
@@ -814,15 +818,18 @@ class FastMCP(Generic[LifespanResultT]):
name: Optional name for the tool (keyword-only, alternative to name_or_fn)
description: Optional description of what the tool does
tags: Optional set of tags for categorizing the tool
- annotations: Optional annotations about the tool's behavior (e.g. {"is_async": True})
+ annotations: Optional annotations about the tool's behavior
exclude_args: Optional list of argument names to exclude from the tool schema
enabled: Optional boolean to enable or disable the tool
- Example:
+ Examples:
+ Register a tool with a custom name:
+ ```python
@server.tool
def my_tool(x: int) -> str:
return str(x)
+ # Register a tool with a custom name
@server.tool
def my_tool(x: int) -> str:
return str(x)
@@ -837,6 +844,7 @@ class FastMCP(Generic[LifespanResultT]):
# Direct function call
server.tool(my_function, name="custom_name")
+ ```
"""
if isinstance(annotations, dict):
annotations = ToolAnnotations(**annotations)
@@ -991,7 +999,9 @@ class FastMCP(Generic[LifespanResultT]):
tags: Optional set of tags for categorizing the resource
enabled: Optional boolean to enable or disable the resource
- Example:
+ Examples:
+ Register a resource with a custom name:
+ ```python
@server.resource("resource://my-resource")
def get_data() -> str:
return "Hello, world!"
@@ -1014,6 +1024,7 @@ class FastMCP(Generic[LifespanResultT]):
async def get_weather(city: str) -> str:
data = await fetch_weather(city)
return f"Weather for {city}: {data}"
+ ```
"""
# Check if user passed function directly instead of calling decorator
if inspect.isroutine(uri):
@@ -1138,7 +1149,9 @@ class FastMCP(Generic[LifespanResultT]):
tags: Optional set of tags for categorizing the prompt
enabled: Optional boolean to enable or disable the prompt
- Example:
+ Examples:
+
+ ```python
@server.prompt
def analyze_table(table_name: str) -> list[Message]:
schema = read_table_schema(table_name)
@@ -1182,6 +1195,7 @@ class FastMCP(Generic[LifespanResultT]):
# Direct function call
server.prompt(my_function, name="custom_name")
+ ```
"""
if isinstance(name_or_fn, classmethod):
@@ -1787,10 +1801,10 @@ class FastMCP(Generic[LifespanResultT]):
) -> FastMCPProxy:
"""Create a FastMCP proxy server for the given backend.
- The ``backend`` argument can be either an existing :class:`~fastmcp.client.Client`
- instance or any value accepted as the ``transport`` argument of
- :class:`~fastmcp.client.Client`. This mirrors the convenience of the
- ``Client`` constructor.
+ The `backend` argument can be either an existing `fastmcp.client.Client`
+ instance or any value accepted as the `transport` argument of
+ `fastmcp.client.Client`. This mirrors the convenience of the
+ `fastmcp.client.Client` constructor.
"""
from fastmcp.client.client import Client
from fastmcp.server.proxy import FastMCPProxy
@@ -1827,14 +1841,14 @@ class FastMCP(Generic[LifespanResultT]):
Given a component, determine if it should be enabled. Returns True if it should be enabled; False if it should not.
Rules:
- • If the component's enabled property is False, always return False.
- • If both include_tags and exclude_tags are None, return True.
- • If exclude_tags is provided, check each exclude tag:
+ - If the component's enabled property is False, always return False.
+ - If both include_tags and exclude_tags are None, return True.
+ - If exclude_tags is provided, check each exclude tag:
- If the exclude tag is a string, it must be present in the input tags to exclude.
- • If include_tags is provided, check each include tag:
+ - If include_tags is provided, check each include tag:
- If the include tag is a string, it must be present in the input tags to include.
- • If include_tags is provided and none of the include tags match, return False.
- • If include_tags is not provided, return True.
+ - If include_tags is provided and none of the include tags match, return False.
+ - If include_tags is not provided, return True.
"""
if not component.enabled:
return False
@@ -1875,12 +1889,21 @@ def add_resource_prefix(
The resource URI with the prefix added
Examples:
- >>> add_resource_prefix("resource://path/to/resource", "prefix")
- "resource://prefix/path/to/resource" # with new style
- >>> add_resource_prefix("resource://path/to/resource", "prefix")
- "prefix+resource://path/to/resource" # with legacy style
- >>> add_resource_prefix("resource:///absolute/path", "prefix")
- "resource://prefix//absolute/path" # with new style
+ With new style:
+ ```python
+ add_resource_prefix("resource://path/to/resource", "prefix")
+ "resource://prefix/path/to/resource"
+ ```
+ With legacy style:
+ ```python
+ add_resource_prefix("resource://path/to/resource", "prefix")
+ "prefix+resource://path/to/resource"
+ ```
+ With absolute path:
+ ```python
+ add_resource_prefix("resource:///absolute/path", "prefix")
+ "resource://prefix//absolute/path"
+ ```
Raises:
ValueError: If the URI doesn't match the expected protocol://path format
@@ -1926,12 +1949,21 @@ def remove_resource_prefix(
The resource URI with the prefix removed
Examples:
- >>> remove_resource_prefix("resource://prefix/path/to/resource", "prefix")
- "resource://path/to/resource" # with new style
- >>> remove_resource_prefix("prefix+resource://path/to/resource", "prefix")
- "resource://path/to/resource" # with legacy style
- >>> remove_resource_prefix("resource://prefix//absolute/path", "prefix")
- "resource:///absolute/path" # with new style
+ With new style:
+ ```python
+ remove_resource_prefix("resource://prefix/path/to/resource", "prefix")
+ "resource://path/to/resource"
+ ```
+ With legacy style:
+ ```python
+ remove_resource_prefix("prefix+resource://path/to/resource", "prefix")
+ "resource://path/to/resource"
+ ```
+ With absolute path:
+ ```python
+ remove_resource_prefix("resource://prefix//absolute/path", "prefix")
+ "resource:///absolute/path"
+ ```
Raises:
ValueError: If the URI doesn't match the expected protocol://path format
@@ -1984,12 +2016,21 @@ def has_resource_prefix(
True if the URI has the specified prefix, False otherwise
Examples:
- >>> has_resource_prefix("resource://prefix/path/to/resource", "prefix")
- True # with new style
- >>> has_resource_prefix("prefix+resource://path/to/resource", "prefix")
- True # with legacy style
- >>> has_resource_prefix("resource://other/path/to/resource", "prefix")
+ With new style:
+ ```python
+ has_resource_prefix("resource://prefix/path/to/resource", "prefix")
+ True
+ ```
+ With legacy style:
+ ```python
+ has_resource_prefix("prefix+resource://path/to/resource", "prefix")
+ True
+ ```
+ With other path:
+ ```python
+ has_resource_prefix("resource://other/path/to/resource", "prefix")
False
+ ```
Raises:
ValueError: If the URI doesn't match the expected protocol://path format
diff --git a/src/fastmcp/tools/tool_transform.py b/src/fastmcp/tools/tool_transform.py
index 149469a4c..c6145d807 100644
--- a/src/fastmcp/tools/tool_transform.py
+++ b/src/fastmcp/tools/tool_transform.py
@@ -100,35 +100,55 @@ class ArgTransform:
examples: Examples for the argument. Use ... for no change.
Examples:
- # Rename argument 'old_name' to 'new_name'
+ Rename argument 'old_name' to 'new_name'
+ ```python
ArgTransform(name="new_name")
+ ```
- # Change description only
+ Change description only
+ ```python
ArgTransform(description="Updated description")
+ ```
- # Add a default value (makes argument optional)
+ Add a default value (makes argument optional)
+ ```python
ArgTransform(default=42)
+ ```
- # Add a default factory (makes argument optional)
+ Add a default factory (makes argument optional)
+ ```python
ArgTransform(default_factory=lambda: time.time())
+ ```
- # Change the type
+ Change the type
+ ```python
ArgTransform(type=str)
+ ```
- # Hide the argument entirely from clients
+ Hide the argument entirely from clients
+ ```python
ArgTransform(hide=True)
+ ```
- # Hide argument but pass a constant value to parent
+ Hide argument but pass a constant value to parent
+ ```python
ArgTransform(hide=True, default="constant_value")
+ ```
- # Hide argument but pass a factory-generated value to parent
+ Hide argument but pass a factory-generated value to parent
+ ```python
ArgTransform(hide=True, default_factory=lambda: uuid.uuid4().hex)
+ ```
- # Make an optional parameter required (removes any default)
+ Make an optional parameter required (removes any default)
+ ```python
ArgTransform(required=True)
+ ```
- # Combine multiple transformations
+ Combine multiple transformations
+ ```python
ArgTransform(name="new_name", description="New desc", default=None, type=int)
+ ```
"""
name: str | EllipsisType = NotSet
@@ -279,9 +299,9 @@ class TransformedTool(Tool):
name: New name for the tool. Defaults to parent tool's name.
transform_args: Optional transformations for parent tool arguments.
Only specified arguments are transformed, others pass through unchanged:
- - str: Simple rename
- - ArgTransform: Complex transformation (rename/description/default/drop)
- - None: Drop the argument
+ - Simple rename (str)
+ - Complex transformation (rename/description/default/drop) (ArgTransform)
+ - Drop the argument (None)
description: New description. Defaults to parent's description.
tags: New tags. Defaults to parent's tags.
annotations: New annotations. Defaults to parent's annotations.
@@ -290,23 +310,29 @@ class TransformedTool(Tool):
Returns:
TransformedTool with the specified transformations.
- Examples:
+ Examples:
# Transform specific arguments only
+ ```python
Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged
+ ```
# Custom function with partial transforms
+ ```python
async def custom(x: int, y: int) -> str:
result = await forward(x=x, y=y)
return f"Custom: {result}"
Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"})
+ ```
# Using **kwargs (gets all args, transformed and untransformed)
+ ```python
async def flexible(**kwargs) -> str:
result = await forward(**kwargs)
return f"Got: {kwargs}"
Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"})
+ ```
"""
transform_args = transform_args or {}
@@ -423,8 +449,8 @@ class TransformedTool(Tool):
Returns:
A tuple containing:
- - dict: The new JSON schema for the transformed tool
- - Callable: Async function that validates and forwards calls to the parent tool
+ - The new JSON schema for the transformed tool as a dictionary
+ - Async function that validates and forwards calls to the parent tool
"""
# Build transformed schema and mapping