fix(client): getAgentInfo() retries on StreamableHTTP session errors (FastMCP compatibility)#1232
fix(client): getAgentInfo() retries on StreamableHTTP session errors (FastMCP compatibility)#1232
Conversation
FastMCP returns 400 "Missing session ID" when listTools() is called after connect() if the session wasn't properly established. Add a single retry-with-fresh-connection in getAgentInfo() matching the session-retry logic already in withCachedConnection for the standard callMCPTool path. Also propagates customHeaders and debugLogs in connectOptions, calls trackStreamableHTTPUrl on connectMCP success, and adds mcp_transport field to AgentConfig for future caller-side transport hints. https://claude.ai/code/session_017GhLX3ZU8z7dFWarSxp1Nw
Per pre-PR review: StreamableHTTPError 401/403 should not trigger the session-reconnect retry (auth failures are not recoverable by reconnecting). Also chains the original sessionErr as retryErr.cause for diagnostic parity with withCachedConnection. Adds 3 new tests covering these paths (10 total). Documents mcp_transport field as not yet active for callMCPTool path. https://claude.ai/code/session_017GhLX3ZU8z7dFWarSxp1Nw
Pre-PR review noted the asymmetry with is401Error() used elsewhere. Add comment to prevent a future refactor from accidentally breaking the 403 case. https://claude.ai/code/session_017GhLX3ZU8z7dFWarSxp1Nw
|
Source issue #1231 closed as not-a-bug. @bokelley tested FastMCP 3.2.4 in four configurations; vanilla StreamableHTTP, What that means for this PR:
Flagging so reviewers have context. @bokelley owns the call on whether to close, rebase onto #1233's scope, or drop the retry and ship only the ancillary fixes. Triaged by Claude Code. Session: https://claude.ai/code/session_014GbxY2Aep3uSVvnkVvLT74 Generated by Claude Code |
|
Triage note (issue #1234): While you're in
No behavior change. Easy to inline here since you're already in the file — and the two changes are contextually related (both address MCP discovery failure UX). Skipping if this PR is too close to ready or you'd rather ship it separately. Generated by Claude Code |
When all MCP endpoint candidates 404 with no 401, the error now appends an actionable hint pointing operators to register the exact MCP path as `agent_uri` rather than the host root. Also fixes pre-existing Prettier formatting in mcp-get-agent-info-session.test.js. Refs #1232 Closes #1234 https://claude.ai/code/session_01QbdnKNvfQsVAHw7WQnyeUi
|
Done — folded in the
No behavior change; purely UX improvement on discovery failure. Generated by Claude Code Generated by Claude Code |
|
Noted — the _Generated by Claude Code Generated by Claude Code |
|
Diff reviewed — promoting from draft now. The One nit for reviewers: the Also note for reviewers: Generated by Claude Code Generated by Claude Code |
The previous wording said "Currently only affects the session-retry guard in getAgentInfo()" but the field has no active wiring in either path; the retry fires on all StreamableHTTPError codes regardless. Replace with accurate forward-compatibility statement. https://claude.ai/code/session_015Qp6t45J2JZ4ZtGqwGWh7r
|
Thanks for the thorough review, @bokelley. Applied the editorial fix in 71585b0: the The Generated by Claude Code Generated by Claude Code |
Closes #1231
Summary
AdCPClient.getAgentInfo()usedconnectMCP()with no post-connect retry logic. FastMCP and other stateful StreamableHTTP servers can return HTTP 400 "Missing session ID" onlistTools()after a successfulinitializehandshake (e.g., when a load balancer routes the two requests to different process instances that don't share session state). The error propagated bare with no recovery path.The standard
callMCPToolpath already handles this case viawithCachedConnection's evict-and-retry loop. This PR bringsgetAgentInfo()to parity.Changes:
SingleAgentClient.ts—getAgentInfo()MCP path: catchStreamableHTTPErrorfromlistTools(), re-connectMCP()with a fresh session, retry once. 401/403 fast-fail before reconnect (auth failures are not recoverable by reconnecting — matcheswithCachedConnectionguard atmcp.ts:209).sessionErris chained asretryErr.causeon double-failure, matchingwithCachedConnectiondiagnostic parity.SingleAgentClient.ts— propagatescustomHeadersanddebugLogsintoconnectOptions(previously silently dropped).mcp.ts—connectMCP()now callstrackStreamableHTTPUrl()on success so subsequentcallMCPToolcalls to the same URL skip the SSE fallback probe.mcp.ts— re-exportsStreamableHTTPErrorfor use by consumers.adcp.ts— addsmcp_transport?: 'streamable_http' | 'sse'toAgentConfig. Field is defined for forward compatibility and aligns with themcp_transportregistry field from adcp#3066 (Option B). Active wiring for thecallMCPToolpath is a follow-up.What was tested
node --test test/unit/mcp-get-agent-info-session.test.js— 10/10 pass (new tests cover: success no-retry, 400 retry, any-code retry, 401 fast-fail, 403 fast-fail, non-StreamableHTTPErrorno-retry, double-failure with cause chain, debug log, connectOptions forwarding)npx tsc --project tsconfig.lib.json --noEmit— pre-existing TS2688/TS5107 env errors only; no new type errors from these changesNits noted (not fixed)
test/unit/mcp-get-agent-info-session.test.jsuses a localStreamableHTTPErrorstub rather than importing from the real SDK. Tests validate the retry logic correctly; a structural mismatch with the SDK class would require an integration test to catch and is out of scope here.mcp_transportwiring incallMCPToolpath deferred (noted in JSDoc@remarks).Pre-PR review
Awaited<ReturnType>pattern correct, 401/403 guard resolves previous blocker, cause-chaining added, nits noted aboveSession: https://claude.ai/code/session_017GhLX3ZU8z7dFWarSxp1Nw
Generated by Claude Code