From 22d3e1452c5c6c33b4b2fcf9b5dc8aa61cd67c13 Mon Sep 17 00:00:00 2001 From: William Easton Date: Wed, 13 Aug 2025 12:52:05 -0500 Subject: [PATCH] Additional MCP and test clean-up --- src/fastmcp/utilities/mcp_config.py | 31 ++++------------------------- tests/client/test_stdio.py | 16 +++++++++++++-- tests/test_mcp_config.py | 11 ++++++++++ 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/fastmcp/utilities/mcp_config.py b/src/fastmcp/utilities/mcp_config.py index 94467c5c4..5138b4e46 100644 --- a/src/fastmcp/utilities/mcp_config.py +++ b/src/fastmcp/utilities/mcp_config.py @@ -7,29 +7,6 @@ from fastmcp.mcp_config import ( ) from fastmcp.server.server import FastMCP -# def composite_server_from_mcp_config( -# config: MCPConfig, name_as_prefix: bool = True -# ) -> tuple[FastMCP[None], list[ClientTransport]]: -# """A utility function to create a composite server from an MCPConfig, returns the underlying -# transports for each server. -# """ -# composite_server = FastMCP[None]() - -# transports = mount_mcp_config_into_server(config, composite_server, name_as_prefix) - -# return composite_server, transports - - -# def mount_mcp_config_into_server( -# config: MCPConfig, -# server: FastMCP[Any], -# name_as_prefix: bool = True, -# ) -> None: -# """A utility function to mount the servers from an MCPConfig into a FastMCP server, returns the underlying -# transports for each server. -# """ -# for name, server_to_mount, transport in mcp_config_to_servers_and_transports(config): -# server.mount(server=server_to_mount, prefix=name if name_as_prefix else None) def mcp_config_to_servers_and_transports( config: MCPConfig, @@ -40,6 +17,7 @@ def mcp_config_to_servers_and_transports( for name, mcp_server in config.mcpServers.items() ] + def mcp_server_type_to_servers_and_transports( name: str, mcp_server: MCPServerTypes, @@ -48,13 +26,12 @@ def mcp_server_type_to_servers_and_transports( from fastmcp.mcp_config import ( TransformingRemoteMCPServer, TransformingStdioMCPServer, -) + ) + server: FastMCP[Any] transport: ClientTransport - if isinstance( - mcp_server, TransformingRemoteMCPServer | TransformingStdioMCPServer - ): + if isinstance(mcp_server, TransformingRemoteMCPServer | TransformingStdioMCPServer): server, transport = mcp_server._to_server_and_transport() else: transport = mcp_server.to_transport() diff --git a/tests/client/test_stdio.py b/tests/client/test_stdio.py index cbac8740e..3a45ddd0d 100644 --- a/tests/client/test_stdio.py +++ b/tests/client/test_stdio.py @@ -1,6 +1,7 @@ import asyncio import gc import inspect +import os import weakref import psutil @@ -10,6 +11,10 @@ from fastmcp import Client from fastmcp.client.transports import PythonStdioTransport, StdioTransport +def running_under_debugger(): + return os.environ.get("DEBUGPY_RUNNING") == "true" + + def gc_collect_harder(): gc.collect() gc.collect() @@ -69,6 +74,9 @@ class TestKeepAlive: assert pid1 == pid2 + @pytest.mark.skipif( + running_under_debugger(), reason="Debugger holds a reference to the transport" + ) async def test_keep_alive_true_exit_scope_kills_transport(self, stdio_script): transport_weak_ref: weakref.ref[PythonStdioTransport] | None = None @@ -83,12 +91,14 @@ class TestKeepAlive: gc_collect_harder() - # When debugging, the debugger holds extra references so this test - # will pass when running and fail under the debugger. + # This test will fail while debugging because the debugger holds a reference to the underlying transport assert transport_weak_ref transport = transport_weak_ref() assert transport is None + @pytest.mark.skipif( + running_under_debugger(), reason="Debugger holds a reference to the transport" + ) async def test_keep_alive_true_exit_scope_kills_client(self, stdio_script): pid: int | None = None @@ -107,6 +117,8 @@ class TestKeepAlive: gc_collect_harder() + # This test may fail/hang while debugging because the debugger holds a reference to the underlying transport + with pytest.raises(psutil.NoSuchProcess): while True: psutil.Process(pid) diff --git a/tests/test_mcp_config.py b/tests/test_mcp_config.py index 961140ee5..a228b3a4a 100644 --- a/tests/test_mcp_config.py +++ b/tests/test_mcp_config.py @@ -2,6 +2,7 @@ import asyncio import gc import inspect import logging +import os import tempfile from collections.abc import AsyncGenerator from pathlib import Path @@ -32,6 +33,10 @@ from fastmcp.mcp_config import ( from fastmcp.tools.tool import Tool as FastMCPTool +def running_under_debugger(): + return os.environ.get("DEBUGPY_RUNNING") == "true" + + def gc_collect_harder(): gc.collect() gc.collect() @@ -237,6 +242,9 @@ async def test_multi_client(tmp_path: Path): assert result_2.data == 3 +@pytest.mark.skipif( + running_under_debugger(), reason="Debugger holds a reference to the transport" +) async def test_multi_client_lifespan(tmp_path: Path): pid_1: int | None = None pid_2: int | None = None @@ -285,6 +293,8 @@ async def test_multi_client_lifespan(tmp_path: Path): gc_collect_harder() + # This test will fail while debugging because the debugger holds a reference to the underlying transport + with pytest.raises(psutil.NoSuchProcess): while True: psutil.Process(pid_1) @@ -295,6 +305,7 @@ async def test_multi_client_lifespan(tmp_path: Path): psutil.Process(pid_2) await asyncio.sleep(0.1) + async def test_multi_client_force_close(tmp_path: Path): server_script = inspect.cleandoc(""" from fastmcp import FastMCP