Remove OpenAPI timeout parameter, make client optional, surface timeout errors (#3067)

* Remove OpenAPI timeout param, make client optional, surface timeout errors

* Close auto-created httpx client via provider lifespan
This commit is contained in:
Jeremiah Lowin 2026-02-03 21:08:09 -05:00 committed by GitHub
commit 5fd41b2e15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 102 additions and 43 deletions

View file

@ -739,3 +739,27 @@ class TestOpenAPIComprehensive:
assert provider is not None
assert hasattr(provider, "_director")
assert hasattr(provider, "_spec")
async def test_timeout_error_produces_useful_message(
self, comprehensive_openapi_spec
):
"""ReadTimeout should surface a clear error, not an empty string."""
mock_client = Mock(spec=httpx.AsyncClient)
mock_client.base_url = "https://api.example.com"
mock_client.headers = None
# httpx internally raises ReadTimeout with an empty message
mock_client.send = AsyncMock(side_effect=httpx.ReadTimeout(""))
server = create_openapi_server(
openapi_spec=comprehensive_openapi_spec,
client=mock_client,
)
async with Client(server) as mcp_client:
with pytest.raises(Exception) as exc_info:
await mcp_client.call_tool("get_user", {"id": 1})
error_message = str(exc_info.value)
assert "timed out" in error_message
assert "ReadTimeout" in error_message

View file

@ -6,6 +6,7 @@ import pytest
from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.server.providers.openapi import OpenAPIProvider
from fastmcp.server.providers.openapi.provider import DEFAULT_TIMEOUT
class TestOpenAPIProviderBasicFunctionality:
@ -146,16 +147,21 @@ class TestOpenAPIProviderBasicFunctionality:
assert get_user_tool is not None
assert get_user_tool.description is not None
def test_provider_with_timeout(self, simple_openapi_spec):
"""Test provider initialization with timeout setting."""
client = httpx.AsyncClient(base_url="https://api.example.com")
provider = OpenAPIProvider(
openapi_spec=simple_openapi_spec,
client=client,
timeout=30.0,
)
def test_provider_creates_default_client_from_spec(self, simple_openapi_spec):
"""Test that omitting client creates one from the spec's servers URL."""
provider = OpenAPIProvider(openapi_spec=simple_openapi_spec)
assert str(provider._client.base_url).rstrip("/") == "https://api.example.com"
assert provider._client.timeout == httpx.Timeout(DEFAULT_TIMEOUT)
assert provider._timeout == 30.0
def test_provider_default_client_requires_servers(self):
"""Test that omitting client without servers in spec raises."""
spec = {
"openapi": "3.0.0",
"info": {"title": "No Servers", "version": "1.0.0"},
"paths": {},
}
with pytest.raises(ValueError, match="No server URL"):
OpenAPIProvider(openapi_spec=spec)
def test_provider_with_empty_spec(self):
"""Test provider with minimal OpenAPI spec."""