Repoint tests and examples off removed deprecations

Replaces FastMCP.as_proxy() helper calls with create_proxy(), rewrites the
mount() as_proxy=/prefix= kwarg tests to plain mount() (the params are gone),
and deletes deprecation-only tests for as_proxy() and remove_tool().
This commit is contained in:
Jeremiah Lowin 2026-07-06 21:32:54 -04:00
commit 6a6fdcb2bb
No known key found for this signature in database
11 changed files with 39 additions and 176 deletions

View file

@ -3,7 +3,7 @@ This example demonstrates how to set up and use an in-memory FastMCP proxy.
It illustrates the pattern: It illustrates the pattern:
1. Create an original FastMCP server with some tools. 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. 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 import FastMCP
from fastmcp.client import Client from fastmcp.client import Client
from fastmcp.server import create_proxy
class EchoService: class EchoService:
@ -38,9 +39,9 @@ async def main():
# 2. Proxy Server Creation # 2. Proxy Server Creation
print("\nStep 2: Creating the Proxy Server (InMemoryProxy)...") print("\nStep 2: Creating the Proxy Server (InMemoryProxy)...")
print( 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( print(
f" -> Proxy Server '{proxy_server.name}' created, proxying '{original_server.name}'." f" -> Proxy Server '{proxy_server.name}' created, proxying '{original_server.name}'."
) )

View file

@ -63,9 +63,9 @@ def check_app_status() -> dict[str, str]:
# Mount sub-applications # 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(): async def get_server_details():

View file

@ -6,6 +6,7 @@ from mcp_types import TextResourceContents
from fastmcp import Client, FastMCP from fastmcp import Client, FastMCP
from fastmcp.client.transports import SSETransport, StreamableHttpTransport from fastmcp.client.transports import SSETransport, StreamableHttpTransport
from fastmcp.server import create_proxy
from fastmcp.server.providers.openapi import MCPType, RouteMap from fastmcp.server.providers.openapi import MCPType, RouteMap
from fastmcp.utilities.tests import run_server_async from fastmcp.utilities.tests import run_server_async
@ -63,7 +64,7 @@ async def sse_server():
@pytest.fixture @pytest.fixture
async def proxy_server(shttp_server: str): async def proxy_server(shttp_server: str):
"""Start a proxy server.""" """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: async with run_server_async(proxy, transport="http") as url:
yield url yield url

View file

@ -46,10 +46,12 @@ class TestParallelCalls:
return script_file return script_file
async def test_parallel_calls(self, stdio_script): async def test_parallel_calls(self, stdio_script):
from fastmcp.server import create_proxy
backend_transport = PythonStdioTransport(script_path=stdio_script) backend_transport = PythonStdioTransport(script_path=stdio_script)
backend_client = Client(transport=backend_transport) backend_client = Client(transport=backend_transport)
proxy = FastMCP.as_proxy(backend=backend_client, name="PROXY") proxy = create_proxy(backend_client, name="PROXY")
count = 10 count = 10

View file

@ -8,6 +8,7 @@ from mcp.server.context import ServerRequestContext
from fastmcp import Client, FastMCP from fastmcp import Client, FastMCP
from fastmcp.exceptions import ToolError from fastmcp.exceptions import ToolError
from fastmcp.server import create_proxy
from fastmcp.server.context import Context from fastmcp.server.context import Context
from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext
from fastmcp.tools.base import ToolResult 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 # proxy server will have its tools listed as well as called in order to
# apply transforms and filters prior to the call. # 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: async with Client(proxy_server) as client:
await client.call_tool("add", {"a": 1, "b": 2}) 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 """Tests that tags on remote FastMCP servers are visible to middleware
via proxy. See https://github.com/PrefectHQ/fastmcp/issues/1300""" 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 = [] TAGS = []

View file

@ -9,6 +9,7 @@ from mcp_types import TextContent
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.client import Client from fastmcp.client import Client
from fastmcp.client.transports import SSETransport from fastmcp.client.transports import SSETransport
from fastmcp.server import create_proxy
from fastmcp.tools.base import Tool from fastmcp.tools.base import Tool
from fastmcp.tools.tool_transform import TransformedTool from fastmcp.tools.tool_transform import TransformedTool
@ -272,7 +273,7 @@ class TestMultipleServerMount:
) )
# Create a proxy server that will fail to connect # Create a proxy server that will fail to connect
unreachable_proxy = FastMCP.as_proxy( unreachable_proxy = create_proxy(
unreachable_client, name="unreachable_proxy" unreachable_client, name="unreachable_proxy"
) )

