From 1b637522d166d40ad41346997d2fda74bc886934 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:51:57 -0500 Subject: [PATCH] Fix base_url fallback when url is not set (#2776) (#2782) Co-authored-by: Taisei Mima fix for httpx.URL("") being truthy but stringifying to empty string. --- src/fastmcp/server/openapi/components.py | 6 ++-- tests/server/openapi/test_comprehensive.py | 32 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/fastmcp/server/openapi/components.py b/src/fastmcp/server/openapi/components.py index 40577cfa7..496a23327 100644 --- a/src/fastmcp/server/openapi/components.py +++ b/src/fastmcp/server/openapi/components.py @@ -64,10 +64,8 @@ class OpenAPITool(Tool): try: # Get base URL from client base_url = ( - str(self._client.base_url) - if hasattr(self._client, "base_url") and self._client.base_url - else "http://localhost" - ) + str(self._client.base_url) if hasattr(self._client, "base_url") else "" + ) or "http://localhost" # Get Headers from client cli_headers = ( diff --git a/tests/server/openapi/test_comprehensive.py b/tests/server/openapi/test_comprehensive.py index e7602c06c..777cd54c8 100644 --- a/tests/server/openapi/test_comprehensive.py +++ b/tests/server/openapi/test_comprehensive.py @@ -498,6 +498,38 @@ class TestOpenAPIComprehensive: assert "123" in str(request.url) assert "users/123" in str(request.url) + async def test_request_uses_localhost_fallback_when_no_base_url( + self, comprehensive_openapi_spec + ): + """Test that tool uses localhost fallback when client has no base_url.""" + mock_client = Mock(spec=httpx.AsyncClient) + mock_client.base_url = httpx.URL("") # Empty URL, same as httpx default + mock_client.headers = None + + mock_response = Mock(spec=Response) + mock_response.status_code = 200 + mock_response.json.return_value = { + "id": 123, + "name": "Test User", + "email": "test@example.com", + } + mock_response.raise_for_status = Mock() + + mock_client.send = AsyncMock(return_value=mock_response) + + server = FastMCPOpenAPI( + openapi_spec=comprehensive_openapi_spec, + client=mock_client, + ) + + async with Client(server) as mcp_client: + await mcp_client.call_tool("get_user", {"id": 123}) + + # Verify request was made to localhost fallback + mock_client.send.assert_called_once() + request = mock_client.send.call_args[0][0] + assert str(request.url).startswith("http://localhost") + async def test_complex_request_with_body_and_parameters( self, comprehensive_openapi_spec ):