diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index 81afc9c88..2061f1616 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -974,8 +974,13 @@ class MCPConfigTransport(ClientTransport): async def connect_session( self, **session_kwargs: Unpack[SessionKwargs] ) -> AsyncIterator[ClientSession]: - async with self.transport.connect_session(**session_kwargs) as session: - yield session + try: + async with self.transport.connect_session(**session_kwargs) as session: + yield session + finally: + # Clean up underlying transports to ensure subprocesses terminate + for transport in self._underlying_transports: + await transport.close() async def close(self): for transport in self._underlying_transports: diff --git a/tests/test_mcp_config.py b/tests/test_mcp_config.py index 41924813b..1c1c7a6c4 100644 --- a/tests/test_mcp_config.py +++ b/tests/test_mcp_config.py @@ -340,17 +340,12 @@ 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 + # Verify processes have terminated + with pytest.raises(psutil.NoSuchProcess): + psutil.Process(pid_1).status() with pytest.raises(psutil.NoSuchProcess): - while True: - psutil.Process(pid_1) - await asyncio.sleep(0.01) - - with pytest.raises(psutil.NoSuchProcess): - while True: - psutil.Process(pid_2) - await asyncio.sleep(0.01) + psutil.Process(pid_2).status() @pytest.mark.skipif(