mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-22 21:44:18 +02:00
Add execution field to base Tool class
The base Tool class now has an optional `execution` field for storing task execution metadata (SEP-1686). This lets gateways/proxies preserve execution info when forwarding tools from backends - previously this metadata was lost because Tool had no way to store it. FunctionTool continues to derive execution from task_config as before. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
659ec38d01
commit
17520ec5d5
2 changed files with 87 additions and 1 deletions
|
|
@ -133,6 +133,10 @@ class Tool(FastMCPComponent):
|
|||
ToolAnnotations | None,
|
||||
Field(description="Additional annotations about the tool"),
|
||||
] = None
|
||||
execution: Annotated[
|
||||
ToolExecution | None,
|
||||
Field(description="Task execution configuration (SEP-1686)"),
|
||||
] = None
|
||||
serializer: Annotated[
|
||||
ToolResultSerializerType | None,
|
||||
Field(description="Optional custom serializer for tool results"),
|
||||
|
|
@ -182,7 +186,7 @@ class Tool(FastMCPComponent):
|
|||
outputSchema=overrides.get("outputSchema", self.output_schema),
|
||||
icons=overrides.get("icons", self.icons),
|
||||
annotations=overrides.get("annotations", self.annotations),
|
||||
execution=overrides.get("execution"),
|
||||
execution=overrides.get("execution", self.execution),
|
||||
_meta=overrides.get(
|
||||
"_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from mcp.types import (
|
|||
ResourceLink,
|
||||
TextContent,
|
||||
TextResourceContents,
|
||||
ToolExecution,
|
||||
)
|
||||
from pydantic import AnyUrl, BaseModel, Field, TypeAdapter
|
||||
from typing_extensions import TypedDict
|
||||
|
|
@ -1813,3 +1814,84 @@ class TestToolNameValidation:
|
|||
assert tool.parameters is not None
|
||||
assert "a" in tool.parameters["properties"]
|
||||
assert "b" in tool.parameters["properties"]
|
||||
|
||||
|
||||
class TestToolExecutionField:
|
||||
"""Tests for the execution field on the base Tool class."""
|
||||
|
||||
def test_tool_with_execution_field(self):
|
||||
"""Test that Tool can store and return execution metadata."""
|
||||
tool = Tool(
|
||||
name="my_tool",
|
||||
description="A tool with execution",
|
||||
parameters={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport="optional"),
|
||||
)
|
||||
|
||||
mcp_tool = tool.to_mcp_tool()
|
||||
assert mcp_tool.execution is not None
|
||||
assert mcp_tool.execution.taskSupport == "optional"
|
||||
|
||||
def test_tool_without_execution_field(self):
|
||||
"""Test that Tool without execution returns None."""
|
||||
tool = Tool(
|
||||
name="my_tool",
|
||||
description="A tool without execution",
|
||||
parameters={"type": "object", "properties": {}},
|
||||
)
|
||||
|
||||
mcp_tool = tool.to_mcp_tool()
|
||||
assert mcp_tool.execution is None
|
||||
|
||||
def test_execution_override_takes_precedence(self):
|
||||
"""Test that explicit override takes precedence over field value."""
|
||||
tool = Tool(
|
||||
name="my_tool",
|
||||
description="A tool",
|
||||
parameters={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport="optional"),
|
||||
)
|
||||
|
||||
override_execution = ToolExecution(taskSupport="required")
|
||||
mcp_tool = tool.to_mcp_tool(execution=override_execution)
|
||||
assert mcp_tool.execution is not None
|
||||
assert mcp_tool.execution.taskSupport == "required"
|
||||
|
||||
async def test_function_tool_task_config_still_works(self):
|
||||
"""FunctionTool should still derive execution from task_config."""
|
||||
|
||||
async def my_fn() -> str:
|
||||
return "hello"
|
||||
|
||||
tool = Tool.from_function(my_fn, task=True)
|
||||
mcp_tool = tool.to_mcp_tool()
|
||||
|
||||
# FunctionTool sets execution from task_config
|
||||
assert mcp_tool.execution is not None
|
||||
assert mcp_tool.execution.taskSupport == "optional"
|
||||
|
||||
def test_tool_execution_required_mode(self):
|
||||
"""Test that Tool can store required execution mode."""
|
||||
tool = Tool(
|
||||
name="my_tool",
|
||||
description="A tool with required execution",
|
||||
parameters={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport="required"),
|
||||
)
|
||||
|
||||
mcp_tool = tool.to_mcp_tool()
|
||||
assert mcp_tool.execution is not None
|
||||
assert mcp_tool.execution.taskSupport == "required"
|
||||
|
||||
def test_tool_execution_forbidden_mode(self):
|
||||
"""Test that Tool can store forbidden execution mode."""
|
||||
tool = Tool(
|
||||
name="my_tool",
|
||||
description="A tool with forbidden execution",
|
||||
parameters={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport="forbidden"),
|
||||
)
|
||||
|
||||
mcp_tool = tool.to_mcp_tool()
|
||||
assert mcp_tool.execution is not None
|
||||
assert mcp_tool.execution.taskSupport == "forbidden"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue