fix(extension): deliver the pair code from the value in hand, and say when it cannot - #306
Conversation
… when it cannot The MCP learns a pair code exists only from the `pair-pending` frame. When that frame does not arrive, `awaitSessionReady` times out and reports the `not-ready` branch, whose hint says "sign in to the target site" — so the user is sent to check a browser session that is fine, while a live XXX-XXX sits in the popup. That is the shape observed on chrischall/mcp-host#639: four cold starts, each showing a pair code on screen, each reporting `pairCode: null` at the MCP. The block that sends it had TWO silent ways to produce exactly that. **1. It re-read the code it already held.** After writing the pending record it fetched the record back purely to get `pairCode` — a value already in scope as `result.pairCode`. A miss for any reason meant no frame and no log. The read was never even useful. `pendingKey` is `${identityHash}:${scopeHash}`, so anything stored at that key carries this same identity and therefore this same code (the code is `sha256(mcpPub ‖ extPub)`, pure). A stored one can only be EQUAL — or STALE, if written under a previous extension identity, in which case the in-hand value is the correct one and the stored one is wrong. Pure downside. **2. It discarded `sendOnLink`'s result.** That helper answers `false` on a socket that is not OPEN. An undelivered code therefore left no trace anywhere, so the MCP's misleading timeout was the only thing anyone had to go on. It now warns, naming the mcpId and the link, and says the code is in the popup — the one line that separates "the extension never asked" from "the user never approved". **What this does NOT claim.** Whether either path is what actually swallowed resy-mcp's frames is not established — I could not observe the extension's service worker. What is established is that both existed and both produce precisely the reported symptom. One is now impossible and the other is loud. ## Verification - `npx vitest run` — **1451 passed** (was 1448; three added). `tsc -b` clean. - The happy path was untested before this and now is: an untrusted MCP gets a `pair-pending` carrying a six-digit code, and no `ready`. - A test makes the pending-pair store read back empty and asserts the frame is sent anyway — that one fails on `main`, which is the regression. - A test closes the link and asserts the warning names the mcpId. Refs chrischall/mcp-host#639 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ERNU4b1U9wHdN2UWL2vhn
|
Claude finished @chrischall's task in 1m 45s —— View job Review complete ✅
Summary This is a tightly-scoped, well-verified fix. I traced both claims in the PR body against the actual code:
Test coverage — the three new tests in
I was not able to execute No correctness, convention, or security issues found. Verdict: pass |
|
✅ Auto-review verdict: pass — The fix correctly replaces a redundant, silently-fallible storage re-read with the already-in-scope pairCode value, and adds a loud warning when sendOnLink fails to deliver; both claims were verified against the code and the new tests are sound. |
🤖 I have created a release *beep* *boop* --- ## [2.6.1](v2.6.0...v2.6.1) (2026-09-08) ### Bug Fixes * **extension:** deliver the pair code from the value in hand, and say when it cannot ([#306](#306)) ([4c7695b](4c7695b)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The MCP learns a pair code exists only from the
pair-pendingframe. When that frame does not arrive,awaitSessionReadytimes out and reports thenot-readybranch, whose hint says "sign in to the target site" — so the user is sent to check a browser session that is fine, while a liveXXX-XXXsits in the popup.That is the shape observed on chrischall/mcp-host#639: four cold starts, each showing a pair code on screen, each reporting
pairCode: nullat the MCP. The block that sends it had two silent ways to produce exactly that.1. It re-read the code it already held
After writing the pending record it fetched the record back purely to get
pairCode— a value already in scope asresult.pairCode. A miss for any reason meant no frame and no log.The read was never even useful.
pendingKeyis${identityHash}:${scopeHash}, so anything stored at that key carries this same identity and therefore this same code (the code issha256(mcpPub ‖ extPub), pure — no nonce, nomcpId). A stored one can only be equal — or stale, if written under a previous extension identity, in which case the in-hand value is the correct one and the stored one is wrong. Pure downside.2. It discarded
sendOnLink's resultThat helper answers
falseon a socket that is notOPEN. An undelivered code therefore left no trace anywhere, so the MCP's misleading timeout was the only thing anyone had to go on. It now warns, naming the mcpId and the link, and says the code is in the popup — the one line that separates "the extension never asked" from "the user never approved".What this does not claim
Whether either path is what actually swallowed resy-mcp's frames is not established — I could not observe the extension's service worker. What is established is that both existed and both produce precisely the reported symptom. One is now impossible and the other is loud.
Verification
npx vitest run— 1451 passed (was 1448; three added).tsc -bclean.pair-pendingcarrying a six-digit code, and noready.main, which is the regression.Refs chrischall/mcp-host#639
🤖 Generated with Claude Code
https://claude.ai/code/session_011ERNU4b1U9wHdN2UWL2vhn