mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
[verified] fix(mcp-oauth): bridge httpx auth_flow bidirectional generator
HermesMCPOAuthProvider.async_auth_flow wrapped the SDK's auth_flow with 'async for item in super().async_auth_flow(request): yield item', which discards httpx's .asend(response) values and resumes the inner generator with None. This broke every OAuth MCP server on the first HTTP response with 'NoneType' object has no attribute 'status_code' crashing at mcp/client/auth/oauth2.py:505. Replace with a manual bridge that forwards .asend() values into the inner generator, preserving httpx's bidirectional auth_flow contract. Add tests/tools/test_mcp_oauth_bidirectional.py with two regression tests that drive the flow through real .asend() round-trips. These catch the bug at the unit level; prior tests only exercised _initialize() and disk-watching, never the full generator protocol. Verified against BetterStack MCP: Before: 'Connection failed (11564ms): NoneType...' after 3 retries After: 'Connected (2416ms); Tools discovered: 83' Regression from #11383.
This commit is contained in:
parent
53e4a2f2c6
commit
3eeab4bc06
2 changed files with 232 additions and 3 deletions
|
|
@ -125,9 +125,28 @@ def _make_hermes_provider_class() -> Optional[type]:
|
|||
self._hermes_server_name, exc,
|
||||
)
|
||||
|
||||
# Delegate to the SDK's auth flow
|
||||
async for item in super().async_auth_flow(request):
|
||||
yield item
|
||||
# Manually bridge the bidirectional generator protocol. httpx's
|
||||
# auth_flow driver (httpx._client._send_handling_auth) calls
|
||||
# ``auth_flow.asend(response)`` to feed HTTP responses back into
|
||||
# the generator. A naive wrapper using ``async for item in inner:
|
||||
# yield item`` DISCARDS those .asend(response) values and resumes
|
||||
# the inner generator with None, so the SDK's
|
||||
# ``response = yield request`` branch in
|
||||
# mcp/client/auth/oauth2.py sees response=None and crashes at
|
||||
# ``if response.status_code == 401`` with AttributeError.
|
||||
#
|
||||
# The bridge below forwards each .asend() value into the inner
|
||||
# generator via inner.asend(incoming), preserving the bidirectional
|
||||
# contract. Regression from PR #11383 caught by
|
||||
# tests/tools/test_mcp_oauth_bidirectional.py.
|
||||
inner = super().async_auth_flow(request)
|
||||
try:
|
||||
outgoing = await inner.__anext__()
|
||||
while True:
|
||||
incoming = yield outgoing
|
||||
outgoing = await inner.asend(incoming)
|
||||
except StopAsyncIteration:
|
||||
return
|
||||
|
||||
return HermesMCPOAuthProvider
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue