From 92edacb86adc344e56f737fee679b0994ab217e6 Mon Sep 17 00:00:00 2001 From: Sandipan Haldar Date: Fri, 25 Apr 2025 12:14:58 +0000 Subject: [PATCH 1/3] added dictionary type to load server config --- src/fastmcp/client/base.py | 1 - src/fastmcp/client/client.py | 2 +- src/fastmcp/client/transports.py | 26 +++++++++++++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) delete mode 100644 src/fastmcp/client/base.py diff --git a/src/fastmcp/client/base.py b/src/fastmcp/client/base.py deleted file mode 100644 index 8b1378917..000000000 --- a/src/fastmcp/client/base.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/fastmcp/client/client.py b/src/fastmcp/client/client.py index 7fdd21899..8509511d2 100644 --- a/src/fastmcp/client/client.py +++ b/src/fastmcp/client/client.py @@ -35,7 +35,7 @@ class Client: def __init__( self, - transport: ClientTransport | FastMCP | AnyUrl | Path | str, + transport: ClientTransport | FastMCP | AnyUrl | Path | dict[str, Any] | str, # Common args roots: RootsList | RootsHandler | None = None, sampling_handler: SamplingHandler | None = None, diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index 210286bf2..8437c8436 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -416,7 +416,7 @@ class FastMCPTransport(ClientTransport): def infer_transport( - transport: ClientTransport | FastMCPServer | AnyUrl | Path | str, + transport: ClientTransport | FastMCPServer | AnyUrl | Path | dict[str, Any] | str, ) -> ClientTransport: """ Infer the appropriate transport type from the given transport argument. @@ -449,7 +449,31 @@ def infer_transport( # the transport is a websocket URL elif isinstance(transport, AnyUrl | str) and str(transport).startswith("ws"): return WSTransport(url=transport) + elif isinstance(transport, dict): + # Stdio transport + if "command" in transport and "args" in transport: + return StdioTransport( + command=transport["command"], + args=transport["args"], + env=transport.get("env", None), + cwd=transport.get("cwd", None), + ) + # HTTP transport + elif "url" in transport: + return SSETransport( + url=transport["url"], + headers=transport.get("headers", None), + ) + + # WebSocket transport + elif "ws_url" in transport: + return WSTransport( + url=transport["ws_url"], + ) + + raise ValueError("Cannot determine transport type from dictionary") + # the transport is an unknown type else: raise ValueError(f"Could not infer a valid transport from: {transport}") From 2be377f24b0c3e4adb0fc71744616d6701901873 Mon Sep 17 00:00:00 2001 From: Sandipan Haldar Date: Sat, 26 Apr 2025 02:04:55 +0530 Subject: [PATCH 2/3] modified to load transport from config --- src/fastmcp/client/base.py | 0 src/fastmcp/client/transports.py | 47 ++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 src/fastmcp/client/base.py diff --git a/src/fastmcp/client/base.py b/src/fastmcp/client/base.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index 8437c8436..426cb7e84 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -9,7 +9,7 @@ from pathlib import Path from typing import ( TypedDict, ) - +from typing import Any from exceptiongroup import BaseExceptionGroup, catch from mcp import ClientSession, McpError, StdioServerParameters from mcp.client.session import ( @@ -449,30 +449,37 @@ def infer_transport( # the transport is a websocket URL elif isinstance(transport, AnyUrl | str) and str(transport).startswith("ws"): return WSTransport(url=transport) + + ## if the transport is a config dict elif isinstance(transport, dict): + if "mcpServers" not in transport: + raise ValueError("Invalid transport dictionary: missing 'mcpServers' key") + else: + server = transport["mcpServers"] + server_name = list(server.keys())[0] # Stdio transport - if "command" in transport and "args" in transport: - return StdioTransport( - command=transport["command"], - args=transport["args"], - env=transport.get("env", None), - cwd=transport.get("cwd", None), - ) + if "command" in server[server_name] and "args" in server[server_name]: + return StdioTransport( + command=server[server_name]["command"], + args=server[server_name]["args"], + env=server[server_name].get("env", None), + cwd=server[server_name].get("cwd", None), + ) - # HTTP transport - elif "url" in transport: - return SSETransport( - url=transport["url"], - headers=transport.get("headers", None), - ) + # HTTP transport + elif "url" in server: + return SSETransport( + url=server["url"], + headers=server.get("headers", None), + ) - # WebSocket transport - elif "ws_url" in transport: - return WSTransport( - url=transport["ws_url"], - ) + # WebSocket transport + elif "ws_url" in server: + return WSTransport( + url=server["ws_url"], + ) - raise ValueError("Cannot determine transport type from dictionary") + raise ValueError("Cannot determine transport type from dictionary") # the transport is an unknown type else: From 322181874fe108d5683c2292b6c46c2d7ad0fcb3 Mon Sep 17 00:00:00 2001 From: Sandipan Haldar Date: Sat, 26 Apr 2025 02:13:22 +0530 Subject: [PATCH 3/3] added check support one mcp server --- src/fastmcp/client/transports.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index 426cb7e84..c8dc0d4cc 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -456,6 +456,8 @@ def infer_transport( raise ValueError("Invalid transport dictionary: missing 'mcpServers' key") else: server = transport["mcpServers"] + if len(list(server.keys())) > 1: + raise ValueError("Invalid transport dictionary: multiple servers found - only one expected") server_name = list(server.keys())[0] # Stdio transport if "command" in server[server_name] and "args" in server[server_name]: