Skip to content

Complete the integrations migration: OpenAPI bindings to D1 tables, OAuth app UI, legacy value cleanup (#980) - #985

Merged
kody-bot merged 4 commits into
mainfrom
cursor/integrations-followup-4bbb
Jul 27, 2026
Merged

kody-bot merged 4 commits into
mainfrom
cursor/integrations-followup-4bbb

Conversation

@kentcdodds

Copy link
Copy Markdown
Owner

Closes #980.

Finishes the migration #979 started: after this PR the values store holds no platform-owned data, and the only evidence of the old approach left in the codebase is the migration SQL (and the migration tests that seed legacy fixtures).

OpenAPI binding storage (migration 0102)

_openapi:<name> value blobs move to first-class tables: user_openapi_bindings (config: spec/api URLs, auth, selection, flags) + user_openapi_binding_operations (one row per resolved operation). Per-operation child rows replace rewriting an up-to-900 KB JSON blob on every openapi_binding_refresh; staying in D1 (vs KV/R2) keeps the data move a pure SQL migration and keeps per-user isolation structural via composite keys, matching the 0101 design. The migration backfills, asserts counts (including non-empty unique operation slugs — a bad row aborts loudly via the assertion trap rather than an opaque constraint error), and deletes the legacy value rows. Both production rows (canva, sentry) were validated against the migratable predicate and the full zod schema before this PR was opened, and were backed up.

OAuth app management UI

New /account/integrations/apps/:appSlug page: app config (secret names only, never values), the connections sharing the app, and a rotate-credentials form that lists affected connections and requires explicit confirmation. Rotation reuses the integration_oauth_app_rotate_credentials service path and merges the client-secret's existing allowed hosts instead of replacing them. Sidebar app group headers and connection detail pages link to it.

Legacy machinery removal + data hygiene (migration 0103)

  • value-name-guards.ts and all reserved-prefix special-casing deleted from value_list/value_set, search, and the account values UI — _integration:*/_openapi:* are ordinary value names now.
  • Migration 0103 deletes the 3 leftover _integration:* rows (abandoned connect-flow attempts, never completed a token exchange; backed up before deploy), NULLs refresh_token_secret_name where the named secret was never written (5 production rows — prevents a confusing missing-secret error on any 401-triggered refresh), and normalizes URL-shaped required_hosts_json entries to bare hosts (1 production row: linkedin).
  • Going forward the connect flow only persists refreshTokenSecretName when the secret actually exists, and required hosts are normalized from URLs to hostnames at save time.

Testing

  • npm run validate green (format, lint, typecheck, 1459 unit tests, 20 Playwright e2e, MCP e2e, migrations + primitives checks).
  • New migration tests: openapi/migration.node.test.ts (backfill field-for-field, abort on non-migratable/missing/duplicate slug), integrations/hygiene-migration.node.test.ts (all three hygiene behaviors).
  • Manual GUI test of the app page + rotation against a seeded local account, with DB-level verification that the rotated secret and app row were written:

oauth_app_page_and_credential_rotation_demo.mp4

Production data preflight (already done)

  • Fleet-wide audit via production D1: exactly 2 _openapi:* rows (both schema-valid), 3 leftover _integration:* rows, 5 orphaned refresh-token references, 1 URL-shaped host. Backup artifact captured before deploy.
  • Post-deploy follow-ups tracked in Integrations follow-up: OpenAPI binding storage, OAuth app UI, and data cleanup #980: delete the leftover <provider>-client-id values (bundle-grep audit in flight) and collapse github/github-kent into one OAuth app (both client secrets verified valid against GitHub's check-token endpoint — same GitHub app, two secrets).
System recap — extends primitives (medium risk)

Mode: recap · Base: main @ e508c8f3 · Head: a9fe6b0b

Classification: extends — no new primitives; OpenAPI bindings storage and the values contract change shape, integrations gain an account-UI management surface.

Primitives touched

Primitive Group Impact
openapi-bindings assistant extends — storage moves from values rows to user_openapi_bindings + user_openapi_binding_operations
d1-app-db storage extends — migrations 0102 (new tables + backfill + legacy delete) and 0103 (data hygiene)
values assistant extends — platform-reserved name prefixes removed; all names are ordinary values
integrations assistant extends — connect flow persists refresh secret name only when written; hosts normalized at save
app-ui surfaces extends — new OAuth app detail + rotate-credentials page under /account/integrations
mcp-server surfaces composes — openapi capabilities re-wired to the new service; contracts unchanged
capability-registry assistant composes — registry loads bindings from the new tables

System map

OpenAPI capabilities and the registry now read bindings from dedicated D1 tables; the account UI gains an OAuth app page whose rotation writes through the integrations service; the values store loses its platform-reserved filtering.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	mcpServer["mcp-server<br/>MCP endpoint (/mcp)"]:::touched
	capabilityRegistry["capability-registry<br/>Capability registry"]:::touched
	openapiBindings["openapi-bindings<br/>OpenAPI provider bindings"]:::extended
	d1AppDb["d1-app-db<br/>D1 app database"]:::extended
	valuesP["values<br/>Values"]:::extended
	integrationsP["integrations<br/>OAuth integrations"]:::extended
	appUi["app-ui<br/>Browser app (Remix 3)"]:::extended
	secretsP["secrets<br/>Secrets"]:::untouched
	mcpServer -->|"openapi_binding_* capabilities"| openapiBindings
	capabilityRegistry -->|"loadOpenApiBindingsForRegistry"| openapiBindings
	openapiBindings -->|"user_openapi_bindings + operations tables (0102)"| d1AppDb
	valuesP -->|"reserved-prefix guards removed; 0103 deletes leftover rows"| d1AppDb
	appUi -->|"/account/integrations/apps/:appSlug + rotate POST"| integrationsP
	integrationsP -->|"rotate writes client secret, merged allowed hosts"| secretsP
	integrationsP -->|"refresh_token_secret_name NULLed, hosts normalized (0103)"| d1AppDb
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Before / after

Before After
OpenAPI binding storage value_entries row _openapi:<name>, whole ≤900 KB blob rewritten per refresh user_openapi_bindings row + one user_openapi_binding_operations row per operation
Reserved value names _integration:/_openapi: hidden from list/search/UI, blocked in value_set no reserved prefixes; all names ordinary
OAuth app management capability-only rotation account UI page with affected-connections preview + confirmation

Invariants

Per-user isolation preserved structurally: both new tables use composite (user_id, …) primary/foreign keys; every new query path is userId-scoped.

Open in Web Open in Cursor 

cursoragent and others added 4 commits July 27, 2026 13:48
Move _openapi:<name> snapshots out of value_entries into
user_openapi_bindings + user_openapi_binding_operations. Per-operation
child rows replace whole-blob rewrites on refresh; migration 0102
backfills and deletes the legacy value rows.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Add /account/integrations/apps/:appSlug showing app config, the
connections sharing it, and a rotate-credentials form that lists
affected connections and requires confirmation.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
…iene migration

Remove the _integration:/_openapi: reserved-name machinery now that no
platform data lives in value_entries. Migration 0103 deletes leftover
_integration:* rows, clears refresh-token secret names that were never
written, and normalizes URL-shaped required hosts. Connect flow now
persists refreshTokenSecretName only when the secret exists and
normalizes URL-shaped required hosts at save time.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Migration 0102 now requires non-empty unique operation slugs so bad
rows abort via the assertion trap instead of an opaque constraint
error. Credential rotation merges the client-secret's existing allowed
hosts instead of replacing them. Drop leftover account-values name
indirection and document the openapi account-deletion order.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@kody-bot, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6878f344-a16a-43f7-8c47-c018d4668a4f

📥 Commits

Reviewing files that changed from the base of the PR and between 85923a4 and a9fe6b0.

📒 Files selected for processing (57)
  • docs/contributing/adding-capabilities.md
  • docs/contributing/architecture/data-storage.md
  • docs/contributing/architecture/integrations.md
  • docs/contributing/architecture/openapi-bindings.md
  • docs/guides/oauth.md
  • docs/guides/openapi-integrations.md
  • packages/worker/client/routes/account-integrations.tsx
  • packages/worker/client/routes/account-values.tsx
  • packages/worker/client/routes/index.tsx
  • packages/worker/client/routes/secret-normalization.ts
  • packages/worker/client/search-filter.node.test.ts
  • packages/worker/migrations/0102-user-openapi-bindings.sql
  • packages/worker/migrations/0103-integration-data-hygiene.sql
  • packages/worker/src/app/account-data-targets.ts
  • packages/worker/src/app/account-integrations-data.node.test.ts
  • packages/worker/src/app/account-integrations-data.ts
  • packages/worker/src/app/account-values-data.ts
  • packages/worker/src/app/document-head.ts
  • packages/worker/src/app/handlers/account-integrations.node.test.ts
  • packages/worker/src/app/handlers/account-integrations.ts
  • packages/worker/src/app/handlers/account-secrets.node.test.ts
  • packages/worker/src/app/handlers/account-secrets.ts
  • packages/worker/src/app/handlers/account-values.node.test.ts
  • packages/worker/src/app/handlers/account-values.ts
  • packages/worker/src/app/loader-data.ts
  • packages/worker/src/app/router.ts
  • packages/worker/src/app/routes.ts
  • packages/worker/src/integrations/hygiene-migration.node.test.ts
  • packages/worker/src/integrations/service.node.test.ts
  • packages/worker/src/mcp/capabilities/integrations/integration-save.node.test.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-delete.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-get.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-list.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-roundtrip.node.test.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-save.ts
  • packages/worker/src/mcp/capabilities/registry.ts
  • packages/worker/src/mcp/capabilities/values/value-capabilities.node.test.ts
  • packages/worker/src/mcp/capabilities/values/value-delete.ts
  • packages/worker/src/mcp/capabilities/values/value-get.ts
  • packages/worker/src/mcp/capabilities/values/value-list.ts
  • packages/worker/src/mcp/capabilities/values/value-set.ts
  • packages/worker/src/mcp/secrets/allowed-hosts.ts
  • packages/worker/src/mcp/secrets/allowed-string-list.node.test.ts
  • packages/worker/src/mcp/tools/execute.ts
  • packages/worker/src/mcp/tools/search-entity-plugins/value.ts
  • packages/worker/src/mcp/tools/search-entity-registry.node.test.ts
  • packages/worker/src/mcp/values/service.node.test.ts
  • packages/worker/src/mcp/values/value-name-guards.node.test.ts
  • packages/worker/src/mcp/values/value-name-guards.ts
  • packages/worker/src/openapi/binding-service.ts
  • packages/worker/src/openapi/binding-shared.node.test.ts
  • packages/worker/src/openapi/binding-shared.ts
  • packages/worker/src/openapi/migration.node.test.ts
  • packages/worker/src/openapi/repo.ts
  • packages/worker/tsconfig-client.json
  • tools/migration-ledger.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/integrations-followup-4bbb

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kody-bot
kody-bot marked this pull request as ready for review July 27, 2026 14:28
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-985.kody-a99.workers.dev

Worker: kody-pr-985
D1: kody-pr-985-db
KV: kody-pr-985-oauth-kv

Mocks:

@kody-bot
kody-bot merged commit 948dc72 into main Jul 27, 2026
17 checks passed
@kody-bot
kody-bot deleted the cursor/integrations-followup-4bbb branch July 27, 2026 14:35
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.

Integrations follow-up: OpenAPI binding storage, OAuth app UI, and data cleanup

3 participants