refactor(openapi,graphql): store invocation config on source, not per-tool - #222
Merged
Conversation
…-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).
Deploying with
|
| 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 |
@executor/sdk
@executor/plugin-file-secrets
@executor/plugin-google-discovery
@executor/plugin-graphql
@executor/plugin-keychain
@executor/plugin-mcp
@executor/plugin-onepassword
@executor/plugin-openapi
@executor/plugin-workos-vault
commit: |
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.
This was referenced Apr 13, 2026
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 everyupdateSourcecall read and rewrote every binding row (O(N)writes per header edit).This PR:
invocationConfigto the source row (one per namespace), alongside the user-facingSourceConfig.{ namespace, binding }.getSource(namespace)on the hot path (one extra KV read, which is shared across all tools in the same source within a request).updateSourcebecomes a singleputSource—O(1)writes instead ofO(N).Why this mattered
The edit-source path looked like this:
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.tshas the same anti-pattern (sourceDatais stamped onto every binding row). Worth a follow-up.Test plan
bun run typecheck— full workspace, cleanbun run --filter '@executor/plugin-openapi' --filter '@executor/plugin-graphql' test— 36/36 passing