Replace FastMCP.as_proxy() with create_proxy() function (#2829)

This commit is contained in:
Jeremiah Lowin 2026-01-10 11:39:06 -05:00 committed by GitHub
commit 3163e61ee4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 341 additions and 124 deletions

View file

@ -46,13 +46,13 @@ server2 = FastMCP("Server2", providers=[provider])
### ProxyProvider
`ProxyProvider` (`src/fastmcp/server/providers/proxy.py`) proxies components from remote MCP servers via a client factory. Used by `FastMCP.as_proxy()` and `FastMCP.mount()` for remote server integration.
`ProxyProvider` (`src/fastmcp/server/providers/proxy.py`) proxies components from remote MCP servers via a client factory. Used by `create_proxy()` and `FastMCP.mount()` for remote server integration.
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
# Create proxy to remote server
server = FastMCP.as_proxy("http://remote-server/mcp")
server = create_proxy("http://remote-server/mcp")
```
### OpenAPIProvider

View file

@ -263,11 +263,11 @@ Users on Claude Pro, Max, Team, and Enterprise plans have first-class remote ser
Create a proxy server that connects to a remote HTTP server:
```python proxy_server.py
from fastmcp import FastMCP
from fastmcp.server import create_proxy
# Create a proxy to a remote server
proxy = FastMCP.as_proxy(
"https://example.com/mcp/sse",
proxy = create_proxy(
"https://example.com/mcp/sse",
name="Remote Server Proxy"
)
@ -280,8 +280,9 @@ if __name__ == "__main__":
For authenticated remote servers, create an authenticated client following the guidance in the [client auth documentation](/clients/auth/bearer) and pass it to the proxy:
```python auth_proxy_server.py {7}
from fastmcp import FastMCP, Client
from fastmcp import Client
from fastmcp.client.auth import BearerAuth
from fastmcp.server import create_proxy
# Create authenticated client
client = Client(
@ -290,7 +291,7 @@ client = Client(
)
# Create proxy using the authenticated client
proxy = FastMCP.as_proxy(client, name="Authenticated Proxy")
proxy = create_proxy(client, name="Authenticated Proxy")
if __name__ == "__main__":
proxy.run()

View file

@ -47,6 +47,77 @@ main.mount(weather_server)
# Now main has access to get_forecast and data://cities
```
## Mounting External Servers
Mount remote HTTP servers or subprocess-based MCP servers using `create_proxy()`:
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
mcp = FastMCP("Orchestrator")
# Mount a remote HTTP server (URLs work directly)
mcp.mount(create_proxy("http://api.example.com/mcp"), namespace="api")
# Mount local Python scripts (file paths work directly)
mcp.mount(create_proxy("./my_server.py"), namespace="local")
```
### Mounting npm/uvx Packages
For npm packages or Python tools, use the config dict format:
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
mcp = FastMCP("Orchestrator")
# Mount npm package via config
github_config = {
"mcpServers": {
"default": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
}
}
}
mcp.mount(create_proxy(github_config), namespace="github")
# Mount Python tool via config
sqlite_config = {
"mcpServers": {
"default": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db", "data.db"]
}
}
}
mcp.mount(create_proxy(sqlite_config), namespace="db")
```
Or use explicit transport classes:
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
from fastmcp.client.transports import NpxStdioTransport, UvxStdioTransport
mcp = FastMCP("Orchestrator")
mcp.mount(
create_proxy(NpxStdioTransport(package="@modelcontextprotocol/server-github")),
namespace="github"
)
mcp.mount(
create_proxy(UvxStdioTransport(tool_name="mcp-server-sqlite", tool_args=["--db", "data.db"])),
namespace="db"
)
```
For advanced configuration, see [Proxying](/servers/providers/proxy).
## Namespacing
<VersionBadge version="3.0.0" />
@ -145,18 +216,16 @@ main.mount(subserver, namespace="api")
### Proxy Mounting
The parent server treats the mounted server as a separate entity:
<Warning>
The `as_proxy` parameter is deprecated. Mounted servers now always have their lifespan and middleware invoked. To create a proxy server explicitly, use `create_proxy()` from `fastmcp.server`.
</Warning>
```python
main.mount(subserver, namespace="api", as_proxy=True)
```
Previously, the parent server could treat the mounted server as a separate entity with its own lifecycle. This behavior is now the default for all mounted servers:
- Full client lifecycle events on mounted server
- Mounted server's lifespan is executed
- Communication via in-memory Client transport
FastMCP automatically uses proxy mounting when the mounted server has a custom lifespan. Override with `as_proxy=True` or `as_proxy=False`.
## Tag Filtering
<VersionBadge version="3.0.0" />

View file

@ -37,7 +37,7 @@ FastMCP includes providers for common patterns:
|----------|--------------|----------------|
| `LocalProvider` | Stores components you define in code | `@mcp.tool`, `mcp.add_tool()` |
| `FastMCPProvider` | Wraps another FastMCP server | `mcp.mount(server)` |
| `ProxyProvider` | Connects to remote MCP servers | `FastMCP.as_proxy(client)` |
| `ProxyProvider` | Connects to remote MCP servers | `create_proxy(client)` |
| `TransformingProvider` | Adds prefixes to avoid name collisions | `mcp.mount(server, namespace="api")` |
Most users only interact with `LocalProvider` (through decorators) and occasionally mount or proxy other servers. The provider abstraction stays invisible until you need it.

View file

@ -36,17 +36,13 @@ sequenceDiagram
<VersionBadge version="2.10.3" />
Create a proxy using `FastMCP.as_proxy()`:
Create a proxy using `create_proxy()`:
```python
from fastmcp import FastMCP
from fastmcp.server.proxy import ProxyClient
from fastmcp.server import create_proxy
# Create a proxy to a remote server
proxy = FastMCP.as_proxy(
ProxyClient("http://example.com/mcp"),
name="MyProxy"
)
# create_proxy() accepts URLs, file paths, and transports directly
proxy = create_proxy("http://example.com/mcp", name="MyProxy")
if __name__ == "__main__":
proxy.run()
@ -57,19 +53,19 @@ This gives you:
- Automatic forwarding of MCP features (sampling, elicitation, etc.)
- Session isolation to prevent context mixing
<Tip>
To mount a proxy inside another FastMCP server, see [Mounting External Servers](/servers/providers/mounting#mounting-external-servers).
</Tip>
## Transport Bridging
A common use case is bridging transports - making a remote server available locally:
```python
from fastmcp import FastMCP
from fastmcp.server.proxy import ProxyClient
from fastmcp.server import create_proxy
# Bridge remote HTTP to local stdio
remote_proxy = FastMCP.as_proxy(
ProxyClient("http://example.com/mcp/sse"),
name="Remote-to-Local"
)
remote_proxy = create_proxy("http://example.com/mcp/sse", name="Remote-to-Local")
# Run locally via stdio for Claude Desktop
if __name__ == "__main__":
@ -79,11 +75,10 @@ if __name__ == "__main__":
Or expose a local server via HTTP:
```python
from fastmcp.server import create_proxy
# Bridge local server to HTTP
local_proxy = FastMCP.as_proxy(
ProxyClient("local_server.py"),
name="Local-to-HTTP"
)
local_proxy = create_proxy("local_server.py", name="Local-to-HTTP")
if __name__ == "__main__":
local_proxy.run(transport="http", host="0.0.0.0", port=8080)
@ -93,13 +88,13 @@ if __name__ == "__main__":
<VersionBadge version="2.10.3" />
ProxyClient provides session isolation - each request gets its own isolated backend session:
`create_proxy()` provides session isolation - each request gets its own isolated backend session:
```python
from fastmcp.server.proxy import ProxyClient
from fastmcp.server import create_proxy
# Each request creates a fresh backend session (recommended)
proxy = FastMCP.as_proxy(ProxyClient("backend_server.py"))
proxy = create_proxy("backend_server.py")
# Multiple clients can use this proxy simultaneously:
# - Client A calls a tool → gets isolated session
@ -113,10 +108,11 @@ If you pass an already-connected client, the proxy reuses that session:
```python
from fastmcp import Client
from fastmcp.server import create_proxy
async with Client("backend_server.py") as connected_client:
# This proxy reuses the connected session
proxy = FastMCP.as_proxy(connected_client)
proxy = create_proxy(connected_client)
# ⚠️ Warning: All requests share the same session
```
@ -129,7 +125,7 @@ Shared sessions may cause context mixing in concurrent scenarios. Use only in si
<VersionBadge version="2.10.3" />
ProxyClient automatically forwards MCP protocol features:
Proxies automatically forward MCP protocol features:
| Feature | Description |
|---------|-------------|
@ -140,11 +136,10 @@ ProxyClient automatically forwards MCP protocol features:
| Progress | Progress notifications |
```python
from fastmcp.server.proxy import ProxyClient
from fastmcp.server import create_proxy
# All features forwarded automatically
backend = ProxyClient("advanced_backend.py")
proxy = FastMCP.as_proxy(backend)
proxy = create_proxy("advanced_backend.py")
# When the backend:
# - Requests LLM sampling → forwarded to your client
@ -157,6 +152,8 @@ proxy = FastMCP.as_proxy(backend)
Selectively disable forwarding:
```python
from fastmcp.server.providers.proxy import ProxyClient
backend = ProxyClient(
"backend_server.py",
sampling_handler=None, # Disable LLM sampling
@ -171,7 +168,7 @@ backend = ProxyClient(
Create proxies from configuration dictionaries:
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
config = {
"mcpServers": {
@ -182,7 +179,7 @@ config = {
}
}
proxy = FastMCP.as_proxy(config, name="Config-Based Proxy")
proxy = create_proxy(config, name="Config-Based Proxy")
```
### Multi-Server Proxies
@ -190,6 +187,8 @@ proxy = FastMCP.as_proxy(config, name="Config-Based Proxy")
Combine multiple servers with automatic namespacing:
```python
from fastmcp.server import create_proxy
config = {
"mcpServers": {
"weather": {
@ -206,7 +205,7 @@ config = {
# Creates unified proxy with prefixed components:
# - weather_get_forecast
# - calendar_add_event
composite = FastMCP.as_proxy(config, name="Composite")
composite = create_proxy(config, name="Composite")
```
## Component Prefixing
@ -229,7 +228,10 @@ Components from a proxy server are "mirrored" - they reflect the remote server's
To modify a proxied component (like disabling it), create a local copy:
```python
proxy = FastMCP.as_proxy("backend_server.py")
from fastmcp import FastMCP
from fastmcp.server import create_proxy
proxy = create_proxy("backend_server.py")
# Get mirrored tool
mirrored_tool = await proxy.get_tool("useful_tool")
@ -258,12 +260,14 @@ When mounting proxy servers, this latency affects all operations on the parent s
For low-latency requirements, consider using [`import_server()`](/servers/providers/mounting#static-importing) to copy tools at startup.
## Advanced: FastMCPProxy Class
## Advanced Usage
### FastMCPProxy Class
For explicit session control, use `FastMCPProxy` directly:
```python
from fastmcp.server.proxy import FastMCPProxy, ProxyClient
from fastmcp.server.providers.proxy import FastMCPProxy, ProxyClient
# Custom session factory
def create_client():
@ -273,3 +277,25 @@ proxy = FastMCPProxy(client_factory=create_client)
```
This gives you full control over session creation and reuse strategies.
### Adding Proxied Components to Existing Server
Mount a proxy to add remote components to an existing server:
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
server = FastMCP("My Server")
# Add local tools
@server.tool
def local_tool() -> str:
return "Local result"
# Mount proxied tools from remote server
remote = create_proxy("http://remote-server/mcp")
server.mount(remote)
# Now server has both local and proxied tools
```

View file

@ -288,17 +288,18 @@ main.mount(sub, namespace="sub")
<VersionBadge version="2.0.0" />
FastMCP can act as a proxy for any MCP server (local or remote) using `FastMCP.as_proxy`, letting you bridge transports or add a frontend to existing servers. For example, you can expose a remote SSE server locally via stdio, or vice versa.
FastMCP can act as a proxy for any MCP server (local or remote) using `create_proxy`, letting you bridge transports or add a frontend to existing servers. For example, you can expose a remote SSE server locally via stdio, or vice versa.
Proxies automatically handle concurrent operations safely by creating fresh sessions for each request when using disconnected clients.
See the [Remote Proxies](/servers/providers/proxy) guide for details and advanced usage.
```python
from fastmcp import FastMCP, Client
from fastmcp import Client
from fastmcp.server import create_proxy
backend = Client("http://example.com/mcp/sse")
proxy = FastMCP.as_proxy(backend, name="ProxyServer")
proxy = create_proxy(backend, name="ProxyServer")
# Now use the proxy like any FastMCP server
```

View file

@ -12,7 +12,7 @@ from typing import Any, Literal
from mcp.server.fastmcp import FastMCP as FastMCP1x
from watchfiles import Change, awatch
from fastmcp.server.server import FastMCP
from fastmcp.server.server import FastMCP, create_proxy
from fastmcp.utilities.logging import get_logger
from fastmcp.utilities.mcp_server_config import (
MCPServerConfig,
@ -45,7 +45,7 @@ def create_client_server(url: str) -> Any:
import fastmcp
client = fastmcp.Client(url)
server = fastmcp.FastMCP.as_proxy(client)
server = create_proxy(client)
return server
except Exception as e:
logger.error(f"Failed to create client for URL {url}: {e}")
@ -54,12 +54,10 @@ def create_client_server(url: str) -> Any:
def create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]:
"""Create a FastMCP server from a MCPConfig."""
from fastmcp import FastMCP
with mcp_config_path.open() as src:
mcp_config = json.load(src)
server = FastMCP.as_proxy(mcp_config)
server = create_proxy(mcp_config)
return server

View file

@ -43,7 +43,7 @@ from fastmcp.mcp_config import (
infer_transport_type_from_url,
)
from fastmcp.server.dependencies import get_http_headers
from fastmcp.server.server import FastMCP
from fastmcp.server.server import FastMCP, create_proxy
from fastmcp.server.tasks.capabilities import get_task_capabilities
from fastmcp.utilities.logging import get_logger
from fastmcp.utilities.mcp_server_config.v1.environments.uv import UVEnvironment
@ -1035,9 +1035,9 @@ class MCPConfigTransport(ClientTransport):
transport = config.to_transport()
client = ProxyClient(transport=transport, timeout=timeout)
proxy = FastMCP.as_proxy(
proxy = create_proxy(
client,
name=f"Proxy-{name}",
backend=client,
tool_transformations=tool_transforms,
include_tags=include_tags,
exclude_tags=exclude_tags,

View file

@ -95,20 +95,20 @@ class _TransformingMCPServerMixin(FastMCPBaseModel):
client_name: str | None = None,
) -> tuple[FastMCP[Any], ClientTransport]:
"""Turn the Transforming MCPServer into a FastMCP Server and also return the underlying transport."""
from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.client.transports import (
ClientTransport, # pyright: ignore[reportUnusedImport]
)
from fastmcp.server import create_proxy
transport: ClientTransport = super().to_transport() # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue, reportUnknownVariableType] # ty: ignore[unresolved-attribute]
transport = cast(ClientTransport, transport)
client: Client[ClientTransport] = Client(transport=transport, name=client_name)
wrapped_mcp_server = FastMCP.as_proxy(
wrapped_mcp_server = create_proxy(
client,
name=server_name,
backend=client,
tool_transformations=self.tools,
include_tags=self.include_tags,
exclude_tags=self.exclude_tags,

View file

@ -1,6 +1,6 @@
from .server import FastMCP
from .context import Context
from .server import FastMCP, create_proxy
from . import dependencies
__all__ = ["Context", "FastMCP"]
__all__ = ["Context", "FastMCP", "create_proxy"]

View file

@ -53,6 +53,8 @@ from fastmcp.utilities.logging import get_logger
if TYPE_CHECKING:
from pathlib import Path
from fastmcp.client.transports import ClientTransport
logger = get_logger(__name__)
# Type alias for client factory functions
@ -574,6 +576,59 @@ class ProxyProvider(Provider):
# because client cleanup is handled per-request
# -----------------------------------------------------------------------------
# Factory Functions
# -----------------------------------------------------------------------------
def _create_client_factory(
target: (
Client[ClientTransportT]
| ClientTransport
| FastMCP[Any]
| FastMCP1Server
| AnyUrl
| Path
| MCPConfig
| dict[str, Any]
| str
),
) -> ClientFactoryT:
"""Create a client factory from the given target.
Internal helper that handles the session strategy based on the target type:
- Connected Client: reuses existing session (with warning about context mixing)
- Disconnected Client: creates fresh sessions per request
- Other targets: creates ProxyClient and fresh sessions per request
"""
if isinstance(target, Client):
client = target
if client.is_connected():
logger.info(
"Proxy detected connected client - reusing existing session for all requests. "
"This may cause context mixing in concurrent scenarios."
)
def reuse_client_factory() -> Client:
return client
return reuse_client_factory
else:
def fresh_client_factory() -> Client:
return client.new()
return fresh_client_factory
else:
# target is not a Client, so it's compatible with ProxyClient.__init__
base_client = ProxyClient(cast(Any, target))
def proxy_client_factory() -> Client:
return base_client.new()
return proxy_client_factory
# -----------------------------------------------------------------------------
# FastMCPProxy - Convenience Wrapper
# -----------------------------------------------------------------------------
@ -587,14 +642,14 @@ class FastMCPProxy(FastMCP):
Example:
```python
from fastmcp import FastMCP
from fastmcp.server import create_proxy
from fastmcp.server.providers.proxy import FastMCPProxy, ProxyClient
# Create a proxy server
proxy = FastMCPProxy(client_factory=lambda: ProxyClient("http://localhost:8000/mcp"))
# Create a proxy server using create_proxy (recommended)
proxy = create_proxy("http://localhost:8000/mcp")
# Or use the convenience method
proxy = FastMCP.as_proxy("http://localhost:8000/mcp")
# Or use FastMCPProxy directly with explicit client factory
proxy = FastMCPProxy(client_factory=lambda: ProxyClient("http://localhost:8000/mcp"))
```
"""
@ -607,7 +662,7 @@ class FastMCPProxy(FastMCP):
"""Initialize the proxy server.
FastMCPProxy requires explicit session management via client_factory.
Use FastMCP.as_proxy() for convenience with automatic session strategy.
Use create_proxy() for convenience with automatic session strategy.
Args:
client_factory: A callable that returns a Client instance when called.

View file

@ -2283,7 +2283,7 @@ class FastMCP(Generic[LifespanResultT]):
namespace: Optional namespace to use for the mounted server's objects. If None,
the server's objects are accessible with their original names.
as_proxy: Deprecated. Mounted servers now always have their lifespan and
middleware invoked. To create a proxy server, use FastMCP.as_proxy()
middleware invoked. To create a proxy server, use create_proxy()
explicitly before mounting.
tool_names: Optional mapping of original tool names to custom names. Use this
to override namespaced names. Keys are the original tool names from the
@ -2310,7 +2310,7 @@ class FastMCP(Generic[LifespanResultT]):
warnings.warn(
"as_proxy is deprecated and will be removed in a future version. "
"Mounted servers now always have their lifespan and middleware invoked. "
"To create a proxy server, use FastMCP.as_proxy() explicitly.",
"To create a proxy server, use create_proxy() explicitly.",
DeprecationWarning,
stacklevel=2,
)
@ -2549,48 +2549,24 @@ class FastMCP(Generic[LifespanResultT]):
) -> FastMCPProxy:
"""Create a FastMCP proxy server for the given backend.
.. deprecated::
Use :func:`fastmcp.server.create_proxy` instead.
This method will be removed in a future version.
The `backend` argument can be either an existing `fastmcp.client.Client`
instance or any value accepted as the `transport` argument of
`fastmcp.client.Client`. This mirrors the convenience of the
`fastmcp.client.Client` constructor.
"""
from fastmcp.client.client import Client
from fastmcp.server.providers.proxy import FastMCPProxy, ProxyClient
if isinstance(backend, Client):
client = backend
# Session strategy based on client connection state:
# - Connected clients: reuse existing session for all requests
# - Disconnected clients: create fresh sessions per request for isolation
if client.is_connected():
proxy_logger = get_logger(__name__)
proxy_logger.info(
"Proxy detected connected client - reusing existing session for all requests. "
"This may cause context mixing in concurrent scenarios."
)
# Reuse sessions - return the same client instance
def reuse_client_factory():
return client
client_factory = reuse_client_factory
else:
# Fresh sessions per request
def fresh_client_factory():
return client.new()
client_factory = fresh_client_factory
else:
# backend is not a Client, so it's compatible with ProxyClient.__init__
base_client = ProxyClient(cast(Any, backend))
# Fresh client created from transport - use fresh sessions per request
def proxy_client_factory():
return base_client.new()
client_factory = proxy_client_factory
return FastMCPProxy(client_factory=client_factory, **settings)
if fastmcp.settings.deprecation_warnings:
warnings.warn(
"FastMCP.as_proxy() is deprecated. Use create_proxy() from "
"fastmcp.server instead: `from fastmcp.server import create_proxy`",
DeprecationWarning,
stacklevel=2,
)
# Call the module-level create_proxy function directly
return create_proxy(backend, **settings)
@classmethod
def generate_name(cls, name: str | None = None) -> str:
@ -2600,3 +2576,60 @@ class FastMCP(Generic[LifespanResultT]):
return f"{class_name}-{secrets.token_hex(2)}"
else:
return f"{class_name}-{name}-{secrets.token_hex(2)}"
# -----------------------------------------------------------------------------
# Module-level Factory Functions
# -----------------------------------------------------------------------------
def create_proxy(
target: (
Client[ClientTransportT]
| ClientTransport
| FastMCP[Any]
| FastMCP1Server
| AnyUrl
| Path
| MCPConfig
| dict[str, Any]
| str
),
**settings: Any,
) -> FastMCPProxy:
"""Create a FastMCP proxy server for the given target.
This is the recommended way to create a proxy server. For lower-level control,
use `FastMCPProxy` or `ProxyProvider` directly from `fastmcp.server.providers.proxy`.
Args:
target: The backend to proxy to. Can be:
- A Client instance (connected or disconnected)
- A ClientTransport
- A FastMCP server instance
- A URL string or AnyUrl
- A Path to a server script
- An MCPConfig or dict
**settings: Additional settings passed to FastMCPProxy (name, etc.)
Returns:
A FastMCPProxy server that proxies to the target.
Example:
```python
from fastmcp.server import create_proxy
# Create a proxy to a remote server
proxy = create_proxy("http://remote-server/mcp")
# Create a proxy to another FastMCP server
proxy = create_proxy(other_server)
```
"""
from fastmcp.server.providers.proxy import (
FastMCPProxy,
_create_client_factory,
)
client_factory = _create_client_factory(target)
return FastMCPProxy(client_factory=client_factory, **settings)

View file

@ -14,7 +14,11 @@ from fastmcp.client import Client
from fastmcp.client.transports import FastMCPTransport, StreamableHttpTransport
from fastmcp.exceptions import ToolError
from fastmcp.resources import ResourceContent, ResourceResult
from fastmcp.server.providers.proxy import FastMCPProxy, ProxyClient
from fastmcp.server import create_proxy
from fastmcp.server.providers.proxy import (
FastMCPProxy,
ProxyClient,
)
from fastmcp.tools.tool import ToolResult
from fastmcp.tools.tool_transform import (
ToolTransformConfig,
@ -131,46 +135,76 @@ def fastmcp_server():
@pytest.fixture
async def proxy_server(fastmcp_server):
"""Fixture that creates a FastMCP proxy server."""
return FastMCP.as_proxy(ProxyClient(transport=FastMCPTransport(fastmcp_server)))
return create_proxy(ProxyClient(transport=FastMCPTransport(fastmcp_server)))
async def test_create_proxy(fastmcp_server):
"""Test that the proxy server properly forwards requests to the original server."""
# Create a client
async def test_create_proxy_with_client(fastmcp_server):
"""Test create_proxy with a Client."""
client = ProxyClient(transport=FastMCPTransport(fastmcp_server))
server = FastMCPProxy.as_proxy(client)
server = create_proxy(client)
assert isinstance(server, FastMCPProxy)
assert isinstance(server, FastMCP)
assert server.name.startswith("FastMCPProxy-")
async def test_as_proxy_with_server(fastmcp_server):
"""FastMCP.as_proxy should accept a FastMCP instance."""
proxy = FastMCP.as_proxy(fastmcp_server)
async def test_create_proxy_with_server(fastmcp_server):
"""create_proxy should accept a FastMCP instance."""
proxy = create_proxy(fastmcp_server)
async with Client(proxy) as client:
result = await client.call_tool("greet", {"name": "Test"})
assert result.data == "Hello, Test!"
async def test_as_proxy_with_transport(fastmcp_server):
"""FastMCP.as_proxy should accept a ClientTransport."""
proxy = FastMCP.as_proxy(FastMCPTransport(fastmcp_server))
async def test_create_proxy_with_transport(fastmcp_server):
"""create_proxy should accept a ClientTransport."""
proxy = create_proxy(FastMCPTransport(fastmcp_server))
async with Client(proxy) as client:
result = await client.call_tool("greet", {"name": "Test"})
assert result.data == "Hello, Test!"
def test_as_proxy_with_url():
"""FastMCP.as_proxy should accept a URL without connecting."""
proxy = FastMCP.as_proxy("http://example.com/mcp/")
def test_create_proxy_with_url():
"""create_proxy should accept a URL without connecting."""
proxy = create_proxy("http://example.com/mcp/")
assert isinstance(proxy, FastMCPProxy)
client = cast(Client, proxy.client_factory())
assert isinstance(client.transport, StreamableHttpTransport)
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."""