fix(mcp): bound and tear down one-shot OAuth fetches so auth can't hang#5789
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview
Tests expand coverage for teardown, buffered bodies, stalled body reads, oversized responses, and SSE vs JSON handling. Reviewed by Cursor Bugbot for commit 66c6f33. Configure here. |
Greptile SummaryThis PR prevents one-shot MCP OAuth requests from hanging indefinitely. The main changes are:
Confidence Score: 5/5The latest changes appear safe to merge.
Important Files Changed
Reviews (3): Last reviewed commit: "fix(mcp): bound and tear down one-shot O..." | Re-trigger Greptile |
4d6ff77 to
96bbd06
Compare
|
@cursor review |
The SSRF-guarded fetch used for MCP OAuth (discovery, DCR, token exchange/
refresh, RFC 7009 revocation) created a fresh pinned undici Agent per request
and never tore it down, and returned the live response so the SDK's lazy body
read happened outside any deadline. The MCP SDK sets no timeout on OAuth legs,
so a stalled body read or a leaked keep-alive socket could leave the flow — and
the browser waiting on it — pending indefinitely ("Connecting… forever").
- Buffer the (always-small) OAuth JSON body inside the guard, under the composed
deadline/caller AbortSignal, then return a detached in-memory Response. undici's
bodyTimeout only measures idle gaps between chunks and cannot bound a slow-drip
body; the AbortSignal is the only true wall-clock deadline over the body read.
- Destroy (not close) the per-request pinned Agent on every path so a one-shot
leg can't strand its keep-alive socket, and teardown itself can't hang.
- Fix the same per-request Agent leak in the auth-type probe.
- Returning a normalized global Response also surfaces provider token-endpoint
errors cleanly instead of the SDK's opaque parse failure.
96bbd06 to
66c6f33
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 66c6f33. Configure here.
Reverts the temporary McpHttpDiag instrumentation from #5782 now that it has served its purpose: it confirmed the OAuth fetch fix (#5789) makes the flow reach and complete the token exchange, and it isolated a separate, pre-existing transport-initialize stall (Gauge returns 200 + session then no body for 30s, reproducible only from the staging egress — not from a fresh IP, and not with the SDK/h2/pinning/tee in isolation). - Delete apps/sim/lib/mcp/http-diagnostics.ts. - Restore client.ts transport fetch to `...(pinned ? { fetch: pinned.fetch } : {})`. - Restore oauth/auth.ts fetchFn to the plain SSRF-guarded default. - Drop the diagnostic-specific test assertion. Removes the body tee() from the transport hot path so the streamable-HTTP connection runs clean while the transport stall is investigated separately.
Summary
Agentper request and never destroyed it, and returned the live response so the MCP SDK read the body outside any deadline. The SDK sets no timeout on OAuth legs, so a stalled body read or an accumulating leaked keep-alive socket could leave the callback (and the browser waiting on it) pending indefinitely.What changed
AbortSignal, then return a detached in-memoryResponse. undici'sbodyTimeoutonly measures idle gaps between chunks and cannot bound a slow-drip/stalled body — theAbortSignalis the only true wall-clock deadline over the body read, so the read now lives inside it.destroy()(notclose()) the per-request pinned Agent on every path so a one-shot leg can't strand its keep-alive socket, and teardown itself can't hang on an in-flight request.Responsealso makes provider token-endpoint errors surface cleanly instead of the SDK's opaque"[object Response]"parse failure.Aligned with the official MCP TypeScript SDK's contract (all lifecycle/deadline enforcement lives in the injected
fetch) and undici best practice (per-request pinned Agent → buffer body →destroy();AbortSignalas the authoritative deadline).Type of Change
Testing
lib/mcpsuite green (366 tests).auth()with the new guard behavior — reaches the token POST, returns in ~1.9s, 0 leaked Agents (8 created / 8 destroyed), and surfaces a cleaninvalid_grantmessage.tscclean; biome clean.McpHttpDiaglogging from diag(mcp): comprehensive HTTP logging for OAuth + streamable-HTTP transport #5782 is still live and will confirm the token POST now fires; a follow-up removes it once verified).Checklist