View file

@ -6,8 +6,8 @@ from contextlib import asynccontextmanager
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.client import Client from fastmcp.client import Client
from fastmcp.client.transports import FastMCPTransport from fastmcp.client.transports import FastMCPTransport
from fastmcp.server import create_proxy
from fastmcp.server.providers import FastMCPProvider from fastmcp.server.providers import FastMCPProvider
from fastmcp.server.providers.proxy import FastMCPProxy
from fastmcp.server.providers.wrapped_provider import _WrappedProvider from fastmcp.server.providers.wrapped_provider import _WrappedProvider
from fastmcp.server.transforms import Namespace from fastmcp.server.transforms import Namespace
@ -25,7 +25,7 @@ class TestProxyServer:
return f"Data for {query}" return f"Data for {query}"
# Create proxy server # Create proxy server
proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) proxy_server = create_proxy(FastMCPTransport(original_server))
# Mount proxy server # Mount proxy server
main_app = FastMCP("MainApp") main_app = FastMCP("MainApp")
@ -45,7 +45,7 @@ class TestProxyServer:
original_server = FastMCP("OriginalServer") original_server = FastMCP("OriginalServer")
# Create proxy server # Create proxy server
proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) proxy_server = create_proxy(FastMCPTransport(original_server))
# Mount proxy server # Mount proxy server
main_app = FastMCP("MainApp") main_app = FastMCP("MainApp")
@ -74,7 +74,7 @@ class TestProxyServer:
return json.dumps({"api_key": "12345"}) return json.dumps({"api_key": "12345"})
# Create proxy server # Create proxy server
proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) proxy_server = create_proxy(FastMCPTransport(original_server))
# Mount proxy server # Mount proxy server
main_app = FastMCP("MainApp") main_app = FastMCP("MainApp")
@ -96,7 +96,7 @@ class TestProxyServer:
return f"Welcome, {name}!" return f"Welcome, {name}!"
# Create proxy server # Create proxy server
proxy_server = FastMCP.as_proxy(FastMCPTransport(original_server)) proxy_server = create_proxy(FastMCPTransport(original_server))
# Mount proxy server # Mount proxy server
main_app = FastMCP("MainApp") main_app = FastMCP("MainApp")
@ -108,10 +108,10 @@ class TestProxyServer:
# The message should contain our welcome text # The message should contain our welcome text
class TestAsProxyKwarg: class TestMountProviderStructure:
"""Test the as_proxy kwarg.""" """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") mcp = FastMCP("Main")
sub = FastMCP("Sub") sub = FastMCP("Sub")
@ -133,53 +133,6 @@ class TestAsProxyKwarg:
tools = await mcp.list_tools() tools = await mcp.list_tools()
assert {t.name for t in tools} == {"sub_sub_tool"} 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): async def test_lifespan_server_mounted_directly(self):
"""Test that servers with lifespan are mounted directly (not auto-proxied). """Test that servers with lifespan are mounted directly (not auto-proxied).
@ -214,10 +167,10 @@ class TestAsProxyKwarg:
tools = await mcp.list_tools() tools = await mcp.list_tools()
assert {t.name for t in tools} == {"sub_sub_tool"} 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") mcp = FastMCP("Main")
sub = FastMCP("Sub") sub = FastMCP("Sub")
sub_proxy = FastMCP.as_proxy(FastMCPTransport(sub)) sub_proxy = create_proxy(FastMCPTransport(sub))
mcp.mount(sub_proxy, "sub") mcp.mount(sub_proxy, "sub")
@ -231,45 +184,11 @@ class TestAsProxyKwarg:
assert isinstance(provider._inner, FastMCPProvider) assert isinstance(provider._inner, FastMCPProvider)
assert provider._inner.server is sub_proxy assert provider._inner.server is sub_proxy
async def test_as_proxy_ignored_for_proxy_mounts_false(self): async def test_mounts_have_live_link(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):
mcp = FastMCP("Main") mcp = FastMCP("Main")
sub = FastMCP("Sub") sub = FastMCP("Sub")
mcp.mount(sub, "sub", as_proxy=True) mcp.mount(sub, "sub")
assert len(await mcp.list_tools()) == 0 assert len(await mcp.list_tools()) == 0
@ -294,7 +213,7 @@ class TestAsProxyKwarg:
def hello(): def hello():
return "hi" return "hi"
mcp.mount(sub, as_proxy=True) mcp.mount(sub)
assert lifespan_check == [] assert lifespan_check == []

View file

@ -12,6 +12,7 @@ from mcp_types import (
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from fastmcp import Client, Context, FastMCP from fastmcp import Client, Context, FastMCP
from fastmcp.server import create_proxy
from fastmcp.client.elicitation import ElicitRequestParams, ElicitResult from fastmcp.client.elicitation import ElicitRequestParams, ElicitResult
from fastmcp.client.logging import LogMessage from fastmcp.client.logging import LogMessage
from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingParams 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. 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: class TestProxyClient:
@ -377,7 +378,7 @@ class TestProxyClient:
else: else:
return f"Elicitation {result.action}" 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 # Test that elicitation works correctly through the proxy
async def elicitation_handler( async def elicitation_handler(
@ -407,16 +408,16 @@ class TestProxyClient:
# Create a disconnected client (should use fresh sessions per request) # Create a disconnected client (should use fresh sessions per request)
base_client = Client(fastmcp_server) base_client = Client(fastmcp_server)
# Test both as_proxy convenience method and direct client_factory usage # Test both create_proxy convenience function and direct client_factory usage
proxy_via_as_proxy = FastMCP.as_proxy(base_client) proxy_via_create_proxy = create_proxy(base_client)
proxy_via_factory = FastMCPProxy(client_factory=base_client.new) proxy_via_factory = FastMCPProxy(client_factory=base_client.new)
# Verify the proxies are created successfully - this tests the client factory pattern # 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 assert proxy_via_factory is not None
# Verify they have the expected client factory behavior # 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") assert hasattr(proxy_via_factory, "_local_provider")
async def test_connected_proxy_client_uses_fresh_sessions( async def test_connected_proxy_client_uses_fresh_sessions(
@ -464,7 +465,7 @@ def roots_backend_server():
@pytest.fixture @pytest.fixture
async def roots_proxy_server(roots_backend_server: FastMCP): 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: class TestProxyServerInitiatedForwardingNonTool:

View file

@ -198,38 +198,6 @@ def test_create_proxy_with_url():
assert client.transport.url == "http://example.com/mcp/" 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(): async def test_proxy_with_async_client_factory():
"""FastMCPProxy should accept an async client_factory.""" """FastMCPProxy should accept an async client_factory."""

View file

@ -1,5 +1,4 @@
import os import os
import warnings
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from textwrap import dedent from textwrap import dedent
@ -161,37 +160,6 @@ class TestLocalProviderProperty:
assert not any(p.name == "my_prompt" for p in prompts) 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: class TestResourcePrefixMounting:
"""Test resource prefixing in mounted servers.""" """Test resource prefixing in mounted servers."""

View file

@ -719,8 +719,9 @@ class TestProxy:
@pytest.fixture @pytest.fixture
def proxy_server(self, mcp_server: FastMCP) -> FastMCP: def proxy_server(self, mcp_server: FastMCP) -> FastMCP:
from fastmcp.client.transports import FastMCPTransport 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 return proxy
async def test_transform_proxy(self, proxy_server: FastMCP): async def test_transform_proxy(self, proxy_server: FastMCP):