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