add-mcp-step.test.tsxfix(connections): stop the add-MCP and recorder OAuth flows failing silently Review findings on the connections UI. All of them share a shape: the flow finished, nothing reported an error, and the user was left with a connection that does not do what they asked for. B1 The mandatory description was dropped for any branded server. The row was matched by raw string against the URL the user typed, but the probe echoes the input while the callback stores the driver's own URL, which for Pylon carries a trailing slash. The dialog then closed as though it had saved. Matched on a normalized resource identifier now (the same rule the server uses), against the URL frozen when the popup opened rather than the still editable field, and a genuine miss is toasted instead of swallowed. B2 A popup blocked by Safari or Firefox left isConnecting set, which disables every control on the recorder credential form: the card was inert until a page reload. Both blocked-popup paths now clear the flag, drop the pending key and say what happened. S3 Refocusing Cedar mid-flow deleted the pending record, so finishing the sign-in afterwards did nothing. The MCP branch now refetches first and keeps the record until the row actually reports connected. S4 Whether a recorder uses MCP OAuth was re-derived at focus time from live query state, which is undefined mid-refetch; an MCP recorder read as non-MCP goes to Klavis and 422s. The decision is recorded in the pending payload when the flow starts. S5 A token connection saved from the Organization tab silently became a personal one. addMcpConnection has no org variant, so those forms now say so; the manual client-app path does have one and is routed to credentialVault.upsertOrg when the scope says org. S6 Picking a provider and then pasting a different URL left the old brand name on the new server. A name this form derived is cleared with the verdict; one the user typed is kept. S8 The section's reconnect listener stood down for the whole dialog, though only the add step ever competes for the message. Ownership now follows the in-flight sign-in, reported by the step itself. Also: one ScopeSelect instead of two hand-duplicated selectors whose non-admin notes had already drifted; a non-admin who owns an org row is no longer told to ask themselves; providerName reused instead of re-derived; a redundant capabilities cast dropped; and the catalog comment no longer claims a new entry needs no server change, which a null providerId disproves. apps/mail's jest allowlist did not name modules/integrations. Added, with six tests over the two OAuth completion paths; all six fail against the code before this change. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 1:06 PM