Multiple connections per plugin

One person, two accounts on the same connector — work and personal. Today the second connection does not add a choice, it removes one: the plugin stops resolving, and every inbound event fires every trigger the owner has. This is the flow, what breaks in it, and the two decisions left.

Where an event goes today

Today

both accounts match

Every provider normalizer already stamps the connection onto the event row. The matcher never reads it, so the two lanes converge and are indistinguishable by the time a run is enqueued.

Provider event webhook / push Normalize stamps conn id WORK connection A PERSONAL connection B Matcher org, kind, owner Trigger → run runs twice wrong account, silently

Proposed

pinned account passes

The trigger records which connection it listens to. The matcher compares it to the connection already on the event, and the other account stops one step short of a run.

Provider event webhook / push Normalize stamps conn id WORK pinned on trigger PERSONAL not pinned here Matcher + connection Trigger → run runs once dropped here

work connection personal connection animation loops; static for reduced-motion readers

What is already in place

Piece Where State
Two live connections per user per connector connections_user_connector_unique is on (org, connector, user, external_account_id) Storable today
Connection stamped on every event automationEvents.integrationConnectionId, set by every normalizer — Linear, Slack, Notion, Sentry, Teams, Gmail Recorded, never read for matching
Per-connection pin on the MCP proxy ?connection=<id> → pinnedConnection() in resolve-target.ts Works; nothing ever sets it
Connections as a list in the API plugins.list returns connections[] and accounts[] Shape already anticipates several
Chips in the trigger sentence TriggerSentence with ScopeChip, SelectChip An account chip fits the existing grammar

What breaks

Settled

Open: where the pin lives

Recommended

A column on automation_triggers

connection_id, narrowed in SQL beside the existing ownerUserId clause. A mis-match leaks a personal inbox into a work automation’s run — the same class of boundary that clause already guards in SQL rather than in JS. An FK makes a disconnect detectable instead of dangling.

Cost: a migration, and an explicit disconnect path, so a disconnect cannot silently widen a trigger back to “any”.

Inside the trigger config

Consistent with how scopes already work: the matcher already loads every (org, kind) candidate and filters in JS, and the editor and save path carry config for free. No migration.

Cost: no referential integrity, and the narrowing sits in the same JS as {mode:"me"} resolution — a config-shape bug or an unmigrated row fails open.

On the automation, not the trigger

One picker in the settings card, and it hands you the tool-access pin later for free.

Cost: an automation with a Linear trigger and a Slack trigger needs two values anyway, so it becomes a map — and the ask reads as per trigger.

Open: how per-account tools appear

Per-connection entries in the agent config

Literally gmail-work and gmail-personal in the agent’s MCP config.

Cost: desiredMcpServers() is built from the static catalog with no network, at boot. Per-connection entries make plugin provisioning depend on a live API call, and every connect or disconnect has to re-sync every machine.

Recommended

One entry, per-account tools from the proxy

The config stays gmail → bare URL, exactly as now. The proxy, seeing two live connections, advertises the tool set once per account under an account-qualified name and resolves the connection itself.

Same thing the agent sees — the account is in the tool name — with no config churn and nothing new to provision offline.

Not yet decided

Tool access during an automation run is deliberately out of scope for now: whether a run sees only its pinned account, or all of them with the choice stated in context. Worth settling before the tool surface ships, because a scheduled run is the case where acting as the wrong account is least likely to be noticed.