diff --git a/docs/development/v3-notes/v3-features.mdx b/docs/development/v3-notes/v3-features.mdx
index 7e3c481dc..95896a8e4 100644
--- a/docs/development/v3-notes/v3-features.mdx
+++ b/docs/development/v3-notes/v3-features.mdx
@@ -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
diff --git a/docs/integrations/claude-desktop.mdx b/docs/integrations/claude-desktop.mdx
index 3158a005a..598e4be5f 100644
--- a/docs/integrations/claude-desktop.mdx
+++ b/docs/integrations/claude-desktop.mdx
@@ -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()
diff --git a/docs/servers/providers/mounting.mdx b/docs/servers/providers/mounting.mdx
index 887a82b87..f74603991 100644
--- a/docs/servers/providers/mounting.mdx
+++ b/docs/servers/providers/mounting.mdx
@@ -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
@@ -145,18 +216,16 @@ main.mount(subserver, namespace="api")
### Proxy Mounting
-The parent server treats the mounted server as a separate entity:
+
+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`.
+
-```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
diff --git a/docs/servers/providers/overview.mdx b/docs/servers/providers/overview.mdx
index fe895a7a2..a9ffc9f62 100644
--- a/docs/servers/providers/overview.mdx
+++ b/docs/servers/providers/overview.mdx
@@ -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.
diff --git a/docs/servers/providers/proxy.mdx b/docs/servers/providers/proxy.mdx
index 779cfc90a..5598aa047 100644
--- a/docs/servers/providers/proxy.mdx
+++ b/docs/servers/providers/proxy.mdx
@@ -36,17 +36,13 @@ sequenceDiagram
-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
+
+To mount a proxy inside another FastMCP server, see [Mounting External Servers](/servers/providers/mounting#mounting-external-servers).
+
+
## 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__":
-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
-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
+```
diff --git a/docs/servers/server.mdx b/docs/servers/server.mdx
index a7c2f4878..90cbcd7e9 100644
--- a/docs/servers/server.mdx
+++ b/docs/servers/server.mdx
@@ -288,17 +288,18 @@ main.mount(sub, namespace="sub")
-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
```
diff --git a/src/fastmcp/cli/run.py b/src/fastmcp/cli/run.py
index b82f82292..ece664b72 100644
--- a/src/fastmcp/cli/run.py
+++ b/src/fastmcp/cli/run.py
@@ -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
diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py
index 34f9438d0..8365d4dce 100644
--- a/src/fastmcp/client/transports.py
+++ b/src/fastmcp/client/transports.py
@@ -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,
diff --git a/src/fastmcp/mcp_config.py b/src/fastmcp/mcp_config.py
index 84a114c36..c55b0009b 100644
--- a/src/fastmcp/mcp_config.py
+++ b/src/fastmcp/mcp_config.py
@@ -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,
diff --git a/src/fastmcp/server/__init__.py b/src/fastmcp/server/__init__.py
index 69ded232c..fb9afd895 100644
--- a/src/fastmcp/server/__init__.py
+++ b/src/fastmcp/server/__init__.py
@@ -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"]
diff --git a/src/fastmcp/server/providers/proxy.py b/src/fastmcp/server/providers/proxy.py
index 65c954f78..f874e1bd4 100644
--- a/src/fastmcp/server/providers/proxy.py
+++ b/src/fastmcp/server/providers/proxy.py
@@ -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.
diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py
index 964864c7c..c012c809a 100644
--- a/src/fastmcp/server/server.py
+++ b/src/fastmcp/server/server.py
@@ -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)
diff --git a/tests/server/proxy/__init__.py b/tests/server/providers/proxy/__init__.py
similarity index 100%
rename from tests/server/proxy/__init__.py
rename to tests/server/providers/proxy/__init__.py
diff --git a/tests/server/proxy/test_proxy_client.py b/tests/server/providers/proxy/test_proxy_client.py
similarity index 100%
rename from tests/server/proxy/test_proxy_client.py
rename to tests/server/providers/proxy/test_proxy_client.py
diff --git a/tests/server/proxy/test_proxy_server.py b/tests/server/providers/proxy/test_proxy_server.py
similarity index 93%
rename from tests/server/proxy/test_proxy_server.py
rename to tests/server/providers/proxy/test_proxy_server.py
index ce6d2046f..972cffb2d 100644
--- a/tests/server/proxy/test_proxy_server.py
+++ b/tests/server/providers/proxy/test_proxy_server.py
@@ -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."""
diff --git a/tests/server/proxy/test_stateful_proxy_client.py b/tests/server/providers/proxy/test_stateful_proxy_client.py
similarity index 100%
rename from tests/server/proxy/test_stateful_proxy_client.py
rename to tests/server/providers/proxy/test_stateful_proxy_client.py