fix(mcp-oauth): close inner SDK auth generator on teardown (salvage #38198) - #63495
fix(mcp-oauth): close inner SDK auth generator on teardown (salvage #38198)#63495NaMinhyeok wants to merge 2 commits into
Conversation
|
Thanks for preserving the original fix and replacing the timing-dependent regression with an HTTPX teardown test. Current main still creates the delegated SDK flow at The new regression drives Automated hermes-sweeper review. |
|
Downstream validation result: this cleanup is useful, but it is not sufficient by itself for the live timeout reported in the related issues. I applied this PR's deterministic With both this cleanup and the SDK lock-scope fix from modelcontextprotocol/python-sdk#3243 applied, the same connection completed in under a second and listed 171 tools. Two existing downstream MCP servers (24 and 7 tools) also remained healthy, and Hermes' 423 MCP tests passed. So I believe this PR should still land for deterministic delegated-generator cleanup, while the functional deadlock itself also needs the SDK fix. |
What does this PR do?
This is a transparent current-
mainsalvage of #38198 by @igorhvr. Igor's original fix commit was cherry-picked with authorship preserved, then rebased onto currentmain; the follow-up commit replaces the GC/sleep-based regression test with a deterministic HTTPX timeout test in the existing bidirectional-flow suite.Hermes manually bridges the MCP SDK's bidirectional OAuth async generator. It forwards
.asend(response), but previously did not close the delegated generator when HTTPX tore down the outer auth flow after a timeout or cancellation. The SDK generator could remain suspended while holding its AnyIO lock, then be finalized from another task and raise:Closing the inner generator in
finallyreleases the lock from the owning task and leaves the cached provider reusable.Related Issue
Fixes #38193
Related: #31987, #49543
Salvages and supersedes the stale current-main integration of #38198.
Type of Change
Changes Made
tools/mcp_oauth_manager.py: always close the delegated SDK auth-flow generator after normal completion, timeout, cancellation, or outer-generator closure.tests/tools/test_mcp_oauth_bidirectional.py: exercise the realhttpx.AsyncClient(auth=provider)teardown path with a forcedReadTimeout, assert immediate lock release, then reuse the same provider successfully.How to Test
Run the focused OAuth suite:
Result: 119 passed, 0 failed.
Run static/cross-platform checks:
Result: both pass.
Regression proof: with the
finally: await inner.aclose()change temporarily removed, the new timeout test fails synchronously because the SDK lock remains held and teardown emits the reported AnyIO ownershipRuntimeError. With the fix restored, it passes and the second request through the same provider returns 200.Full-suite run after installing
.[all,dev]:Result: 40,419 passed, 38 failed. The 38 local-environment/platform failures are in 15 unrelated files outside this two-file diff (host Claude OAuth credentials, macOS
/tmp→/private/tmpresolution, Linux/systemd assumptions, platform SDK version drift, and timing-sensitive process tests). All MCP OAuth tests passed in the full run.Checklist
Code
pytest tests/ -qand all tests pass — full-suite local-environment failures are documented aboveDocumentation & Housekeeping
cli-config.yaml.exampleupdate — N/ACONTRIBUTING.md/AGENTS.mdupdate — N/A