mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-19 12:04:18 +02:00
* Consolidate tool transformation logic into TransformingProvider
Tool transformations were previously scattered across LocalProvider,
ProxyProvider, and MCPConfig. This consolidates all transformation
logic into TransformingProvider via with_transforms(tool_transforms={...}).
- Add tool_transforms parameter to TransformingProvider
- Add tool_transforms to Provider.with_transforms()
- Remove transformation storage from LocalProvider and ProxyProvider
- Remove add_tool_transformation() and remove_tool_transformation() from FastMCP
- Add tool_transforms parameter to factory methods (from_openapi, from_fastapi, create_proxy)
- Update tests to use new patterns
* Fix: reject tool lookups by pre-transform name
* Add collision validation for tool_transforms and fix docstring examples
- Validate duplicate target names in tool_transforms raise ValueError
- Fix docstring examples to use arguments/ArgTransformConfig (not args/ArgTransform)
- Add test for collision validation
* Add server-level tool transform APIs and fix task registration
- Add AggregateProvider to present multiple providers as one
- Add _get_root_provider() to apply server-level transforms uniformly
- Fix _docket_lifespan to use root provider (ensures renamed tools
register with correct keys for background execution)
- Add tool_transforms kwarg to __init__ (non-deprecated)
- Add add_tool_transform(), remove_tool_transform(), tool_transforms property
- Deprecate old API names (tool_transformations, add_tool_transformation, etc.)
- Update tests to use new API
* Add graceful degradation for provider errors in AggregateProvider
* Match original behavior: parallel queries with DEBUG logging
* Refactor transforms to middleware-style call_next pattern
Replaces the ad-hoc transformation system with a unified Transform
abstraction using the same call_next pattern as server middleware.
Key changes:
- New src/fastmcp/server/transforms/ module with Transform base class
- Namespace, ToolTransform, Visibility all implement the same interface
- Transforms compose via functools.partial chain building
- Visibility is now just the first transform in provider._transforms
- Server-level transforms apply after provider aggregation
- Task registration now applies full transform chain
Removes TransformingProvider, _BoundTransform, ComponentSource protocol.
User-facing API unchanged: mount(), add_transform(), enable/disable all
work as before.
* Add comprehensive transforms and visibility documentation
New docs/servers/providers/transforms.mdx covering:
- Mental model for middleware-style transform pattern
- Built-in transforms (Namespace, ToolTransform)
- Server vs provider-level transforms and ordering
- Tool modification (immediate vs deferred)
- Custom transform creation
New docs/servers/visibility.mdx covering:
- Enable/disable API for runtime visibility control
- Keys and tags for targeting components
- Allowlist mode with only=True
- Server vs provider visibility layering
Updates existing docs to reference new pages and simplifies
redundant content. Visibility is documented as a user feature,
not as an implementation detail.
* Restructure transforms docs and delete tool-transformation pattern
* Cleanup: simplify get_tasks and remove unused Provider.get_component
* Update loq
* Update loq limits and add loq note to AGENTS.md
* Deprecate add_tool_transformation and tool_transformations param
* Address PR review feedback: remove redundant imports, fix path reference
* Add missing imports to code examples in v3-features.mdx
104 lines
3.8 KiB
Python
104 lines
3.8 KiB
Python
"""Tests for deprecated add_tool_transformation API."""
|
|
|
|
import warnings
|
|
|
|
from fastmcp import FastMCP
|
|
from fastmcp.client import Client
|
|
from fastmcp.tools.tool_transform import ToolTransformConfig
|
|
|
|
|
|
class TestAddToolTransformationDeprecated:
|
|
"""Test that add_tool_transformation still works but emits deprecation warning."""
|
|
|
|
async def test_add_tool_transformation_emits_warning(self):
|
|
"""add_tool_transformation should emit deprecation warning."""
|
|
mcp = FastMCP("test")
|
|
|
|
@mcp.tool
|
|
def my_tool() -> str:
|
|
return "hello"
|
|
|
|
with warnings.catch_warnings(record=True) as w:
|
|
warnings.simplefilter("always")
|
|
mcp.add_tool_transformation(
|
|
"my_tool", ToolTransformConfig(name="renamed_tool")
|
|
)
|
|
|
|
assert len(w) == 1
|
|
assert issubclass(w[0].category, DeprecationWarning)
|
|
assert "add_tool_transformation is deprecated" in str(w[0].message)
|
|
|
|
async def test_add_tool_transformation_still_works(self):
|
|
"""add_tool_transformation should still apply the transformation."""
|
|
mcp = FastMCP("test")
|
|
|
|
@mcp.tool
|
|
def verbose_tool_name() -> str:
|
|
return "result"
|
|
|
|
# Suppress warning for this test - we just want to verify it works
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
mcp.add_tool_transformation(
|
|
"verbose_tool_name", ToolTransformConfig(name="short")
|
|
)
|
|
|
|
async with Client(mcp) as client:
|
|
tools = await client.list_tools()
|
|
tool_names = [t.name for t in tools]
|
|
|
|
# Original name should be gone, renamed version should exist
|
|
assert "verbose_tool_name" not in tool_names
|
|
assert "short" in tool_names
|
|
|
|
# Should be callable by new name
|
|
result = await client.call_tool("short", {})
|
|
assert result.content[0].text == "result"
|
|
|
|
async def test_remove_tool_transformation_emits_warning(self):
|
|
"""remove_tool_transformation should emit deprecation warning."""
|
|
mcp = FastMCP("test")
|
|
|
|
with warnings.catch_warnings(record=True) as w:
|
|
warnings.simplefilter("always")
|
|
mcp.remove_tool_transformation("any_tool")
|
|
|
|
assert len(w) == 1
|
|
assert issubclass(w[0].category, DeprecationWarning)
|
|
assert "remove_tool_transformation is deprecated" in str(w[0].message)
|
|
assert "no effect" in str(w[0].message)
|
|
|
|
async def test_tool_transformations_constructor_emits_warning(self):
|
|
"""tool_transformations constructor param should emit deprecation warning."""
|
|
with warnings.catch_warnings(record=True) as w:
|
|
warnings.simplefilter("always")
|
|
FastMCP(
|
|
"test",
|
|
tool_transformations={"my_tool": ToolTransformConfig(name="renamed")},
|
|
)
|
|
|
|
assert len(w) == 1
|
|
assert issubclass(w[0].category, DeprecationWarning)
|
|
assert "tool_transformations parameter is deprecated" in str(w[0].message)
|
|
|
|
async def test_tool_transformations_constructor_still_works(self):
|
|
"""tool_transformations constructor param should still apply transforms."""
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
mcp = FastMCP(
|
|
"test",
|
|
tool_transformations={
|
|
"my_tool": ToolTransformConfig(name="renamed_tool")
|
|
},
|
|
)
|
|
|
|
@mcp.tool
|
|
def my_tool() -> str:
|
|
return "result"
|
|
|
|
async with Client(mcp) as client:
|
|
tools = await client.list_tools()
|
|
tool_names = [t.name for t in tools]
|
|
|
|
assert "my_tool" not in tool_names
|
|
assert "renamed_tool" in tool_names
|