Repository navigation
5.7.0: one authorization URL per connect, not one per call - #36
Merged
Merged
Conversation
Prod, 2026-09-28: a four-item connect_resources with Gmail and Calendar both answering requirement=interaction handed the person two URLs. onInteraction threw on the first item, so Calendar was never started; the retry started it and elicited again, for a code the person's wallet tab already held. - connect_resources starts every live item before handing over a URL, then hands over one — the head's — and marks the other live codes at the same interaction endpoint `coveredBy` it. A code started while a URL is out is covered too. A retry waits on covered codes through pass 2 with progress. - A covered code gets its own URL only when a poll re-advertises it, or when it has been at the head of the queue (queue_position 1) for 30 s with no browser holding it (poll status `pending`, not `interacting`). A poll held by Prefer: wait=20 can answer up to 20 s stale, so 30 s after first seeing the head leaves at least 10 s for an open tab to take it. Covered codes are polled in drainMs/2 slices so the check is not rounds late. - onInteraction is called once per URL handed over, not per code minted, and is expected to return. A throw is still passed through. invoke now elicits natively after onInteraction returns, as connect_resources does. - pollConnection returns the 202 body's `status`, `queuePosition`, `queueDepth`, and `advertised` when that poll carried requirement=interaction. `stopOnAdvertise` lets the caller end a slice on a re-advertised code it has not handed over. - A re-advertised code without a Location header is recognised: Wallet poll.js re-advertises with AAuth-Requirement only, which was dropped. - surfaceNatively reads capabilities from the 2026-07-28 request envelope; serveStdio does not backfill getClientCapabilities(), so the stdio bin fell back to text on that revision. - tool.call records outcome: input_required. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CEukeoQvjrsXzfmBNDoZN3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Prod, 2026-09-28 16:16–16:18 UTC. Claude Code 2.1.283 on 2026-07-28 called
connect_resourcesonce with four Google items. Gmail and Calendar both answeredrequirement=interaction. The person got two URL elicitations:onInteraction(mcp.aauth.dev) threw the elicitation, which left pass 1 at the first item, so Calendar never started.onInteractionthrew a second elicitation. The wallet tab already held both codes:aauth.reach.connectedqueued: 2, then Calendar ackedweb_delivered.The Person Server queues every pending interaction per person and drains the queue into the open tab. The second URL was not needed.
What changes
coveredBythat URL, and so is a code started while that URL is out. A retry waits on covered codes through pass 2 with progress notifications.requirement=interaction), or (b) the code has sat at the head of the queue (queue_position1) for 30 s with the poll still answeringstatus: 'pending', not'interacting'. Why 30 s: the tab gets every queued code when it connects and acks within about 1 s (16:17:19.8 → 16:17:20 in the incident). But a poll held byPrefer: wait=20answers with the record as it was when the poll arrived, so it can be 20 s stale. 30 s after first seeing the code at the head leaves at least 10 s. Covered codes are polled in 15 s slices so the check isn't whole rounds late. A PS that sends nostatuscounts aspending, so its covered codes fall back to their own URL instead of waiting outCONNECT_MAX_MS.onInteractionis a hand-over hook. It is called once per URL handed over and is expected to return. A throw still passes through, with the URL recorded as handed over first.invokenow elicits natively after the hook returns, asconnect_resourcesdoes.pollConnectionreturns the 202 body'sstatus,queuePositionandqueueDepth, plusadvertisedwhen that poll carriedrequirement=interaction. A newstopOnAdvertiseoption ends a slice on a code the caller has not handed over. The poller is the proxy's ownpollUntilDone;@aauth/mcp-agentis not used.Locationis now recognised. Walletpoll.jsre-advertises withAAuth-RequirementandRetry-Afteronly, andinteractionFromrequiredLocation, so poll re-advertisements were dropped.surfaceNativelyreads client capabilities from the 2026-07-28 request envelope.serveStdiodoes not backfillgetClientCapabilities(), so the stdio bin fell back to text on that revision.tool.callrecordsoutcome: 'input_required'.Tests
connect-resources.test.ts, with real SDK clients on both eras:interaction. Oneinput_requiredcarrying one URL. The SDK driver's retry lands all four.onInteractionis called once.-32042with one URL. The retry lands all four.pendingat the head past the bound gets its own URL, not before the bound.interactinggets no URL however long it waits.Also:
connect.test.tscovers the poll fields and the Location-less re-advertisement.invoke-resume.test.tscoversinvokeeliciting natively once.npm run typecheck,npm test(220 passed),npm run build.Known gap (Wallet, not changed here)
If the person closes the wallet tab after it acked the queued codes, those codes stay
interacting. The PS's stranded-delivery guard (poll.js) re-advertises only records created withreach_delivery: true. A covered code in that state waits untilCONNECT_MAX_MS(10 min) and answerstimed_out.Not merged or published. That waits for Dick.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CEukeoQvjrsXzfmBNDoZN3