Remove deprecated mount/import argument order and separator params (#2582)

This commit is contained in:
Jeremiah Lowin 2025-12-09 13:58:51 -05:00 committed by GitHub
commit 9b41d16dc0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 1 additions and 492 deletions

View file

@ -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:

View file

@ -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

View file

@ -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)