fix(mcp): refine upstream OAuth metadata fallback

This commit is contained in:
gym-cmd 2026-05-15 13:28:28 +01:00
parent 0daeeab884
commit 8e864f33ee
2 changed files with 37 additions and 8 deletions

View file

@ -748,7 +748,7 @@ async def fetch_upstream_oauth_protected_resource(
async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.Oauth2Check)
last_error: Optional[Exception] = None
network_errors: list[Exception] = []
for candidate in candidates:
try:
response = await async_client.get(
@ -756,13 +756,13 @@ async def fetch_upstream_oauth_protected_resource(
headers={"Accept": "application/json"},
)
except Exception as exc: # network / connect errors
last_error = exc
if is_network_error(exc):
network_errors.append(exc)
continue
if response.status_code == 200:
try:
payload = response.json()
except Exception as exc:
last_error = exc
except Exception:
continue
if isinstance(payload, dict):
_OAUTH_METADATA_CACHE[cache_key] = (
@ -771,10 +771,8 @@ async def fetch_upstream_oauth_protected_resource(
)
return payload
if last_error is not None and all(
is_network_error(last_error) for _ in candidates
):
raise last_error
if len(network_errors) == len(candidates):
raise network_errors[-1]
return None

View file

@ -258,6 +258,37 @@ async def test_oauth_protected_resource_passthrough_network_error_returns_502():
assert exc_info.value.status_code == 502
@pytest.mark.asyncio
async def test_fetch_upstream_metadata_returns_none_when_not_all_candidates_network_fail():
passthrough_server = MCPServer(
server_id="passthrough-partial-network",
name="jet_knowledge_qa",
server_name="jet_knowledge_qa",
alias="jet_knowledge_qa",
url="https://upstream.example.com/mcp",
transport=MCPTransport.http,
auth_type=MCPAuth.none,
extra_headers=["Authorization"],
)
not_found_response = MagicMock()
not_found_response.status_code = 404
mock_client = MagicMock()
mock_client.get = AsyncMock(
side_effect=[not_found_response, httpx.ConnectError("path fallback failed")]
)
with patch.object(
discoverable_endpoints, "get_async_httpx_client", return_value=mock_client
):
result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource(
passthrough_server
)
assert result is None
assert mock_client.get.await_count == 2
@pytest.mark.asyncio
async def test_oauth_protected_resource_gateway_managed_unchanged():
"""Regression guard: OAuth2 servers still advertise the gateway as AS."""