diff --git a/docs/clients/client.mdx b/docs/clients/client.mdx index 7c1455712..70c47bfa9 100644 --- a/docs/clients/client.mdx +++ b/docs/clients/client.mdx @@ -37,7 +37,7 @@ Clients must be initialized with a `transport`. You can either provide an alread The following inference rules are used to determine the appropriate `ClientTransport` based on the input type: 1. **`ClientTransport` Instance**: If you provide an already instantiated transport object, it's used directly. -2. **`FastMCP` Instance**: Creates a `FastMCPTransport` for efficient in-memory communication (ideal for testing). +2. **`FastMCP` Instance**: Creates a `FastMCPTransport` for efficient in-memory communication (ideal for testing). This also works with a **FastMCP 1.0 server** created via `mcp.server.fastmcp.FastMCP`. 3. **`Path` or `str` pointing to an existing file**: * If it ends with `.py`: Creates a `PythonStdioTransport` to run the script using `python`. * If it ends with `.js`: Creates a `NodeStdioTransport` to run the script using `node`. diff --git a/docs/clients/transports.mdx b/docs/clients/transports.mdx index 958cafcae..b89f46884 100644 --- a/docs/clients/transports.mdx +++ b/docs/clients/transports.mdx @@ -290,8 +290,8 @@ asyncio.run(main()) ### FastMCP Transport - **Class:** `fastmcp.client.transports.FastMCPTransport` -- **Inferred From:** An instance of `fastmcp.server.FastMCP` -- **Use Case:** Connecting directly to a `FastMCP` server instance in the same Python process +- **Inferred From:** An instance of `fastmcp.server.FastMCP` or a **FastMCP 1.0 server** (`mcp.server.fastmcp.FastMCP`) +- **Use Case:** Connecting directly to a FastMCP server instance in the same Python process This is extremely useful for testing your FastMCP servers. diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index e2ae60816..7aacd5ad6 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -19,6 +19,7 @@ from mcp.client.sse import sse_client from mcp.client.stdio import stdio_client from mcp.client.streamable_http import streamablehttp_client from mcp.client.websocket import websocket_client +from mcp.server.fastmcp import FastMCP as FastMCP1Server from mcp.shared.memory import create_connected_server_and_client_session from pydantic import AnyUrl from typing_extensions import Unpack @@ -448,15 +449,21 @@ class NpxStdioTransport(StdioTransport): class FastMCPTransport(ClientTransport): - """ - Special transport for in-memory connections to an MCP server. + """In-memory transport for FastMCP servers. - This is particularly useful for testing or when client and server - are in the same process. + This transport connects directly to a FastMCP server instance in the same + Python process. It works with both FastMCP 2.x servers and FastMCP 1.0 + servers from the low-level MCP SDK. This is particularly useful for unit + tests or scenarios where client and server run in the same runtime. """ - def __init__(self, mcp: FastMCPServer): - self.server = mcp # Can be FastMCP or MCPServer + def __init__(self, mcp: FastMCPServer | FastMCP1Server): + """Initialize a FastMCPTransport from a FastMCP server instance.""" + + # Accept both FastMCP 2.x and FastMCP 1.0 servers. Both expose a + # ``_mcp_server`` attribute pointing to the underlying MCP server + # implementation, so we can treat them identically. + self.server = mcp @contextlib.asynccontextmanager async def connect_session( @@ -558,6 +565,7 @@ class MCPConfigTransport(ClientTransport): def infer_transport( transport: ClientTransport | FastMCPServer + | FastMCP1Server | AnyUrl | Path | MCPConfig @@ -573,7 +581,7 @@ def infer_transport( The function supports these input types: - ClientTransport: Used directly without modification - - FastMCPServer: Creates an in-memory FastMCPTransport + - FastMCPServer or FastMCP1Server: Creates an in-memory FastMCPTransport - Path or str (file path): Creates PythonStdioTransport (.py) or NodeStdioTransport (.js) - AnyUrl or str (URL): Creates StreamableHttpTransport (default) or SSETransport (for /sse endpoints) - MCPConfig or dict: Creates MCPConfigTransport, potentially connecting to multiple servers @@ -610,8 +618,8 @@ def infer_transport( if isinstance(transport, ClientTransport): return transport - # the transport is a FastMCP server - elif isinstance(transport, FastMCPServer): + # the transport is a FastMCP server (2.x or 1.0) + elif isinstance(transport, FastMCPServer | FastMCP1Server): inferred_transport = FastMCPTransport(mcp=transport) # the transport is a path to a script diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 62bddf0ab..0e97cb8d6 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -673,7 +673,7 @@ class TestInferTransport: assert transport.transport.command == "echo" assert transport.transport.args == ["hello"] - def test_infer_composite_client(config): + def test_infer_composite_client(self): config = { "mcpServers": { "local": { @@ -689,4 +689,17 @@ class TestInferTransport: transport = infer_transport(config) assert isinstance(transport, MCPConfigTransport) assert isinstance(transport.transport, FastMCPTransport) - assert len(transport.transport.server._mounted_servers) == 2 + assert len(cast(FastMCP, transport.transport.server)._mounted_servers) == 2 + + def test_infer_fastmcp_server(self, fastmcp_server): + """FastMCP server instances should infer to FastMCPTransport.""" + transport = infer_transport(fastmcp_server) + assert isinstance(transport, FastMCPTransport) + + def test_infer_fastmcp_v1_server(self): + """FastMCP 1.0 server instances should infer to FastMCPTransport.""" + from mcp.server.fastmcp import FastMCP as FastMCP1 + + server = FastMCP1() + transport = infer_transport(server) + assert isinstance(transport, FastMCPTransport)