Skip to content

Connect card: pin the MCP install URL to the URL's org (fix wrong-org flash) - #1011

Merged
RhysSullivan merged 1 commit into
mainfrom
claude/mcp-url-org-flash
Jun 14, 2026
Merged

Connect card: pin the MCP install URL to the URL's org (fix wrong-org flash)#1011
RhysSullivan merged 1 commit into
mainfrom
claude/mcp-url-org-flash

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

A multi-org user loading /<orgB> while their session cookie still points at
orgA saw orgA's slug flash into the copyable npx add-mcp .../<slug>/mcp URL
before /account/me resolved — a worse-than-cosmetic bug, since the wrong
endpoint is copyable.

Cause

On first paint auth.organization comes from the SSR auth-hint, which is scoped
to the cookie's org (the gate deliberately doesn't do a WorkOS membership
lookup per document request). But the page is scoped to the URL's org. The
connect card reads useOrganizationSlug(), which __root.tsx seeded from
auth.organization.slug — so until /account/me (URL-scoped) resolved, the
card rendered the cookie org's slug.

Fix

Seed OrganizationProvider's slug from the URL's {-$orgSlug} param
(urlOrgSlug ?? activeSlug). The URL slug is the actual request scope and is
correct on the very first paint (it's in the path during SSR too), so the wrong
slug never appears. Falls back to the session slug on a bare URL, which
OrgSlugGate is about to canonicalize onto it anyway. OrgSlugGate keeps using
the session slug for canonicalization; only the install-URL context changes.

useOrganizationSlug is consumed only by the connect card
(McpInstallCard), so there's no other blast radius — the organizationId
consumers are untouched.

Verification

Typecheck/lint/format clean. The fix is a one-line seed change; correctness
holds across steady-state, the multi-org flash, bare-URL canonicalize, and
foreign-slug 404 (verified by reasoning — a sub-second first-paint flash is too
timing/highlight-brittle to pin in e2e reliably). Happy to demo live on a
two-org dev instance.

A multi-org user loading /<orgB> while their session cookie still points at
orgA saw orgA's slug flash into the copyable `npx add-mcp .../<slug>/mcp` box
before /account/me resolved. On first paint `auth.organization` comes from the
SSR auth-hint, which is scoped to the COOKIE's org — but the page is scoped to
the URL's org.

Seed OrganizationProvider's slug (which feeds the connect card via
useOrganizationSlug) from the URL's `{-$orgSlug}` param, falling back to the
session slug on a bare URL (which OrgSlugGate canonicalizes onto it anyway). The
URL slug is the actual request scope and is correct on the very first paint, so
the wrong slug never appears. OrgSlugGate keeps using the session slug for
canonicalization; only the install-URL context changes. useOrganizationSlug is
consumed solely by the connect card, so there's no other blast radius.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 405e3c0 Jun 13 2026, 09:22 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 405e3c0 Commit Preview URL

Branch Preview URL
Jun 13 2026, 09:21 PM

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown

Greptile Summary

Seeds OrganizationProvider's organizationSlug from the URL's orgSlug param when present, falling back to auth.organization.slug (the session/cookie org). This removes the sub-second flash where a multi-org user loading /<orgB> while their cookie pointed at orgA would momentarily see orgA's slug in the copyable npx add-mcp .../<slug>/mcp install URL.

  • One-line change (scopeSlug = urlOrgSlug ?? activeSlug): the URL slug is available on the very first paint (it is in the route path during SSR too), whereas auth.organization.slug comes from the SSR auth-hint, which is scoped to the cookie's org and is only corrected after /account/me resolves client-side.
  • OrgSlugGate and organizationId are untouched: the gate still canonicalises bare URLs using activeSlug, and organizationId (used by ConnectionOwner, OAuthClientForm, AddAccountModal, and OwnerDisplay) retains its pre-existing transient-cookie-org value during the flash window; the OrganizationContext comment explicitly marks both fields as UI-only hints with server-side enforcement of access.
  • useOrganizationSlug is consumed exclusively by McpInstallCard, so the blast radius of the slug change is fully contained; no consumer joins the two context fields in a way that would be harmed by the transient organizationId/organizationSlug pointing to different orgs during the flash.

Confidence Score: 5/5

Safe to merge — the change is a single-line seed that only affects the copyable install URL in the connect card, with no impact on auth, access control, or other org-scoped consumers.

The fix is minimal and well-bounded. useOrganizationSlug is consumed only by McpInstallCard, and the OrganizationContext is documented as a UI-only hint with server-side enforcement. The URL slug is available on the very first paint (including during SSR), so the fallback chain urlOrgSlug ?? activeSlug is correct in all three cases: URL present (uses correct scope), bare URL (falls back to session slug that OrgSlugGate will canonicalise), and foreign slug 404 (short-circuited upstream before this line is reached). No consumers combine organizationId and organizationSlug in a way that is harmed by the transient mismatch during the flash window.

No files require special attention.

Important Files Changed

Filename Overview
apps/cloud/src/routes/__root.tsx Adds scopeSlug = urlOrgSlug ?? activeSlug and passes it to OrganizationProvider so the connect-card install URL is seeded from the URL path on first paint rather than the potentially-stale cookie org slug.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant SSR as SSR Gate
    participant Root as __root.tsx (AuthGate)
    participant OrgCtx as OrganizationProvider
    participant Card as McpInstallCard
    participant Me as /account/me

    Note over Browser,Me: Multi-org user loads /<orgB> (cookie still has orgA)

    Browser->>SSR: "GET /<orgB>/..."
    SSR-->>Browser: "authHint = { org: orgA } (cookie scope), urlOrgSlug = orgB"

    Note over Root: BEFORE fix: scopeSlug = activeSlug = orgA slug (wrong)
    Note over Root: AFTER fix:  scopeSlug = urlOrgSlug ?? activeSlug = orgB slug (correct)

    Root->>OrgCtx: "organizationId=orgA.id, organizationSlug=scopeSlug"
    OrgCtx->>Card: useOrganizationSlug() returns orgB slug
    Card-->>Browser: "npx add-mcp .../<orgB>/mcp (correct on first paint)"

    Browser->>Me: GET /account/me (URL-scoped to orgB)
    Me-->>Root: "auth.organization = { id: orgB.id, slug: orgB slug }"
    Root->>OrgCtx: "organizationId=orgB.id, organizationSlug=orgB slug (steady state)"
Loading

Reviews (1): Last reviewed commit: "Connect card: pin the MCP install URL to..." | Re-trigger Greptile

@pkg-pr-new

pkg-pr-new Bot commented Jun 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1011

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1011

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1011

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1011

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1011

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1011

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1011

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1011

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1011

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1011

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1011

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1011

executor

npm i https://pkg.pr.new/executor@1011

commit: 405e3c0

@RhysSullivan
RhysSullivan merged commit 013fa38 into main Jun 14, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant