diff --git a/examples/in_memory_proxy_example.py b/examples/in_memory_proxy_example.py index 45e7a10b2..760ba579e 100644 --- a/examples/in_memory_proxy_example.py +++ b/examples/in_memory_proxy_example.py @@ -3,7 +3,7 @@ This example demonstrates how to set up and use an in-memory FastMCP proxy. It illustrates the pattern: 1. Create an original FastMCP server with some tools. -2. Create a proxy FastMCP server using ``FastMCP.as_proxy(original_server)``. +2. Create a proxy FastMCP server using ``create_proxy(original_server)``. 3. Use another Client to connect to the proxy server (in-memory) and interact with the original server's tools through the proxy. """ @@ -13,6 +13,7 @@ from mcp.types import TextContent from fastmcp import FastMCP from fastmcp.client import Client +from fastmcp.server import create_proxy class EchoService: @@ -38,9 +39,9 @@ async def main(): # 2. Proxy Server Creation print("\nStep 2: Creating the Proxy Server (InMemoryProxy)...") print( - f" (Using FastMCP.as_proxy to wrap '{original_server.name}' directly)" + f" (Using create_proxy to wrap '{original_server.name}' directly)" ) - proxy_server = FastMCP.as_proxy(original_server, name="InMemoryProxy") + proxy_server = create_proxy(original_server, name="InMemoryProxy") print( f" -> Proxy Server '{proxy_server.name}' created, proxying '{original_server.name}'." ) diff --git a/examples/mount_example.py b/examples/mount_example.py index 9e8c0fb42..5844ebe4c 100644 --- a/examples/mount_example.py +++ b/examples/mount_example.py @@ -63,9 +63,9 @@ def check_app_status() -> dict[str, str]: # Mount sub-applications -app.mount(server=weather_app, prefix="weather") +app.mount(server=weather_app, namespace="weather") -app.mount(server=news_app, prefix="news") +app.mount(server=news_app, namespace="news") async def get_server_details(): diff --git a/tests/client/test_openapi.py b/tests/client/test_openapi.py index fa3dcd3ca..236e6efb3 100644 --- a/tests/client/test_openapi.py +++ b/tests/client/test_openapi.py @@ -6,6 +6,7 @@ from mcp_types import TextResourceContents from fastmcp import Client, FastMCP from fastmcp.client.transports import SSETransport, StreamableHttpTransport +from fastmcp.server import create_proxy from fastmcp.server.providers.openapi import MCPType, RouteMap from fastmcp.utilities.tests import run_server_async @@ -63,7 +64,7 @@ async def sse_server(): @pytest.fixture async def proxy_server(shttp_server: str): """Start a proxy server.""" - proxy = FastMCP.as_proxy(StreamableHttpTransport(shttp_server)) + proxy = create_proxy(StreamableHttpTransport(shttp_server)) async with run_server_async(proxy, transport="http") as url: yield url diff --git a/tests/client/test_stdio.py b/tests/client/test_stdio.py index 3d1335ae5..0b2b56364 100644 --- a/tests/client/test_stdio.py +++ b/tests/client/test_stdio.py @@ -46,10 +46,12 @@ class TestParallelCalls: return script_file async def test_parallel_calls(self, stdio_script): + from fastmcp.server import create_proxy + backend_transport = PythonStdioTransport(script_path=stdio_script) backend_client = Client(transport=backend_transport) - proxy = FastMCP.as_proxy(backend=backend_client, name="PROXY") + proxy = create_proxy(backend_client, name="PROXY") count = 10 diff --git a/tests/server/middleware/test_middleware_nested.py b/tests/server/middleware/test_middleware_nested.py index 2f6d2593e..cc2eea6c0 100644 --- a/tests/server/middleware/test_middleware_nested.py +++ b/tests/server/middleware/test_middleware_nested.py @@ -8,6 +8,7 @@ from mcp.server.context import ServerRequestContext from fastmcp import Client, FastMCP from fastmcp.exceptions import ToolError +from fastmcp.server import create_proxy from fastmcp.server.context import Context from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext from fastmcp.tools.base import ToolResult @@ -475,7 +476,7 @@ class TestProxyServer: ): # proxy server will have its tools listed as well as called in order to # apply transforms and filters prior to the call. - proxy_server = FastMCP.as_proxy(mcp_server, name="Proxy Server") + proxy_server = create_proxy(mcp_server, name="Proxy Server") async with Client(proxy_server) as client: await client.call_tool("add", {"a": 1, "b": 2}) @@ -492,7 +493,7 @@ class TestProxyServer: ): """Tests that tags on remote FastMCP servers are visible to middleware via proxy. See https://github.com/PrefectHQ/fastmcp/issues/1300""" - proxy_server = FastMCP.as_proxy(mcp_server, name="Proxy Server") + proxy_server = create_proxy(mcp_server, name="Proxy Server") TAGS = [] diff --git a/tests/server/mount/test_mount.py b/tests/server/mount/test_mount.py index fdd555138..0ae383a1c 100644 --- a/tests/server/mount/test_mount.py +++ b/tests/server/mount/test_mount.py @@ -9,6 +9,7 @@ from mcp_types import TextContent from fastmcp import FastMCP from fastmcp.client import Client from fastmcp.client.transports import SSETransport +from fastmcp.server import create_proxy from fastmcp.tools.base import Tool from fastmcp.tools.tool_transform import TransformedTool @@ -272,7 +273,7 @@ class TestMultipleServerMount: ) # Create a proxy server that will fail to connect - unreachable_proxy = FastMCP.as_proxy( + unreachable_proxy = create_proxy( unreachable_client, name="unreachable_proxy" ) diff --git a/tests/server/mount/test_proxy.py b/tests/server/mount/test_proxy.py index 08caa36a0..e514a5ff4 100644 --- a/tests/server/mount/test_proxy.py +++ b/tests/server/mount/test_proxy.py @@ -6,8 +6,8 @@ from contextlib import asynccontextmanager from fastmcp import FastMCP from fastmcp.client import Client from fastmcp.client.transports import FastMCPTransport +from fastmcp.server import create_proxy from fastmcp.server.providers import FastMCPProvider -from fastmcp.server.providers.proxy import FastMCPProxy from fastmcp.server.providers.wrapped_provider import _WrappedProvider from fastmcp.server.transforms import Namespace @@ -25,7 +25,7 @@ class TestProxyServer: return f"Data for {query}" # Create proxy server - proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) + proxy_server = create_proxy(FastMCPTransport(original_server)) # Mount proxy server main_app = FastMCP("MainApp") @@ -45,7 +45,7 @@ class TestProxyServer: original_server = FastMCP("OriginalServer") # Create proxy server - proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) + proxy_server = create_proxy(FastMCPTransport(original_server)) # Mount proxy server main_app = FastMCP("MainApp") @@ -74,7 +74,7 @@ class TestProxyServer: return json.dumps({"api_key": "12345"}) # Create proxy server - proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) + proxy_server = create_proxy(FastMCPTransport(original_server)) # Mount proxy server main_app = FastMCP("MainApp") @@ -96,7 +96,7 @@ class TestProxyServer: return f"Welcome, {name}!" # Create proxy server - proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) + proxy_server = create_proxy(FastMCPTransport(original_server)) # Mount proxy server main_app = FastMCP("MainApp") @@ -108,10 +108,10 @@ class TestProxyServer: # The message should contain our welcome text -class TestAsProxyKwarg: - """Test the as_proxy kwarg.""" +class TestMountProviderStructure: + """Test the provider structure produced by mounting.""" - async def test_as_proxy_defaults_false(self): + async def test_mount_wraps_in_fastmcp_provider(self): mcp = FastMCP("Main") sub = FastMCP("Sub") @@ -133,53 +133,6 @@ class TestAsProxyKwarg: tools = await mcp.list_tools() assert {t.name for t in tools} == {"sub_sub_tool"} - async def test_as_proxy_false(self): - mcp = FastMCP("Main") - sub = FastMCP("Sub") - - @sub.tool - def sub_tool() -> str: - return "test" - - mcp.mount(sub, "sub", as_proxy=False) - - # Index 1 because LocalProvider is at index 0 - provider = mcp.providers[1] - # Provider is wrapped with Namespace transform - assert isinstance(provider, _WrappedProvider) - assert len(provider._transforms) == 1 - assert isinstance(provider._transforms[0], Namespace) - # Inner provider is FastMCPProvider - assert isinstance(provider._inner, FastMCPProvider) - assert provider._inner.server is sub - # Verify namespace is applied - tools = await mcp.list_tools() - assert {t.name for t in tools} == {"sub_sub_tool"} - - async def test_as_proxy_true(self): - mcp = FastMCP("Main") - sub = FastMCP("Sub") - - @sub.tool - def sub_tool() -> str: - return "test" - - mcp.mount(sub, "sub", as_proxy=True) - - # Index 1 because LocalProvider is at index 0 - provider = mcp.providers[1] - # Provider is wrapped with Namespace transform - assert isinstance(provider, _WrappedProvider) - assert len(provider._transforms) == 1 - assert isinstance(provider._transforms[0], Namespace) - # Inner provider is FastMCPProvider wrapping a proxy - assert isinstance(provider._inner, FastMCPProvider) - assert provider._inner.server is not sub - assert isinstance(provider._inner.server, FastMCPProxy) - # Verify namespace is applied - tools = await mcp.list_tools() - assert {t.name for t in tools} == {"sub_sub_tool"} - async def test_lifespan_server_mounted_directly(self): """Test that servers with lifespan are mounted directly (not auto-proxied). @@ -214,10 +167,10 @@ class TestAsProxyKwarg: tools = await mcp.list_tools() assert {t.name for t in tools} == {"sub_sub_tool"} - async def test_as_proxy_ignored_for_proxy_mounts_default(self): + async def test_mounting_a_proxy_preserves_the_proxy(self): mcp = FastMCP("Main") sub = FastMCP("Sub") - sub_proxy = FastMCP.as_proxy(FastMCPTransport(sub)) + sub_proxy = create_proxy(FastMCPTransport(sub)) mcp.mount(sub_proxy, "sub") @@ -231,45 +184,11 @@ class TestAsProxyKwarg: assert isinstance(provider._inner, FastMCPProvider) assert provider._inner.server is sub_proxy - async def test_as_proxy_ignored_for_proxy_mounts_false(self): - mcp = FastMCP("Main") - sub = FastMCP("Sub") - sub_proxy = FastMCP.as_proxy(FastMCPTransport(sub)) - - mcp.mount(sub_proxy, "sub", as_proxy=False) - - # Index 1 because LocalProvider is at index 0 - provider = mcp.providers[1] - # Provider is wrapped with Namespace transform - assert isinstance(provider, _WrappedProvider) - assert len(provider._transforms) == 1 - assert isinstance(provider._transforms[0], Namespace) - # Inner provider is FastMCPProvider - assert isinstance(provider._inner, FastMCPProvider) - assert provider._inner.server is sub_proxy - - async def test_as_proxy_ignored_for_proxy_mounts_true(self): - mcp = FastMCP("Main") - sub = FastMCP("Sub") - sub_proxy = FastMCP.as_proxy(FastMCPTransport(sub)) - - mcp.mount(sub_proxy, "sub", as_proxy=True) - - # Index 1 because LocalProvider is at index 0 - provider = mcp.providers[1] - # Provider is wrapped with Namespace transform - assert isinstance(provider, _WrappedProvider) - assert len(provider._transforms) == 1 - assert isinstance(provider._transforms[0], Namespace) - # Inner provider is FastMCPProvider - assert isinstance(provider._inner, FastMCPProvider) - assert provider._inner.server is sub_proxy - - async def test_as_proxy_mounts_still_have_live_link(self): + async def test_mounts_have_live_link(self): mcp = FastMCP("Main") sub = FastMCP("Sub") - mcp.mount(sub, "sub", as_proxy=True) + mcp.mount(sub, "sub") assert len(await mcp.list_tools()) == 0 @@ -294,7 +213,7 @@ class TestAsProxyKwarg: def hello(): return "hi" - mcp.mount(sub, as_proxy=True) + mcp.mount(sub) assert lifespan_check == [] diff --git a/tests/server/providers/proxy/test_proxy_client.py b/tests/server/providers/proxy/test_proxy_client.py index 80ee1862b..594642a78 100644 --- a/tests/server/providers/proxy/test_proxy_client.py +++ b/tests/server/providers/proxy/test_proxy_client.py @@ -12,6 +12,7 @@ from mcp_types import ( from pydantic import BaseModel, Field from fastmcp import Client, Context, FastMCP +from fastmcp.server import create_proxy from fastmcp.client.elicitation import ElicitRequestParams, ElicitResult from fastmcp.client.logging import LogMessage from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingParams @@ -88,7 +89,7 @@ async def proxy_server(fastmcp_server: FastMCP): """ A proxy server that forwards interactions with the proxy client to the given fastmcp server. """ - return FastMCP.as_proxy(ProxyClient(fastmcp_server)) + return create_proxy(ProxyClient(fastmcp_server)) class TestProxyClient: @@ -377,7 +378,7 @@ class TestProxyClient: else: return f"Elicitation {result.action}" - proxy_server = FastMCP.as_proxy(ProxyClient(fastmcp_server)) + proxy_server = create_proxy(ProxyClient(fastmcp_server)) # Test that elicitation works correctly through the proxy async def elicitation_handler( @@ -407,16 +408,16 @@ class TestProxyClient: # Create a disconnected client (should use fresh sessions per request) base_client = Client(fastmcp_server) - # Test both as_proxy convenience method and direct client_factory usage - proxy_via_as_proxy = FastMCP.as_proxy(base_client) + # Test both create_proxy convenience function and direct client_factory usage + proxy_via_create_proxy = create_proxy(base_client) proxy_via_factory = FastMCPProxy(client_factory=base_client.new) # Verify the proxies are created successfully - this tests the client factory pattern - assert proxy_via_as_proxy is not None + assert proxy_via_create_proxy is not None assert proxy_via_factory is not None # Verify they have the expected client factory behavior - assert hasattr(proxy_via_as_proxy, "_local_provider") + assert hasattr(proxy_via_create_proxy, "_local_provider") assert hasattr(proxy_via_factory, "_local_provider") async def test_connected_proxy_client_uses_fresh_sessions( @@ -464,7 +465,7 @@ def roots_backend_server(): @pytest.fixture async def roots_proxy_server(roots_backend_server: FastMCP): - return FastMCP.as_proxy(ProxyClient(roots_backend_server)) + return create_proxy(ProxyClient(roots_backend_server)) class TestProxyServerInitiatedForwardingNonTool: diff --git a/tests/server/providers/proxy/test_proxy_server.py b/tests/server/providers/proxy/test_proxy_server.py index 525012d1a..762a09abc 100644 --- a/tests/server/providers/proxy/test_proxy_server.py +++ b/tests/server/providers/proxy/test_proxy_server.py @@ -198,38 +198,6 @@ def test_create_proxy_with_url(): assert client.transport.url == "http://example.com/mcp/" -# --- Deprecated as_proxy tests (verify backwards compatibility) --- - - -async def test_as_proxy_deprecated_with_server(fastmcp_server): - """FastMCP.as_proxy should work but emit deprecation warning.""" - import warnings - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - proxy = FastMCP.as_proxy(fastmcp_server) - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "create_proxy" in str(w[0].message) - - async with Client(proxy) as client: - result = await client.call_tool("greet", {"name": "Test"}) - assert result.data == "Hello, Test!" - - -def test_as_proxy_deprecated_with_url(): - """FastMCP.as_proxy should work but emit deprecation warning.""" - import warnings - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - proxy = FastMCP.as_proxy("http://example.com/mcp/") - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - - assert isinstance(proxy, FastMCPProxy) - - async def test_proxy_with_async_client_factory(): """FastMCPProxy should accept an async client_factory.""" diff --git a/tests/server/test_server.py b/tests/server/test_server.py index 83374362d..d1aae6206 100644 --- a/tests/server/test_server.py +++ b/tests/server/test_server.py @@ -1,5 +1,4 @@ import os -import warnings from pathlib import Path from tempfile import TemporaryDirectory from textwrap import dedent @@ -161,37 +160,6 @@ class TestLocalProviderProperty: assert not any(p.name == "my_prompt" for p in prompts) -class TestRemoveToolDeprecation: - async def test_remove_tool_emits_deprecation_warning(self): - mcp = FastMCP() - - @mcp.tool - def my_tool() -> str: - return "result" - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - mcp.remove_tool("my_tool") - - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "local_provider" in str(w[0].message) - - async def test_remove_tool_still_works(self): - mcp = FastMCP() - - @mcp.tool - def my_tool() -> str: - return "result" - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - mcp.remove_tool("my_tool") - - tools = await mcp.list_tools() - assert not any(t.name == "my_tool" for t in tools) - - class TestResourcePrefixMounting: """Test resource prefixing in mounted servers.""" diff --git a/tests/tools/tool_transform/test_tool_transform.py b/tests/tools/tool_transform/test_tool_transform.py index dcee4cf97..f9e50df72 100644 --- a/tests/tools/tool_transform/test_tool_transform.py +++ b/tests/tools/tool_transform/test_tool_transform.py @@ -719,8 +719,9 @@ class TestProxy: @pytest.fixture def proxy_server(self, mcp_server: FastMCP) -> FastMCP: from fastmcp.client.transports import FastMCPTransport + from fastmcp.server import create_proxy - proxy = FastMCP.as_proxy(FastMCPTransport(mcp_server)) + proxy = create_proxy(FastMCPTransport(mcp_server)) return proxy async def test_transform_proxy(self, proxy_server: FastMCP):