mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-27 15:51:33 +02:00
Remove deprecated mount/import argument order and separator params (#2582)
This commit is contained in:
parent
95fb8b4894
commit
9b41d16dc0
3 changed files with 1 additions and 492 deletions
|
|
@ -2649,10 +2649,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
server: FastMCP[LifespanResultT],
|
||||
prefix: str | None = None,
|
||||
as_proxy: bool | None = None,
|
||||
*,
|
||||
tool_separator: str | None = None,
|
||||
resource_separator: str | None = None,
|
||||
prompt_separator: str | None = None,
|
||||
) -> None:
|
||||
"""Mount another FastMCP server on this server with an optional prefix.
|
||||
|
||||
|
|
@ -2697,56 +2693,9 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
as_proxy: Whether to treat the mounted server as a proxy. If None (default),
|
||||
automatically determined based on whether the server has a custom lifespan
|
||||
(True if it has a custom lifespan, False otherwise).
|
||||
tool_separator: Deprecated. Separator character for tool names.
|
||||
resource_separator: Deprecated. Separator character for resource URIs.
|
||||
prompt_separator: Deprecated. Separator character for prompt names.
|
||||
"""
|
||||
from fastmcp.server.proxy import FastMCPProxy
|
||||
|
||||
# Deprecated since 2.9.0
|
||||
# Prior to 2.9.0, the first positional argument was the prefix and the
|
||||
# second was the server. Here we swap them if needed now that the prefix
|
||||
# is optional.
|
||||
if isinstance(server, str):
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"Mount prefixes are now optional and the first positional argument "
|
||||
"should be the server you want to mount.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
server, prefix = cast(FastMCP[Any], prefix), server
|
||||
|
||||
if tool_separator is not None:
|
||||
# Deprecated since 2.4.0
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"The tool_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Tools are now prefixed using 'prefix_toolname' format.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if resource_separator is not None:
|
||||
# Deprecated since 2.4.0
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"The resource_separator parameter is deprecated and ignored. "
|
||||
"Resource prefixes are now added using the protocol://prefix/path format.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if prompt_separator is not None:
|
||||
# Deprecated since 2.4.0
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"The prompt_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Prompts are now prefixed using 'prefix_promptname' format.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
# if as_proxy is not specified and the server has a custom lifespan,
|
||||
# we should treat it as a proxy
|
||||
if as_proxy is None:
|
||||
|
|
@ -2771,9 +2720,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
self,
|
||||
server: FastMCP[LifespanResultT],
|
||||
prefix: str | None = None,
|
||||
tool_separator: str | None = None,
|
||||
resource_separator: str | None = None,
|
||||
prompt_separator: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Import the MCP objects from another FastMCP server into this one,
|
||||
|
|
@ -2805,56 +2751,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
server: The FastMCP server to import
|
||||
prefix: Optional prefix to use for the imported server's objects. If None,
|
||||
objects are imported with their original names.
|
||||
tool_separator: Deprecated. Separator for tool names.
|
||||
resource_separator: Deprecated and ignored. Prefix is now
|
||||
applied using the protocol://prefix/path format
|
||||
prompt_separator: Deprecated. Separator for prompt names.
|
||||
"""
|
||||
|
||||
# Deprecated since 2.9.0
|
||||
# Prior to 2.9.0, the first positional argument was the prefix and the
|
||||
# second was the server. Here we swap them if needed now that the prefix
|
||||
# is optional.
|
||||
if isinstance(server, str):
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"Import prefixes are now optional and the first positional argument "
|
||||
"should be the server you want to import.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
server, prefix = cast(FastMCP[Any], prefix), server
|
||||
|
||||
if tool_separator is not None:
|
||||
# Deprecated since 2.4.0
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"The tool_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Tools are now prefixed using 'prefix_toolname' format.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if resource_separator is not None:
|
||||
# Deprecated since 2.4.0
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"The resource_separator parameter is deprecated and ignored. "
|
||||
"Resource prefixes are now added using the protocol://prefix/path format.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if prompt_separator is not None:
|
||||
# Deprecated since 2.4.0
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"The prompt_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Prompts are now prefixed using 'prefix_promptname' format.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
# Import tools from the server
|
||||
for key, tool in (await server.get_tools()).items():
|
||||
if prefix:
|
||||
|
|
|
|||
|
|
@ -1,275 +0,0 @@
|
|||
import warnings
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.client import Client
|
||||
|
||||
|
||||
class TestDeprecatedMountArgOrder:
|
||||
"""Test deprecated positional argument order for mount() method."""
|
||||
|
||||
async def test_mount_deprecated_arg_order_with_warning(self):
|
||||
"""Test that mount(prefix, server) still works but raises deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
# Test the deprecated argument order: mount(prefix, server)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
main_app.mount("sub", sub_app) # type: ignore[arg-type] # Old order: prefix first, server second
|
||||
|
||||
# Check that a deprecation warning was raised
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, DeprecationWarning)
|
||||
assert (
|
||||
"Mount prefixes are now optional and the first positional argument should be the server"
|
||||
in str(w[0].message)
|
||||
)
|
||||
|
||||
# Verify the mount worked correctly despite deprecated order
|
||||
tools = await main_app.get_tools()
|
||||
assert "sub_sub_tool" in tools
|
||||
|
||||
# Test functionality
|
||||
async with Client(main_app) as client:
|
||||
result = await client.call_tool("sub_sub_tool", {})
|
||||
assert result.data == "Sub tool result"
|
||||
|
||||
async def test_mount_new_arg_order_no_warning(self):
|
||||
"""Test that mount(server, prefix) works without deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
# Test the new argument order: mount(server, prefix)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
main_app.mount(sub_app, "sub") # New order: server first, prefix second
|
||||
|
||||
# Check that no deprecation warning was raised for argument order
|
||||
mount_warnings = [
|
||||
warning
|
||||
for warning in w
|
||||
if "Mount prefixes are now optional" in str(warning.message)
|
||||
]
|
||||
assert len(mount_warnings) == 0
|
||||
|
||||
# Verify the mount worked correctly
|
||||
tools = await main_app.get_tools()
|
||||
assert "sub_sub_tool" in tools
|
||||
|
||||
async def test_mount_deprecated_order_no_prefix(self):
|
||||
"""Test deprecated order detection when first arg is empty string."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
# Test with empty string as first argument (old style for no prefix)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
main_app.mount("", sub_app) # type: ignore[arg-type] # Old order: empty prefix first, server second
|
||||
|
||||
# Check that a deprecation warning was raised
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, DeprecationWarning)
|
||||
assert (
|
||||
"Mount prefixes are now optional and the first positional argument should be the server"
|
||||
in str(w[0].message)
|
||||
)
|
||||
|
||||
# Verify the mount worked correctly (no prefix)
|
||||
tools = await main_app.get_tools()
|
||||
assert "sub_tool" in tools # No prefix applied
|
||||
|
||||
|
||||
class TestDeprecatedImportArgOrder:
|
||||
"""Test deprecated positional argument order for import_server() method."""
|
||||
|
||||
async def test_import_deprecated_arg_order_with_warning(self):
|
||||
"""Test that import_server(prefix, server) still works but raises deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
# Test the deprecated argument order: import_server(prefix, server)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
await main_app.import_server("sub", sub_app) # type: ignore[arg-type] # Old order: prefix first, server second
|
||||
|
||||
# Check that a deprecation warning was raised
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, DeprecationWarning)
|
||||
assert (
|
||||
"Import prefixes are now optional and the first positional argument should be the server"
|
||||
in str(w[0].message)
|
||||
)
|
||||
|
||||
# Verify the import worked correctly despite deprecated order
|
||||
assert "sub_sub_tool" in main_app._tool_manager._tools
|
||||
|
||||
# Test functionality
|
||||
async with Client(main_app) as client:
|
||||
result = await client.call_tool("sub_sub_tool", {})
|
||||
assert result.data == "Sub tool result"
|
||||
|
||||
async def test_import_new_arg_order_no_warning(self):
|
||||
"""Test that import_server(server, prefix) works without deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
# Test the new argument order: import_server(server, prefix)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
await main_app.import_server(
|
||||
sub_app, "sub"
|
||||
) # New order: server first, prefix second
|
||||
|
||||
# Check that no deprecation warning was raised for argument order
|
||||
import_warnings = [
|
||||
warning
|
||||
for warning in w
|
||||
if "Import prefixes are now optional" in str(warning.message)
|
||||
]
|
||||
assert len(import_warnings) == 0
|
||||
|
||||
# Verify the import worked correctly
|
||||
assert "sub_sub_tool" in main_app._tool_manager._tools
|
||||
|
||||
async def test_import_deprecated_order_no_prefix(self):
|
||||
"""Test deprecated order detection when first arg is empty string."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
# Test with empty string as first argument (old style for no prefix)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
await main_app.import_server("", sub_app) # type: ignore[arg-type] # Old order: empty prefix first, server second
|
||||
|
||||
# Check that a deprecation warning was raised
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, DeprecationWarning)
|
||||
assert (
|
||||
"Import prefixes are now optional and the first positional argument should be the server"
|
||||
in str(w[0].message)
|
||||
)
|
||||
|
||||
# Verify the import worked correctly (no prefix)
|
||||
assert "sub_tool" in main_app._tool_manager._tools # No prefix applied
|
||||
|
||||
async def test_import_deprecated_order_with_resources_and_prompts(self):
|
||||
"""Test deprecated order works with all component types."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
@sub_app.tool
|
||||
def sub_tool() -> str:
|
||||
return "Sub tool result"
|
||||
|
||||
@sub_app.resource(uri="data://config")
|
||||
def sub_resource():
|
||||
return "Sub resource data"
|
||||
|
||||
@sub_app.resource(uri="users://{user_id}/info")
|
||||
def sub_template(user_id: str):
|
||||
return f"Sub template for user {user_id}"
|
||||
|
||||
@sub_app.prompt
|
||||
def sub_prompt() -> str:
|
||||
return "Sub prompt content"
|
||||
|
||||
# Test the deprecated argument order with all component types
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
await main_app.import_server("api", sub_app) # type: ignore[arg-type] # Old order: prefix first, server second
|
||||
|
||||
# Check that a deprecation warning was raised
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, DeprecationWarning)
|
||||
|
||||
# Verify all component types were imported correctly with prefix
|
||||
assert "api_sub_tool" in main_app._tool_manager._tools
|
||||
assert "data://api/config" in main_app._resource_manager._resources
|
||||
assert "users://api/{user_id}/info" in main_app._resource_manager._templates
|
||||
assert "api_sub_prompt" in main_app._prompt_manager._prompts
|
||||
|
||||
|
||||
class TestArgOrderDetection:
|
||||
"""Test that argument order detection works correctly."""
|
||||
|
||||
async def test_mount_correctly_identifies_server_vs_string(self):
|
||||
"""Test that mount correctly identifies FastMCP instances vs strings."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
# This should NOT trigger deprecation warning (server first, prefix second)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
main_app.mount(sub_app, "prefix")
|
||||
|
||||
mount_warnings = [
|
||||
warning
|
||||
for warning in w
|
||||
if "Mount prefixes are now optional" in str(warning.message)
|
||||
]
|
||||
assert len(mount_warnings) == 0
|
||||
|
||||
# This SHOULD trigger deprecation warning (string first, server second)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
main_app.mount("prefix2", sub_app) # type: ignore[arg-type]
|
||||
|
||||
mount_warnings = [
|
||||
warning
|
||||
for warning in w
|
||||
if "Mount prefixes are now optional" in str(warning.message)
|
||||
]
|
||||
assert len(mount_warnings) == 1
|
||||
|
||||
async def test_import_correctly_identifies_server_vs_string(self):
|
||||
"""Test that import_server correctly identifies FastMCP instances vs strings."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
# This should NOT trigger deprecation warning (server first, prefix second)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
await main_app.import_server(sub_app, "prefix")
|
||||
|
||||
import_warnings = [
|
||||
warning
|
||||
for warning in w
|
||||
if "Import prefixes are now optional" in str(warning.message)
|
||||
]
|
||||
assert len(import_warnings) == 0
|
||||
|
||||
# This SHOULD trigger deprecation warning (string first, server second)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
await main_app.import_server("prefix2", sub_app) # type: ignore[arg-type]
|
||||
|
||||
import_warnings = [
|
||||
warning
|
||||
for warning in w
|
||||
if "Import prefixes are now optional" in str(warning.message)
|
||||
]
|
||||
assert len(import_warnings) == 1
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
"""Tests for the deprecated separator parameters in mount() and import_server() methods."""
|
||||
|
||||
import pytest
|
||||
from mcp import McpError
|
||||
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
# reset deprecation warnings for this module
|
||||
pytestmark = pytest.mark.filterwarnings("default::DeprecationWarning")
|
||||
|
||||
|
||||
def test_mount_resource_separator_deprecation_warning():
|
||||
"""Test that using resource_separator in mount() raises a deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
with pytest.warns(DeprecationWarning) as warnings:
|
||||
main_app.mount("sub", sub_app, resource_separator="+") # type: ignore[arg-type]
|
||||
|
||||
# Check that we get both the argument order warning and the resource_separator warning
|
||||
warning_messages = [str(w.message) for w in warnings]
|
||||
assert any(
|
||||
"resource_separator parameter is deprecated and ignored" in msg
|
||||
for msg in warning_messages
|
||||
)
|
||||
assert any("Mount prefixes are now optional" in msg for msg in warning_messages)
|
||||
|
||||
|
||||
async def test_mount_tool_separator_deprecation_warning():
|
||||
"""Test that using tool_separator in mount() raises a deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
with pytest.warns(DeprecationWarning) as warnings:
|
||||
main_app.mount("sub", sub_app, tool_separator="-") # type: ignore[arg-type]
|
||||
|
||||
# Check that we get both the argument order warning and the tool_separator warning
|
||||
warning_messages = [str(w.message) for w in warnings]
|
||||
assert any(
|
||||
"tool_separator parameter is deprecated" in msg for msg in warning_messages
|
||||
)
|
||||
assert any("Mount prefixes are now optional" in msg for msg in warning_messages)
|
||||
|
||||
# Verify the separator is ignored and the default is used
|
||||
@sub_app.tool
|
||||
def test_tool():
|
||||
return "test"
|
||||
|
||||
async with Client(main_app) as client:
|
||||
assert "sub_test_tool" in {t.name for t in await client.list_tools()}
|
||||
assert "sub-test_tool" not in {t.name for t in await client.list_tools()}
|
||||
|
||||
|
||||
async def test_mount_prompt_separator_deprecation_warning():
|
||||
"""Test that using prompt_separator in mount() raises a deprecation warning."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
with pytest.warns(DeprecationWarning) as warnings:
|
||||
main_app.mount("sub", sub_app, prompt_separator="-") # type: ignore[arg-type]
|
||||
|
||||
# Check that we get both the argument order warning and the prompt_separator warning
|
||||
warning_messages = [str(w.message) for w in warnings]
|
||||
assert any(
|
||||
"prompt_separator parameter is deprecated" in msg for msg in warning_messages
|
||||
)
|
||||
assert any("Mount prefixes are now optional" in msg for msg in warning_messages)
|
||||
|
||||
# Verify the separator is ignored and the default is used
|
||||
@sub_app.prompt
|
||||
def test_prompt():
|
||||
return "test"
|
||||
|
||||
async with Client(main_app) as client:
|
||||
assert await client.get_prompt("sub_test_prompt")
|
||||
with pytest.raises(McpError, match="Unknown prompt"):
|
||||
await client.get_prompt("sub-test_prompt")
|
||||
|
||||
|
||||
async def test_import_server_separator_deprecation_warnings():
|
||||
"""Test that using separators in import_server() raises deprecation warnings."""
|
||||
main_app = FastMCP("MainApp")
|
||||
sub_app = FastMCP("SubApp")
|
||||
|
||||
with pytest.warns(DeprecationWarning) as warnings:
|
||||
await main_app.import_server("sub", sub_app, tool_separator="-") # type: ignore[arg-type]
|
||||
|
||||
# Check that we get both warnings
|
||||
warning_messages = [str(w.message) for w in warnings]
|
||||
assert any(
|
||||
"tool_separator parameter is deprecated" in msg for msg in warning_messages
|
||||
)
|
||||
assert any("Import prefixes are now optional" in msg for msg in warning_messages)
|
||||
|
||||
main_app = FastMCP("MainApp")
|
||||
with pytest.warns(DeprecationWarning) as warnings:
|
||||
await main_app.import_server("sub", sub_app, resource_separator="+") # type: ignore[arg-type]
|
||||
|
||||
warning_messages = [str(w.message) for w in warnings]
|
||||
assert any(
|
||||
"resource_separator parameter is deprecated" in msg for msg in warning_messages
|
||||
)
|
||||
assert any("Import prefixes are now optional" in msg for msg in warning_messages)
|
||||
|
||||
main_app = FastMCP("MainApp")
|
||||
with pytest.warns(DeprecationWarning) as warnings:
|
||||
await main_app.import_server("sub", sub_app, prompt_separator="-") # type: ignore[arg-type]
|
||||
|
||||
warning_messages = [str(w.message) for w in warnings]
|
||||
assert any(
|
||||
"prompt_separator parameter is deprecated" in msg for msg in warning_messages
|
||||
)
|
||||
assert any("Import prefixes are now optional" in msg for msg in warning_messages)
|
||||
Loading…
Add table
Add a link
Reference in a new issue