From 1afe73c13624a6e3325ac5a71c3ac615ee06d17a Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Wed, 21 May 2025 17:42:26 -0400 Subject: [PATCH 1/5] feat: support FastMCP v1 server transport --- docs/clients/client.mdx | 2 +- docs/clients/transports.mdx | 4 ++-- src/fastmcp/client/transports.py | 26 +++++++++++++++++--------- tests/client/test_client.py | 17 +++++++++++++++-- 4 files changed, 35 insertions(+), 14 deletions(-) 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) From 26bc3271ff00478866f41ce80083ce054aadf87a Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Thu, 22 May 2025 09:35:32 -0400 Subject: [PATCH 2/5] Add versioning note to docs --- docs/getting-started/installation.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index 46552e5c4..b28201471 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -60,6 +60,18 @@ mcp = FastMCP("My MCP Server") Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the 1.0 API. However, as the projects diverge, this can not be guaranteed. You may see deprecation warnings if you attempt to use 1.0 APIs in FastMCP 2.x. Please refer to this documentation for details on new capabilities. +## Versioning and Breaking Changes + +While we make every effort not to introduce backwards incompatible changes to our public APIs and behavior, FastMCP exists in a rapidly evolving MCP landscape. We're committed to bringing the most cutting-edge features to our users, which occasionally necessitates changes to existing functionality. + +As a practice, breaking changes will only occur on minor version changes (e.g., 2.3.x to 2.4.0). A minor version change indicates either: +- A new feature set significant enough to deserve a new line of features +- An implementation of breaking changes that could affect behavior if users upgrade blindly + +For users concerned about stability in production environments, we recommend pinning FastMCP to a specific version in your dependencies. + +Note that the "public API" includes the core functionality of the `FastMCP` server and its methods. It does not include private methods or objects that are stored as private attributes, as we do not expect users to rely on those implementation details. + ## Installing for Development If you plan to contribute to FastMCP, you should begin by cloning the repository and using uv to install all dependencies (development dependencies are installed automatically): From f51a768e2a6608639213829423e6454ec5ee74bf Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Thu, 22 May 2025 11:13:36 -0400 Subject: [PATCH 3/5] Update docs/getting-started/installation.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/getting-started/installation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index b28201471..3707f6416 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -65,7 +65,7 @@ Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the 1.0 While we make every effort not to introduce backwards incompatible changes to our public APIs and behavior, FastMCP exists in a rapidly evolving MCP landscape. We're committed to bringing the most cutting-edge features to our users, which occasionally necessitates changes to existing functionality. As a practice, breaking changes will only occur on minor version changes (e.g., 2.3.x to 2.4.0). A minor version change indicates either: -- A new feature set significant enough to deserve a new line of features +- A significant new feature set that warrants a new minor version - An implementation of breaking changes that could affect behavior if users upgrade blindly For users concerned about stability in production environments, we recommend pinning FastMCP to a specific version in your dependencies. From dcd161133d4b44362ed2ecd7626b34350f07edd6 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Thu, 22 May 2025 11:13:41 -0400 Subject: [PATCH 4/5] Update docs/getting-started/installation.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/getting-started/installation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index 3707f6416..af632a44d 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -66,7 +66,7 @@ While we make every effort not to introduce backwards incompatible changes to ou As a practice, breaking changes will only occur on minor version changes (e.g., 2.3.x to 2.4.0). A minor version change indicates either: - A significant new feature set that warrants a new minor version -- An implementation of breaking changes that could affect behavior if users upgrade blindly +- Introducing breaking changes that may affect behavior on upgrade For users concerned about stability in production environments, we recommend pinning FastMCP to a specific version in your dependencies. From 983848f77193fb7b516e3b2d6d88ed6d44b1d42e Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Thu, 22 May 2025 11:16:11 -0400 Subject: [PATCH 5/5] Update installation.mdx --- docs/getting-started/installation.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index af632a44d..aaff866e3 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -62,7 +62,7 @@ Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the 1.0 ## Versioning and Breaking Changes -While we make every effort not to introduce backwards incompatible changes to our public APIs and behavior, FastMCP exists in a rapidly evolving MCP landscape. We're committed to bringing the most cutting-edge features to our users, which occasionally necessitates changes to existing functionality. +While we make every effort not to introduce backwards-incompatible changes to our public APIs and behavior, FastMCP exists in a rapidly evolving MCP landscape. We're committed to bringing the most cutting-edge features to our users, which occasionally necessitates changes to existing functionality. As a practice, breaking changes will only occur on minor version changes (e.g., 2.3.x to 2.4.0). A minor version change indicates either: - A significant new feature set that warrants a new minor version @@ -70,6 +70,8 @@ As a practice, breaking changes will only occur on minor version changes (e.g., For users concerned about stability in production environments, we recommend pinning FastMCP to a specific version in your dependencies. +Whenever possible, FastMCP will issue deprecation warnings when users attempt to use APIs that are either deprecated or destined for future removal. These warnings will be maintained for at least 1 minor version release, and may be maintained longer. + Note that the "public API" includes the core functionality of the `FastMCP` server and its methods. It does not include private methods or objects that are stored as private attributes, as we do not expect users to rely on those implementation details. ## Installing for Development