feat(search): connect multiple accounts per integration
Merged
WA
2d ago+34402+34402+01234567890123456789012345678901234567890123456789-259-259-012345678901234567890123456789
Summary
- Members can connect several accounts of one OAuth integration, e.g. Google Drive in two tenants, up to 5 live accounts per provider. This applies to every OAuth integration, including Slack and GitHub, and to the MCP connectors that can identify an account
- One migration,
0453: credentials are now unique per provider subject and tenant, for both OAuth and managed MCP. It builds the new indexes concurrently, then drops the old ones. Nothing used the old indexes as conflict targets, and the new ones are supersets, so the build can't fail on existing data - Connecting: Add account creates a new credential, an already connected identity is refused, and reconnect stays pinned to its account. Disconnecting frees the slot, so a member can switch accounts; the same identity returning restores its credential. Invitation links only refresh the existing account
- GitHub setup lists installations across a member's accounts and verifies through whichever one administers the installation. One failing account doesn't block the others. Slack sender resolution accepts any linked account and still refuses an unlinked sender or one linked to another person
- Credential block Find operations keep returning the person's first connected account, so a workflow's account doesn't change when someone adds another. List reaches the rest
- People list groups MCP connections per server, so several accounts can't exceed its summary limit
- Viewer connector status and the enrollment page no longer assume one row per option. Docs and READMEs updated
- Integrations UI: accounts are called accounts, an account needing reconnect gets a Reconnect button, the disconnect dialog names only that account, and Add account stays out of a single account's view
- Connecting from any surface refreshes what Search can reach
Managed MCP
- Reads the signed-in account through each connector's identity tool during OAuth completion:
notion-get-self,get_account_info(Granola),fireflies_get_user,whoami(Coda),get_user_details(HubSpot). Connections are now named for the account - OAuth and MCP share
resolveAccountWrite, so add, reconnect, and limit rules can't drift - Existing connections keep working as before: a lone account reconnects even if the identity tool fails, and a disconnected connection comes back with the same id, so workflows pinned to it survive
- The MCP attempt version moves to 4, so an old instance rejects a new attempt during the deploy rather than overwriting the wrong account's tokens. An attempt in flight at deploy asks the person to connect again
- Anything unexpected from a provider yields no identity rather than a guess. An unidentified connection keeps working
- Existing connections are identified from their stored tokens when someone adds an account. If those tokens no longer work, the add is refused with a prompt to reconnect first
- Zoom, Lucid, and Databricks stay at one connection: I found no documented way to identify the account
Existing users
- Nothing changes until someone adds an account. The migration doesn't touch rows, confirmed by upgrading a database with existing rows, replaying it, and recovering an interrupted index build
- After an add, List operations return each account, and a connector's member count counts accounts
Rollout
- Rollback: old code reads one row per option or server, so once a member has two accounts it picks one arbitrarily and reconnect can fail. Old MCP code could also overwrite one of them. Have those members disconnect the extra account. Restoring the old indexes needs a dedupe first
credential_addedfires for each added account, so subscribed workflows run for it
Type of Change
- New feature
Testing
- New
multiple-provider-accounts.integration.tson real Postgres: add, duplicate, per-provider limit, concurrent completion, reconnect pinning, disconnect then switch or restore, invitation links, Slack sender resolution, and the same for managed MCP including pre-existing connections. Ran on both the push and the SQL migration provisioning paths - Extended
github-installations.integration.ts,github-installations.test.ts, andtool-permissions.integration.ts(an approval stays on the disconnected account when a different one is added); newmember-integrations.test.tsandmcp-account-identity.test.ts. Saw the new behavior tests fail before the fix - Ran the affected unit suites, the MCP integration suites, biome on changed files,
check:migrations,check:api-validation:strict,check:explicit-any,check:boundaries,check:client-boundary,check:comment-hygiene,check:utils,check:test-patterns,check:schema-mock,check:source-conventions,check:file-names - New
org-redesignbrowser check with two accounts, one needing reconnect, including screenshots and the account chooser on Add account - Type-check on pre-push;
check:auditsand the full suite run in CI, not locally - Not yet tried with real provider accounts. MCP identity parsing follows vendor docs only: Notion's shape is documented, Granola and Fireflies document the fields but not the format, Coda comes from a catalog on its docs site, and HubSpot's user field is unconfirmed, so it can fall back to the account
Checklist
- Code follows project style guidelines
- Self-reviewed my changes
- Tests added/updated and passing (new tests pass the
test-auditauthoring gate) - No new warnings introduced
- I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)