Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/plans/hosted-mcp-connection-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@ The seat learns each ending through §5's event, and the owner sees it on the ca
4. The material.
5. The delete.

**Our removal revokes one token, and the vendor's page revokes a whole client** (the staging walk, 2026-10-23). Step 3 sends the row's own token and no other, though what that ends is the vendor's to say (Google's reach is in §4). The page an entry names lists the applications an account has authorized, and its revoke ends one application's authorization for that account. Sentry's deletes every token and pending grant that authorization covers (`ApiAuthorizationsEndpoint.delete`, `src/sentry/api/endpoints/api_authorizations.py` in getsentry/sentry, read 2026-10-23). Sentry's MCP server is middleware over Sentry's API and holds an upstream authorization of its own (getsentry/sentry-mcp's README, read the same day), so the application on that page is the server's, never our `cimd` client (`integrations/hostedMcp/sentry.ts:23–28`). Revoking it there cuts every MCP client that authorization serves, that account's rows on every instance included, and its listing cannot show whether our revoke took. The notices that send a person to the page name Commonly as what to remove or review there (`frontend/src/i18n/locales/en.json:2033`, `:2216`), and Sentry's page lists no Commonly.

**A row learns of a vendor's revoke only by using its token.** It reads connected until a refresh is refused, which marks it `error` (`services/hostedMcpCredentialService.ts:254`, `:461`), and a refresh runs only when the recorded lifetime is missing or ends within a minute (above). Until then each call fails as the vendor answers it: a 401 or 403 is `credential_rejected` today (`services/hostedMcpToolDefinitions.ts:306–310`). The code these two paragraphs cite is read at `main` `87687037`.

A grant's `expiresAt` is capped at the Connection's when the row has one (§10.4).

## 10. Build order and named tests
Expand Down Expand Up @@ -485,6 +489,7 @@ Each step can be tested without the vendor, except the live measurements in step
- Measured live: that a removal's revoke took at Linear, shown by something other than its 200 (§3). Linear advertises one URL as both its `token_endpoint` and its `revocation_endpoint` (fetched 2026-09-29), so only a live removal shows which request that URL treats as a revoke.
- The entry's `page`: Linear's Security & access settings, whose "Authorized applications" list has "Revoke access" on each app (Linear docs, *Security & Access*, fetched 2026-09-30). The signed-in walk of 2026-10-15 measured its URL as `https://linear.app/settings/account/security`, where the list shows the connection as "MCP · Read · Revoke" (connector-ops 77962), and #2230 pins it. Before #2230 the entry named `https://linear.app/settings/security`, which is Linear's workspace admin page.
- Walked 2026-10-15 by a fresh account with a fresh Linear workspace (connector-ops 77850 and 77962, and the readiness matrix's Linear row). Connect granted `read openid`. A read grant of the 36 pins, in a pod that passes §12's rule, carried a native agent's call, ok in the trail, with #2206's marker ahead of the data, and the same call was ok again after #2211 deployed. The walk had no pi seat: under §12 a pod with one in it fails the rule, so a grant there is refused for every member. Still unmeasured: a removal's revoke taking effect at Linear.
- **A walk on another instance needs that instance's own key ring, backend URL and agent runtime** (the staging walk, 2026-10-23). The callback stores each pair under the instance's connector key ring, so an instance that offers any entry maps its own (`connectorSecrets.enabled`, #2533), or no callback can store a pair. The start's redirect and a `cimd` client id are URLs on the instance's backend URL (§4), so an instance whose backend URL names another advertises that instance's redirect, and its consents return there (#2529). C3 and C6 need an agent runtime on the instance to call the grant, so a walk on an instance without one witnesses neither. A cleanup revoke on the vendor's page reaches as far as §9 says, so a walk uses an account that holds no row on another instance: that row can lose its authorization with the walk's and still read connected (§9).
8. **The entries after Linear**, in the order connector-ops proposed (76040) and 76044 kept, with the batch of 2026-10-09 after Airtable (connector-ops 76914, Wren 76915): Atlassian, Airtable, Canva, Miro, ClickUp, Todoist, Calendly, then Google through a pre-registered client (§4). Each is its own entry PR, admitted as §11 says and walked as step 7 is. Google Calendar is built in parallel without jumping that order, since Atlassian and Airtable wait only on Sam's accounts (connector-ops 76137).
- **Wave 1** (Sam, 2026-10-19, relayed by connector-ops on the TASK-237 to TASK-246 rows): writes are allowed when the member approves them, and apps are chosen by what our users use. That replaces the hold and every order this step gives, which stay as the record, and Basecamp, Honeycomb and Stripe are off. Wave 1 is ten rows, built now: GitHub with writes, writes for the Linear and Atlassian entries, then Notion, Sentry, Supabase, PostHog, Asana, Cloudflare and GitLab. Any other app waits until it is chosen the same way. Each pin comes from a capture taken on a consenting account, never from docs, and Wren rules each entry's pin set in its PR thread under §3's rules. Every entry still meets the rest of this step, and no entry that pins a write merges before §5's binding of a parked call to its pin.
- **Held** (Sam, 2026-10-14, relayed by connector-ops in 77368): no new catalogue app is built until an outside user connects one, and "payment apps and new OAuth apps are out". Linear, Google Calendar, Atlassian and Airtable are built (`integrations/hostedMcp/entries.ts:20`). Every other entry this step names is held, and so is the order above. Its batch after Airtable rests on the 2026-10-09 measurements, and the 2026-10-13 sweep re-measured the batch's first three: Canva is conditional and parked, and Miro and ClickUp are conditional ([hosted-mcp-catalogue-sweep-2026-10-13.md](hosted-mcp-catalogue-sweep-2026-10-13.md)). So when the hold lifts, the next entry is ruled afresh, not taken from this list, and the next bullet is that ruling. The rest of this step stays as the design each entry follows when it is built.
Expand Down
Loading