Skip to content

Registry-powered onboarding: full-page picker, spec-carried auth, preset dismantle - #1851

Merged
RhysSullivan merged 61 commits into
mainfrom
onboarding-demo
Aug 30, 2026
Merged

Registry-powered onboarding: full-page picker, spec-carried auth, preset dismantle#1851
RhysSullivan merged 61 commits into
mainfrom
onboarding-demo

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

Reworks the add-integration flow around the integrations.sh registry (already deployed).

Picker. The connect dialog becomes a full-page picker at /integrations/browse: a card grid with infinite scroll, one card per addable surface, sourced from the registry. Presets stop being cards — the one exception is local-process servers (Chrome DevTools over stdio), which have no registry representation. Added-state matches on registry surface slugs. Only connectable surfaces are offered; click-time failures render on the card.

Auth. Specs and registry records carry how to authenticate, and the add flows consume it: spec-detected OAuth binds to first-party clients at connect time by endpoint host; registry header patterns seed API-key methods for GraphQL endpoints (Linear's no-Bearer key), spec-less REST (GitHub's PAT), and MCP servers (alongside detected OAuth, or when the probe can't reach the server). Registry spec overrides prefill the add flow's overrides editor (Neon's console-cookie schemes). Query-located API keys become real placements instead of empty methods; cookie-located schemes stop producing unfillable methods.

MCP protocol resilience. Servers that echo the proposed 2026-07-28 revision while emitting 2024-era results (Walmart's MCP) made discovery fail; on that exact signature discovery retries with legacy negotiation and the add pins versionNegotiation on the integration. Discovery errors keep the underlying message.

Google Discovery URLs pasted by hand now route through the same adapter presets used, via spec-format URL detection.

Removed: provider preset cards (Google/Microsoft families, Profile/My Graph Operations), kind facet chips, Petstore and Emulate demo presets.

e2e: the catalog-search scenario is updated and four scenarios (provider-plugins-ui, google-photos-preset-ui, first-party-oauth, google-health-checks) are rewritten against the registry path; all five pass, as do the full unit, lint, format, typecheck, and route-tree gates after merging main.

The registry now returns named product rows (many products on one
vendor domain, like Microsoft Graph's workloads), so rows title by the
registry name when present instead of prettifying the domain, and row
keys carry the surface slug — one domain no longer collapses to one row
per kind. Named rows also make the preset dedupe effective for products
whose preset shares the name. The catalog origin is overridable via
VITE_PUBLIC_INTEGRATIONS_SH_ORIGIN for testing against a local registry.
The catalog-search scenario clicks the row's Add button, which is where
the action lives since the add/added rework.
…eset rows

Pasting a Discovery URL failed as 'not OpenAPI' while the identical URL
worked through a preset, because the spec-format adapter only engaged
when a preset supplied specFormat. Adapters can now recognize URLs, and
resolution falls back to detection when no explicit format is given.

A preset row and a catalog row for the same product no longer read as
two different kinds of thing: the row shows the registry's identity
(the domain) like every other result, and the preset supplies the add
flow.
The registry now says how a GraphQL surface authenticates — credential
kind, header pattern, and a note — because a GraphQL endpoint has no
spec document to say it itself. Adding Linear from the picker seeds the
auth-method editor with 'Authorization: {api_key}' (no Bearer prefix,
which is the whole point) and shows the note; the same facts ride along
when a preset owns the add flow. The detected-method footer says
'Declared by this API' rather than 'Read from the spec', which was
untrue for spec-less surfaces.
GitHub's official OpenAPI description declares no securitySchemes, so
the add flow offered OAuth (from the preset template) and nothing else,
while a PAT bearer header is how most calls actually authenticate. The
registry's declared placement now seeds an API key method alongside
detected OAuth; a spec-declared key method still wins over the
registry's version of the same fact.

Preset-to-catalog matching moves from rendered titles to a product key
(normalized name plus kind), so 'GitHub REST' and the registry's
'GitHub API' count as one product — titles and domains both proved
unreliable joins. MCP surfaces carry their auth facts too.
The probe stays authoritative when it lands: its OAuth and open-server
verdicts are unchanged, but the registry's declared header pattern now
replaces the generic Bearer guess on a bare 401, rides alongside
detected OAuth (GitHub's MCP takes a PAT bearer in clients without
OAuth support), and stands in entirely when there is no probe result —
an authless server is a fact the registry already knew.
Registry surfaces can carry RFC 6902 JSON Patch for a spec; the picker
passes it through and the OpenAPI add flow prefills its overrides
editor with it, so the patch is visible and editable rather than
applied invisibly. A preset's own overrides win, and a user's draft
wins over both. First use: Neon's spec declares the Neon console's
session cookies as security schemes — the registry's patch removes
them, and the add flow now offers exactly the real Bearer method.
An apiKey with in=query (Viator's legacy ?apiKey=) reached the add flow
as a method with a '(query)' label and zero placements — an empty,
unfillable card that would register as a no-auth method. Query keys now
become real query placements end to end (the invoke layer always knew
how to render them). Cookie-located schemes stop producing methods
entirely: placements carry header|query only, and a cookie scheme is
usually the vendor console's own session, not a mintable credential.
The registry search API now pages (offset over its full ranked list),
so the picker keeps appending as you scroll instead of stopping at the
browse head: a sentinel near the list end fetches the next page a
screen early, appended pages are keyed to the query so a keystroke
discards them, and rows that slide across page boundaries while the
live index moves are deduped. A failed page fetch is retried the next
time the sentinel scrolls into view rather than ending the list.
Three cards per row on desktop, two on tablet, one on mobile. Each card
carries the icon, Add/Added state, name, mono domain, and a two-line
description clamp; skeletons and the loading-more placeholders match
the card shape, and the infinite-scroll sentinel spans the full grid
width.
The registry can know a surface exists without knowing where it lives
(conjur.org's OpenAPI has no recorded spec URL); such surfaces rendered
an Add card whose click could only fail — and the failure appeared as a
page-top alert, three screens away from the card that was clicked.
Surfaces without a connect target are no longer offered, and the
remaining resolve-on-click path (kinds-only entries from older
registries) reports its failure on the card itself.
Adding Stripe MCP produced slug stripe_mcp (the add flow derives its
namespace from 'Stripe' + the surface word), but the card's added check
only tried the preset's bare name. The rendered title is now a
candidate too, and preset test ids carry the plugin key — two plugins
both naming a preset 'stripe' produced duplicate ids.
Demo fixtures are not integrations anyone ships with; both existed to
demo executor, and the picker is not the place for that.
Every connectable card now comes from the registry, which carries the
identity, spec or endpoint, auth facts, and overrides; the spec itself
declares how to authenticate, and a deployment's first-party OAuth
clients bind at connect time by endpoint host. Presets with a connect
URL stop being cards — only local-process servers (Chrome DevTools over
stdio) remain, since they have no registry representation yet. The
per-query identity borrowing, product-key preset enrichment, and its
flickering domain chips all go with it. Registry-hosted spec URLs
resolve against the configured catalog origin so a local registry is
self-consistent before a deploy.
Three causes. Typing toward a URL flipped the grid back to the browse
head mid-keystroke ('stripe.co' parses as a URL), so the list now
freezes on the last real query while only the detect hint changes.
Stale results swapped wholesale with no signal when the debounced
response landed; the grid now dims while a new query loads. And cards
without descriptions were shorter, so grid rows resized between result
sets; cards hold a uniform minimum height. Debounce drops to 150ms.
# Conflicts:
#	e2e/scenarios/provider-plugins-ui.test.ts
#	packages/plugins/mcp/src/sdk/discover.ts
#	packages/plugins/mcp/src/sdk/errors.ts
#	packages/plugins/mcp/src/sdk/plugin.ts
#	packages/react/src/pages/integrations.tsx
Upstream gave the multiplayer shell a connect-dialog affordance while
this branch replaced the dialog with the full-page picker; the sidebar
action navigates there instead.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 29, 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 d0b81e5 Commit Preview URL

Branch Preview URL
Aug 30 2026, 01:49 AM

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 29, 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 d0b81e5 Aug 30 2026, 01:50 AM

@pkg-pr-new

pkg-pr-new Bot commented Aug 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

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

@executor-js/config

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

@executor-js/execution

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

@executor-js/sdk

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

@executor-js/codemode-core

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

@executor-js/runtime-quickjs

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

@executor-js/plugin-file-secrets

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

@executor-js/plugin-graphql

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

@executor-js/plugin-keychain

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

@executor-js/plugin-mcp

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

@executor-js/plugin-onepassword

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

@executor-js/plugin-openapi

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

executor

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

commit: d0b81e5

Normalization canonicalizes a service-hosted Discovery URL onto the
central directory, which is right for identity but not universally
fetchable: the directory does not list every service. Google Ads
answers only on googleads.googleapis.com, so a URL a user pasted and
that works returned HTTP 404 through the adapter. The fetch now falls
back to the host the caller named rather than growing an allowlist of
every such service.
axiom.co/docs/restapi/versions/v2.json 404s; Axiom publishes the spec
in its docs repo. Verified through the add flow: 78 tools, api key and
oauth2 methods detected.
The Graph adapter only engaged when a preset supplied specFormat, so a
hand-pasted Graph URL fell to the plain OpenAPI path and died on the
43 MB monolith. It now recognizes the published monolith and executor's
slice assets, with or without a #preset selector — the same URL
detection the Google Discovery adapter already had. Already-sliced
hosted specs stay on the plain path, since they parse there fine.
Registry rows already carry the URL and auth indicators, so clicking
Add now registers in place: the card flips to a View link into the
integration's hub, the user stays on the picker, and several adds can
run at once. Each plugin exposes a headless useQuickAdd on its client
plugin — MCP probes and declares the detected methods (sharing the add
page's seed policy via mcpDetectedAuthSeeds), OpenAPI lets the spec
carry auth, GraphQL declares the registry's header pattern. A quick
add that cannot complete falls back to the configuration screen
prefilled with the same facts.

Also fixes two latent bugs this surfaced: the probe API response
schema stripped versionNegotiation, so the legacy-protocol pin never
survived the HTTP boundary; and per-row state keyed on domain|kind,
which conflated two same-kind products on one domain (Google Photos
Library and Picker).
What you already have floats to the front of the results, where its
View state is worth seeing. Fresh quick-adds hold their place for the
rest of the session — teleporting the card the user just clicked is
the jumping this page keeps having to unlearn — and float on the next
visit.
The float now covers the whole catalog, not just loaded rows:
installed integrations whose registry rows sit below the loaded page
get cards synthesized from the integration record, deduplicated
against loaded rows by claimed slug. Added-ness also matches the
namespaces the classic add pages derive (domain, display name), not
only the registry slug. The check icon goes; View carries the state.
Synthetic installed cards ran their own letter-mark fallback while the
sidebar resolved real icons for the same integrations — two resolvers
for one integration. They now run the identical cascade: preset icon
by exact identity, else the logo proxy from the integration's own URL.

The page also stops scrolling as a whole: the title, search, and
scratch row pin, and the results grid scrolls in its own container —
an endless list makes the page scrollbar meaningless and drags the
search box off screen.
The hook held the previous query's entries while the next loaded — by
design, to avoid flashing — but nothing recorded which query they
belonged to, so after the loading dim was removed a calendar search
showed Gmail cards at full opacity. Entries now carry their request
key and the hook exposes stale; the page keeps held rows only while
every token of the live text matches their name or domain, so
refinements keep their rows and new words drop the old ones instantly.

Also: extra pages reset when the request changes, which unwedges the
sentinel after a mid-flight query switch (the in-flight guard held it
until a scroll), and stops minutes-old extras reviving verbatim under
a fresh first page.
Every checkout registered the dev server as the same portless name on
the machine-shared multiplex proxy, so the proxy's duplicate-app
selector listed every parallel checkout's server under one host — and
its per-entry Kill button SIGTERMed whichever sibling you clicked.
The name now derives from the checkout directory, scoping the
selector (and its Kill button) to that checkout's own processes.
The setup text reads like the provider's own docs but is
machine-written registry data; a mono provenance line names the
source and links the domain's integrations.sh page so the reader
weights it accordingly.
The GitHub registry row quick-added with no auth methods: its spec
declares no security at all, the preset's OAuth endpoints only rode
the preset path, and the registry's PAT header pattern was dropped.
The OpenAPI quick add now pulls both across — a URL-matched preset
contributes its OAuth template, spec overrides, spec format, family,
and health check; the registry's header pattern becomes a declared
API-key method — while spec-derived defaults still apply when neither
source knows anything.

The AI-generated guidance line also gains thumbs: votes land in
analytics keyed by domain and credential label, so wrong registry
guidance is findable instead of silently misleading.
OAuth needs a registered app (or a DCR round-trip) before Connect does
anything, so an integration declaring both greeted most users with
"Register app" — a dead end — while the working method sat one tab
over. The modal now opens on the first non-OAuth method; OAuth stays
one click away, and a handoff-specified template still wins.
Credential guidance and favicons key on the integration's display URL,
which falls back to the spec URL when no base URL is stored — and a
registry quick add stores neither, so a spec hosted on a code host sent
guidance lookups to raw.githubusercontent.com instead of the product.
The registry row already names the product's domain; quick add now
records it as displayDomain and display derivation prefers it. The
field is declared at all three hops (extension input, HTTP payload,
handler map) — the handler's explicit field list is where such fields
silently die.
Selection carries the CLICKED surface instead of re-finding by kind,
and the kinds-only fallback disambiguates same-kind products by the
row's product name against surface slugs — two same-kind products on
one domain no longer merge one card's URL with the other's slug, auth,
and overrides. The OpenAPI quick add replicates the full add page's
method policy: preset OAuth wins outright; otherwise every
spec-detected method is preserved (one preview call, only when the
registry declared a header) and the registry key is appended only when
the spec has no key method. The MCP remote add carries
versionNegotiation through the payload schema and handler map, with a
round-trip test — the legacy pin previously died at the HTTP boundary.
URL presets hide only when registry-listed (built-ins and the
provider catalogs declare it), so a deployment's custom preset keeps
its card, and quick add matches the plugin's complete preset list.
Quick-add bridges render only for plugins with the hook and call it
unconditionally; duplicate plugin keys get one bridge.
Auth derivation previewed the raw spec while the add stored the
overridden one, so a preset's scope overrides were derived away; and
registry overrides wrongly beat preset overrides. One spec plan
(preset overrides first, mirroring the full page) now feeds both the
preview and the add, and the composition is a pure exported function
with tests pinning the contract: override-changed OAuth scopes survive
alongside an appended registry key, and a spec-declared key suppresses
the registry's.
An explicitly empty preset override list is a decision (suppress the
registry's patches), not an absence — presence-based selection now
mirrors the full page's ?? exactly. Both request payloads come from
one builder, and the tests assert the actual regression contract:
identical specFormat and specOverrides on the preview and the add,
so reverting either call site fails the suite.
performQuickAdd holds the whole operation with the two mutations
injected; the hook is a thin binder. The tests now capture what
preview and add actually receive — identical spec plan on both,
auth composed from the overridden document with the registry key
appended, the derived slug — so reverting either call site fails
the suite instead of slipping past helper-level assertions.
# Conflicts:
#	packages/plugins/mcp/src/react/AddMcpIntegration.tsx
#	packages/react/src/components/command-palette.tsx
#	packages/react/src/pages/integrations.tsx
Detected rows are titled by their kind now ('OAuth · Detected') and
the summary footer names the source and next step; six scenarios in
directories the local runs never covered still asserted the old
'Method 1 · Detected' copy and the removed aria-disabled block.
Three more CI-only scenarios still drove the removed connect dialog:
the disabled-API journey now enters through the preset deep link, and
the frontend-error test keys its loaded-signal on the header's Add
link. A ?preset= deep link also seeds the spec URL from the preset
itself — the dialog always passed &url= alongside, which hid that the
link never worked on its own.
@RhysSullivan
RhysSullivan merged commit 30ddd79 into main Aug 30, 2026
43 of 44 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