Skip to content

refactor(openapi,graphql): store invocation config on source, not per-tool - #222

Merged
RhysSullivan merged 2 commits into
mainfrom
rs/fix-per-tool-config
Apr 13, 2026
Merged

refactor(openapi,graphql): store invocation config on source, not per-tool#222
RhysSullivan merged 2 commits into
mainfrom
rs/fix-per-tool-config

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

Summary

Per-tool binding rows in the OpenAPI and GraphQL plugins were duplicating the full InvocationConfig (baseUrl/endpoint + headers) on every binding entry. For a source with N operations, the plugin stored N copies of the same config in KV, and every updateSource call read and rewrote every binding row (O(N) writes per header edit).

This PR:

  • Moves invocationConfig to the source row (one per namespace), alongside the user-facing SourceConfig.
  • Per-tool rows now store only { namespace, binding }.
  • The invoker resolves runtime config via getSource(namespace) on the hot path (one extra KV read, which is shared across all tools in the same source within a request).
  • updateSource becomes a single putSourceO(1) writes instead of O(N).
  • Applied symmetrically to the OpenAPI and GraphQL plugins.

Why this mattered

The edit-source path looked like this:

const toolIds = yield* operationStore.listByNamespace(namespace);
for (const toolId of toolIds) {
  const entry = yield* operationStore.get(toolId);
  if (entry) {
    yield* operationStore.put([{ toolId, namespace, binding: entry.binding, config: newInvocationConfig }]);
  }
}

For a Stripe-sized OpenAPI spec this is hundreds of KV writes per header rotation, and every binding row inflates with a redundant headers blob.

Not in scope

packages/plugins/mcp/src/sdk/binding-store.ts has the same anti-pattern (sourceData is stamped onto every binding row). Worth a follow-up.

Test plan

  • bun run typecheck — full workspace, clean
  • bun run --filter '@executor/plugin-openapi' --filter '@executor/plugin-graphql' test — 36/36 passing
  • Manually exercise an OpenAPI add + edit-source flow against a real spec to confirm invocation still works end-to-end
  • Same for GraphQL

…-tool

Per-tool binding rows were duplicating the full InvocationConfig
(baseUrl/endpoint + headers) on every entry. A source with N
operations stored N copies of the same config in KV, and every
updateSource call read + rewrote every binding row.

Move invocationConfig to the source row (one per namespace) alongside
the user-facing SourceConfig. Per-tool rows now store only
{ namespace, binding }. The invoker resolves config via getSource on
the hot path; updateSource is a single putSource (O(1) writes).
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 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 0580036 Commit Preview URL

Branch Preview URL
Apr 13 2026, 07:04 AM

@pkg-pr-new

pkg-pr-new Bot commented Apr 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor/sdk

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/sdk@222

@executor/plugin-file-secrets

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-file-secrets@222

@executor/plugin-google-discovery

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-google-discovery@222

@executor/plugin-graphql

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-graphql@222

@executor/plugin-keychain

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-keychain@222

@executor/plugin-mcp

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-mcp@222

@executor/plugin-onepassword

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-onepassword@222

@executor/plugin-openapi

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-openapi@222

@executor/plugin-workos-vault

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-workos-vault@222

commit: 0580036

Pre-refactor source rows don't carry invocationConfig. Mark the field
decode-optional on StoredSourceSchema, and in the KV stores rehydrate
missing invocationConfig on read:

- GraphQL: synthesize from source.config.endpoint + headers (lossless,
  endpoint is always user-provided).
- OpenAPI: scan for a legacy binding row whose inlined config carries
  the resolved baseUrl. Fall back to source.config.baseUrl.

getSource writes the healed row back; listSources rehydrates in memory
only to avoid N writes per list. All migration paths are tagged with
TODO(migration) for a later cleanup pass.
@RhysSullivan
RhysSullivan merged commit b28b464 into main Apr 13, 2026
7 checks passed
RhysSullivan added a commit that referenced this pull request May 31, 2026
…-tool (#222)

* refactor(openapi,graphql): store invocation config on source, not per-tool

Per-tool binding rows were duplicating the full InvocationConfig
(baseUrl/endpoint + headers) on every entry. A source with N
operations stored N copies of the same config in KV, and every
updateSource call read + rewrote every binding row.

Move invocationConfig to the source row (one per namespace) alongside
the user-facing SourceConfig. Per-tool rows now store only
{ namespace, binding }. The invoker resolves config via getSource on
the hot path; updateSource is a single putSource (O(1) writes).

* feat(openapi,graphql): self-heal legacy source rows on read

Pre-refactor source rows don't carry invocationConfig. Mark the field
decode-optional on StoredSourceSchema, and in the KV stores rehydrate
missing invocationConfig on read:

- GraphQL: synthesize from source.config.endpoint + headers (lossless,
  endpoint is always user-provided).
- OpenAPI: scan for a legacy binding row whose inlined config carries
  the resolved baseUrl. Fall back to source.config.baseUrl.

getSource writes the healed row back; listSources rehydrates in memory
only to avoid N writes per list. All migration paths are tagged with
TODO(migration) for a later cleanup pass.
@RhysSullivan
RhysSullivan deleted the rs/fix-per-tool-config branch June 11, 2026 08:01
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