Highlights
Unified credential bindings
Source credentials (OAuth tokens, header secrets, client-credential pairs) are now stored as scoped credential bindings instead of plugin-specific shapes. Two consequences for users:
- One consistent UI for editing credentials across OpenAPI, MCP, and GraphQL sources. The old per-plugin credential forms are gone, replaced with a shared slot UI in
@executor-js/react(credential-bindings,credential-slot-bindings,oauth-sign-in). - Existing OpenAPI / MCP / GraphQL / google-discovery credentials are migrated into the new binding rows automatically on first launch (drizzle migrations
0007,0008,0010forapps/local). No user action required.
See where a secret or connection is used
A new executor.usages surface lets plugins declare every place they reference a secret or connection. The local UI uses it to:
- Show a "used in N sources / M bindings" summary on the secret detail page.
- Surface a clear "this secret is in use, remove the references first" toast (backed by the new
SecretInUseError/ConnectionInUseError) instead of letting the delete fail silently.
Plugin authors implement usagesForSecret / usagesForConnection; the executor fans out and concatenates.
MCP source detection works on more servers
Adding an MCP source now succeeds against servers that previously failed to probe.
- Bearer-auth MCP servers are detected on the initial probe instead of being misclassified as plain HTTP. Probe error messages also tell you what shape of auth challenge came back.
- 401 challenges that advertise
resource_metadata=are recognized as MCP-spec OAuth signals. - Probes accept RFC 6750-compliant body forms for the access-token check, so servers that don't honor the
Authorizationheader on the probe path still discover correctly. - A live-snapshot regression suite covers 29 real public MCP servers; new probes are checked against those snapshots before shipping.
- The MCP connection pool now keys by source identity, so two MCP sources pointing at the same upstream URL with different credentials no longer collide on one shared connection.
TypeScript code runs in dynamic-worker and QuickJS runtimes
Both runtimes strip TypeScript syntax before evaluation, so tools that pass TS source (annotations, as casts, type aliases, etc.) execute without a separate compile step. Previously you had to hand-strip types or precompile.
OpenAPI source UX
- Source-add screens reworked: cleaner flow, the freeform combobox lists the URL you typed first, scoped credential UI clarified.
- OpenAPI import size limit removed — large specs (Stripe, GitHub, etc.) import without truncation.
- The OpenAPI source edit page lets you change the OAuth2 token / authorization endpoint URLs without removing and re-adding the source.
- Source favicons render again on the sources list (regression from the source-credential cutover).
listSourceBindingsno longer 500s when called after a source has been removed.
OAuth reliability
A pile of small fixes to the OAuth flow, individually unspectacular, collectively meaning fewer mysterious "Sign in" failures:
- DCR registration declares the requested scopes in the body so providers that key on body-scope (rather than
scope=on the auth URL) issue refresh tokens with the right grants. - Refresh requests use the exact scopes from the original token grant; servers that reject scope upgrades on refresh stop returning
invalid_scope. id_tokenvalues returned alongside an access token are stripped before validation (some providers send malformed JWTs in this slot, which used to fail the whole exchange).- The
scopeparameter is omitted entirely when empty, instead of being sent asscope=. - OAuth endpoint URLs from discovery / DCR are validated; obviously-broken metadata fails fast with a clear message rather than later in the popup. Exposed as
assertSupportedOAuthEndpointUrl/isSupportedOAuthEndpointUrlfor plugin use. - Token-endpoint failures now include the upstream HTTP status + body summary in the surfaced error.
- Popup handling: the popup is reserved before the start request fires, and close events are detected reliably, so cancelling sign-in no longer leaves the UI stuck waiting.
Source-registration tools require approval
Tools that register new sources (e.g. addSource on the OpenAPI plugin) now go through the standard approval gate by default, the same way destructive tools do. Safer to point an agent at a workspace that has source-mutating tools available.
Migration safety for older CLI builds
If an older executor build opens a data directory that has been migrated by a newer build, you now get an explicit "this data directory was created by a newer version" error instead of a low-level SQLite schema mismatch crash. Drizzle migration preflight checks were also tightened so partially-applied migrations are caught earlier.
SDK additions
For plugin authors and embedders:
Usage,UsagesForSecretInput,UsagesForConnectionInput— the usages contract.CredentialBindingKind,CredentialBindingValue,ConfiguredCredentialBinding,ConfiguredCredentialValue,ScopedSecretCredentialInput,CredentialBindingRef,SetCredentialBindingInput,RemoveCredentialBindingInput,ReplaceCredentialBindingsInput,ResolvedCredentialSlot,CredentialBindingId— the new credential bindings surface.SecretInUseError,ConnectionInUseError— typed errors for blocked deletes.RefreshSourceInput,RemoveSourceInput,RemoveSecretInput,RemoveToolPolicyInput,CredentialBindingRow— new typed inputs / row types.ScopedDBAdapter,ScopedTypedAdapter— type exports for scope-aware storage adapters.- Plugin
clientConfigis threaded throughvite-plugininto the client bundle, so plugins can hand SDK-side config to their React surface without a separate config endpoint. - Schema compile perf: a new lint rule + hoisted
Schemacompilers keep parse-paths fast.
Fixes
executeWithPausenow settles correctly when the running fiber fails — previously a fiber failure could leave the execution hanging instead of surfacing the error. (#523)isDevModecorrectly identifies the compiled bun binary, so installed CLI builds no longer misdetect themselves as running from source. Thanks @grfwings (#699)- Drizzle migration handling parses migration metadata with a typed schema and reports outdated-client failures cleanly. Thanks @grfwings (#741)
- Frontend errors and API-client decode failures are now reported through the existing error-reporting path instead of being swallowed silently.
- Source forms keep local error messages on screen instead of clearing them on the next render.
- "Remove in-use source" surfaces a toast instead of a silent failure. (#530)
Breaking changes
SDK: makeTestConfig import path moved
The deep import path moved from @executor-js/core/sdk/testing to @executor-js/core/sdk/test-config. The package-root re-export is unchanged:
// still works
import { makeTestConfig } from "@executor-js/core/sdk";
SDK: per-plugin credential shapes replaced by credential bindings
If you authored a plugin that stored OAuth tokens or other credentials directly under a plugin-specific column, migrate to the unified CredentialBinding* surface. The in-tree plugins (OpenAPI, MCP, GraphQL, google-discovery) have all been ported — see them for reference. End users of the CLI / web UI are unaffected; existing rows migrate automatically.