Skip to content

fix: keep executor.jsonc in sync with source add/remove#408

Open
RyanNg1403 wants to merge 1 commit intoRhysSullivan:mainfrom
RyanNg1403:fix/sync-config-on-engine-source-remove
Open

fix: keep executor.jsonc in sync with source add/remove#408
RyanNg1403 wants to merge 1 commit intoRhysSullivan:mainfrom
RyanNg1403:fix/sync-config-on-engine-source-remove

Conversation

@RyanNg1403
Copy link
Copy Markdown

Summary

Two related bugs caused executor.jsonc and the runtime DB to drift out of sync, so sources resurrected after a UI delete and OAuth auth blocks vanished after a restart. Both are reproducible against executor@1.4.9.

  • Engine-level removeSource hook in the openapi, mcp, and graphql plugins now mirrors the SDK-level removeSpec / removeSource and writes back to configFile.
  • apps/local config-sync now translates McpAuthConfigMcpConnectionAuth and forwards it to executor.mcp.addSource, so remote MCP auth survives boot.

Why

Bug 1 — engine-level remove never updates executor.jsonc

The SDK methods (openapi.removeSpec, mcp.removeSource, graphql.removeSource) correctly call ctx.storage.removeSource(...) and configFile.removeSource(...). The plugin lifecycle hook also named removeSource — used by the engine's executor.sources.remove and therefore by the HTTP sources.remove handler and the web UI delete button — only deleted plugin storage rows. The DB was cleaned, but the file wasn't.

On the next boot, apps/local boot-sync read executor.jsonc, saw the still-present source entry, and re-upserted it into the DB. From the user's perspective, deleted sources resurrected after every restart.

Bug 2 — boot-sync silently strips MCP auth

apps/local/src/server/config-sync.ts rebuilt the executor.mcp.addSource(...) call for remote MCP sources without passing source.auth. addSource then wrote back to executor.jsonc via configFile.upsertSource(...) with auth: undefined — so { kind: "oauth2", connectionId: ... } blocks silently disappeared from the file on the very first restart. The next restart saw a source with no auth, hit a 401 on connect, and registered zero tools.

The OAuth connection row and keychain tokens themselves remained intact in the DB; the file just lost the pointer.

Validation

  • bunx --bun vitest run in packages/plugins/openapi — 64/64 (incl. new regression test asserting executor.sources.remove triggers configFile.removeSource)
  • bunx --bun vitest run in packages/plugins/mcp — 30/30
  • bunx --bun vitest run in packages/plugins/graphql — 14/14
  • bunx --bun vitest run in packages/core/sdk — 90/90
  • bunx --bun vitest run in apps/local — 23/23 (incl. new config-sync.test.ts covering all McpAuthConfig variants)
  • bun run typecheck clean across apps/local, packages/plugins/{openapi,mcp,graphql}
  • End-to-end repro on a local executor web:
    • Bug 1: added the GitHub OpenAPI spec via UI, deleted via UI, restarted — executor.jsonc correctly omits the source and the DB stays clean (was reappearing pre-fix)
    • Bug 2: configured Linear MCP via UI OAuth, restarted — auth: { kind: "oauth2", connectionId: ... } block is preserved in executor.jsonc and 33 Linear tools load (was silently 401'ing pre-fix)

Two related bugs caused executor.jsonc and the runtime DB to drift
out of sync, so sources resurrected after a UI delete and OAuth
auth blocks vanished after a restart.

1. Engine-level removeSource (openapi, mcp, graphql) only deleted
   plugin storage rows. The SDK-level removeSpec/removeSource paths
   also call configFile.removeSource, but the engine hook (used by
   the HTTP `sources.remove` handler and the web UI delete button)
   never did. Effect: deleted source reappeared on next boot from
   executor.jsonc.

2. apps/local config-sync rebuilt the executor.mcp.addSource call
   for remote MCP sources without passing source.auth. addSource
   then wrote back to executor.jsonc with auth=undefined, so the
   {kind:"oauth2", connectionId} block silently disappeared on the
   first restart and the source went 401 with zero tools.

Fixes:
- Plugin engine-hook removeSource now mirrors removeSpec/removeSource
  behavior and calls options.configFile?.removeSource(sourceId) after
  the storage delete (openapi, mcp, graphql).
- config-sync translates McpAuthConfig -> McpConnectionAuth via a new
  translateMcpAuth helper and forwards it to executor.mcp.addSource.

Tests:
- packages/plugins/openapi/src/sdk/plugin.test.ts: regression test
  asserts engine-level executor.sources.remove triggers
  configFile.removeSource.
- apps/local/src/server/config-sync.test.ts: covers all McpAuthConfig
  variants (none, header w/ + w/o secret-public-ref prefix, oauth2,
  undefined).
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