mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-21 04:54:17 +02:00
Add meta support to tool transformation utilities (#1295)
This commit is contained in:
parent
3fc2510110
commit
3e53e62f24
4 changed files with 129 additions and 1 deletions
|
|
@ -92,6 +92,7 @@ The `Tool.from_tool()` class method is the primary way to create a transformed t
|
|||
- `tags`: An optional set of tags for the new tool.
|
||||
- `annotations`: An optional set of `ToolAnnotations` for the new tool.
|
||||
- `serializer`: An optional function that will be called to serialize the result of the new tool.
|
||||
- `meta`: Control meta information for the tool. Use `None` to remove meta, any dict to set meta, or leave unset to inherit from parent.
|
||||
|
||||
The result is a new `TransformedTool` object that wraps the parent tool and applies the transformations you specify. You can add this tool to your MCP server using its `add_tool()` method.
|
||||
|
||||
|
|
@ -255,6 +256,50 @@ transform_args = {
|
|||
`default_factory` can only be used with `hide=True`. This is because visible parameters need static defaults that can be represented in a JSON schema for the client.
|
||||
</Warning>
|
||||
|
||||
### Meta Information
|
||||
|
||||
<VersionBadge version="2.11.0" />
|
||||
|
||||
You can control meta information on transformed tools using the `meta` parameter. Meta information is additional data about the tool that doesn't affect its functionality but can be used by clients for categorization, routing, or other purposes.
|
||||
|
||||
```python {15-17}
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.tools import Tool
|
||||
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool
|
||||
def analyze_data(data: str) -> dict:
|
||||
"""Analyzes the provided data."""
|
||||
return {"result": f"Analysis of {data}"}
|
||||
|
||||
# Add custom meta information
|
||||
enhanced_tool = Tool.from_tool(
|
||||
analyze_data,
|
||||
name="enhanced_analyzer",
|
||||
meta={
|
||||
"category": "analytics",
|
||||
"priority": "high",
|
||||
"requires_auth": True
|
||||
}
|
||||
)
|
||||
|
||||
mcp.add_tool(enhanced_tool)
|
||||
```
|
||||
|
||||
You can also remove meta information entirely:
|
||||
|
||||
```python {6}
|
||||
# Remove meta information from parent tool
|
||||
simplified_tool = Tool.from_tool(
|
||||
analyze_data,
|
||||
name="simple_analyzer",
|
||||
meta=None # Removes any meta information
|
||||
)
|
||||
```
|
||||
|
||||
If you don't specify the `meta` parameter, the transformed tool inherits the parent tool's meta information.
|
||||
|
||||
### Required Values
|
||||
|
||||
In rare cases where you want to make an optional argument required, you can set `required=True`. This has no effect if the argument was already required.
|
||||
|
|
@ -456,6 +501,10 @@ directly in the MCPConfig json file.
|
|||
"weather_get_forecast": {
|
||||
"name": "miami_weather",
|
||||
"description": "Get the weather for Miami",
|
||||
"meta": {
|
||||
"category": "weather",
|
||||
"location": "miami"
|
||||
},
|
||||
"arguments": {
|
||||
"city": {
|
||||
"name": "city",
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ class Tool(FastMCPComponent):
|
|||
annotations: ToolAnnotations | None = None,
|
||||
output_schema: dict[str, Any] | None | Literal[False] = None,
|
||||
serializer: Callable[[Any], str] | None = None,
|
||||
meta: dict[str, Any] | None | NotSetT = NotSet,
|
||||
enabled: bool | None = None,
|
||||
) -> TransformedTool:
|
||||
from fastmcp.tools.tool_transform import TransformedTool
|
||||
|
|
@ -223,6 +224,7 @@ class Tool(FastMCPComponent):
|
|||
annotations=annotations,
|
||||
output_schema=output_schema,
|
||||
serializer=serializer,
|
||||
meta=meta,
|
||||
enabled=enabled,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ class TransformedTool(Tool):
|
|||
annotations: ToolAnnotations | None = None,
|
||||
output_schema: dict[str, Any] | None | Literal[False] = None,
|
||||
serializer: Callable[[Any], str] | None = None,
|
||||
meta: dict[str, Any] | None | NotSetT = NotSet,
|
||||
enabled: bool | None = None,
|
||||
) -> TransformedTool:
|
||||
"""Create a transformed tool from a parent tool.
|
||||
|
|
@ -390,6 +391,10 @@ class TransformedTool(Tool):
|
|||
- dict: Use custom output schema
|
||||
- False: Disable output schema and structured outputs
|
||||
serializer: New serializer. Defaults to parent's serializer.
|
||||
meta: Control meta information:
|
||||
- NotSet (default): Inherit from parent tool
|
||||
- dict: Use custom meta information
|
||||
- None: Remove meta information
|
||||
|
||||
Returns:
|
||||
TransformedTool with the specified transformations.
|
||||
|
|
@ -546,6 +551,7 @@ class TransformedTool(Tool):
|
|||
description if not isinstance(description, NotSetT) else tool.description
|
||||
)
|
||||
final_title = title if not isinstance(title, NotSetT) else tool.title
|
||||
final_meta = meta if not isinstance(meta, NotSetT) else tool.meta
|
||||
|
||||
transformed_tool = cls(
|
||||
fn=final_fn,
|
||||
|
|
@ -559,6 +565,7 @@ class TransformedTool(Tool):
|
|||
tags=tags or tool.tags,
|
||||
annotations=annotations or tool.annotations,
|
||||
serializer=serializer or tool.serializer,
|
||||
meta=final_meta,
|
||||
transform_args=transform_args,
|
||||
enabled=enabled if enabled is not None else True,
|
||||
)
|
||||
|
|
@ -851,6 +858,10 @@ class ToolTransformConfig(FastMCPBaseModel):
|
|||
default_factory=set,
|
||||
description="The new tags for the tool.",
|
||||
)
|
||||
meta: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="The new meta information for the tool.",
|
||||
)
|
||||
|
||||
enabled: bool = Field(
|
||||
default=True,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ from fastmcp.client.client import Client
|
|||
from fastmcp.exceptions import ToolError
|
||||
from fastmcp.tools import Tool, forward, forward_raw
|
||||
from fastmcp.tools.tool import FunctionTool, ToolResult
|
||||
from fastmcp.tools.tool_transform import ArgTransform, TransformedTool
|
||||
from fastmcp.tools.tool_transform import (
|
||||
ArgTransform,
|
||||
ToolTransformConfig,
|
||||
TransformedTool,
|
||||
)
|
||||
|
||||
|
||||
def get_property(tool: Tool, name: str) -> dict[str, Any]:
|
||||
|
|
@ -1428,3 +1432,65 @@ def test_transform_adds_description_to_none(sample_tool_no_title):
|
|||
"""Test that transformed tools can add description when parent has None."""
|
||||
transformed = Tool.from_tool(sample_tool_no_title, description="Added description")
|
||||
assert transformed.description == "Added description"
|
||||
|
||||
|
||||
# Meta transformation tests
|
||||
def test_transform_inherits_meta(sample_tool):
|
||||
"""Test that transformed tools inherit meta when none specified."""
|
||||
sample_tool.meta = {"original": True, "version": "1.0"}
|
||||
transformed = Tool.from_tool(sample_tool)
|
||||
assert transformed.meta == {"original": True, "version": "1.0"}
|
||||
|
||||
|
||||
def test_transform_overrides_meta(sample_tool):
|
||||
"""Test that transformed tools can override meta."""
|
||||
sample_tool.meta = {"original": True, "version": "1.0"}
|
||||
transformed = Tool.from_tool(sample_tool, meta={"custom": True, "priority": "high"})
|
||||
assert transformed.meta == {"custom": True, "priority": "high"}
|
||||
|
||||
|
||||
def test_transform_sets_meta_to_none(sample_tool):
|
||||
"""Test that transformed tools can explicitly set meta to None."""
|
||||
sample_tool.meta = {"original": True, "version": "1.0"}
|
||||
transformed = Tool.from_tool(sample_tool, meta=None)
|
||||
assert transformed.meta is None
|
||||
|
||||
|
||||
def test_transform_inherits_none_meta(sample_tool_no_title):
|
||||
"""Test that transformed tools inherit None meta."""
|
||||
sample_tool_no_title.meta = None
|
||||
transformed = Tool.from_tool(sample_tool_no_title)
|
||||
assert transformed.meta is None
|
||||
|
||||
|
||||
def test_transform_adds_meta_to_none(sample_tool_no_title):
|
||||
"""Test that transformed tools can add meta when parent has None."""
|
||||
sample_tool_no_title.meta = None
|
||||
transformed = Tool.from_tool(sample_tool_no_title, meta={"added": True})
|
||||
assert transformed.meta == {"added": True}
|
||||
|
||||
|
||||
def test_tool_transform_config_inherits_meta(sample_tool):
|
||||
"""Test that ToolTransformConfig inherits meta when unset."""
|
||||
sample_tool.meta = {"original": True, "version": "1.0"}
|
||||
config = ToolTransformConfig(name="config_tool")
|
||||
transformed = config.apply(sample_tool)
|
||||
assert transformed.meta == {"original": True, "version": "1.0"}
|
||||
|
||||
|
||||
def test_tool_transform_config_overrides_meta(sample_tool):
|
||||
"""Test that ToolTransformConfig can override meta."""
|
||||
sample_tool.meta = {"original": True, "version": "1.0"}
|
||||
config = ToolTransformConfig(
|
||||
name="config_tool", meta={"config": True, "priority": "high"}
|
||||
)
|
||||
transformed = config.apply(sample_tool)
|
||||
assert transformed.meta == {"config": True, "priority": "high"}
|
||||
|
||||
|
||||
def test_tool_transform_config_removes_meta(sample_tool):
|
||||
"""Test that ToolTransformConfig can remove meta with None."""
|
||||
sample_tool.meta = {"original": True, "version": "1.0"}
|
||||
config = ToolTransformConfig(name="config_tool", meta=None)
|
||||
transformed = config.apply(sample_tool)
|
||||
assert transformed.meta is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue