Skip to content

Cut /tools latency: fix annotation N+1, cache WorkOS + tools.list#361

Open
RhysSullivan wants to merge 3 commits intomainfrom
rs/executor-tools-list-perf
Open

Cut /tools latency: fix annotation N+1, cache WorkOS + tools.list#361
RhysSullivan wants to merge 3 commits intomainfrom
rs/executor-tools-list-perf

Conversation

@RhysSullivan
Copy link
Copy Markdown
Owner

Summary

Three targeted latency fixes for the MCP / /sources/:id/tools hot path. Measured end-to-end on /api/scopes/<org>/sources/<src>/tools: 5.91s → 1.03s; repeat tools.search in an MCP session: ~1.5s p50 → ~0ms.

  • tools: collapse annotation N+1 + push sourceId filter to DB — openapi/graphql resolveAnnotations ran one listOperationsBySource per distinct scope across the tool rows (N queries per annotate). Stores the owning scopeId on each StoredOperation and partitions a single query's result by op.scopeId, keeping shadowed tools resolving against the right binding. Also pushes executor.tools.list's sourceId filter to storage so /sources/:id/tools stops pulling every row in the scope stack and filtering client-side.

  • workos: cache sealed-session auth for 10s within a request — every protected request authenticates twice (lookupOrgForRequest + OrgAuth middleware). Each cache miss re-runs the WorkOS refresh POST when the access token is expired. Cache.make keyed by sealed session collapses the two into one lookup within a ~10s window.

  • mcp-session: cache executor.tools.list on the DOtools.search scans the full list on every call; a single MCP session DO sees many searches. Plain Map<JSON.stringify(filter), Tool[]> in the DO closure, no TTL (DO idle timeout = 5m caps staleness). Cache state lands on the executor.tools.list.cached span as cache.state: hit|miss.

Test plan

  • bunx tsc --noEmit -p apps/cloud/tsconfig.json clean
  • Live verified against production traces in Axiom:
    • executor.tools.list.cached shows hit on second+ tools.search in same session (0ms) vs miss on first (2.2s populate)
    • /sources/cloudflare_api/tools dropped from 5.91s → 1.03s post-deploy
  • Confirm scope isolation: shadowed openapi/graphql tools still pick up the inner scope's binding (covered by existing plugin tests)

openapi/graphql resolveAnnotations ran one listOperationsBySource per
distinct scope in the toolRows. Store the owning scopeId on each
StoredOperation instead and issue a single query, partitioning by
op.scopeId so shadowed tools still resolve against the right binding.

Also push executor.tools.list's sourceId filter down to storage so
`/sources/:id/tools` stops pulling every row in the scope stack.
Every protected request authenticates twice — once in
lookupOrgForRequest to choose the org, once in the OrgAuth middleware
to populate AuthContext. Each uncached call re-runs the WorkOS refresh
POST when the access token is expired. Wrap authenticateSealedSession
in Cache.make keyed by the sealed session string so both callers share
a single lookup per (sessionData, ~10s window).
tools.search loads the full tool list on every call to score against
it. The MCP session DO survives the whole CLI session, so repeated
searches share one DB fetch. Wrap executor.tools.list in a plain Map
keyed by JSON.stringify(filter); DO idle timeout (5m) caps staleness.
Subsequent searches drop from ~1.5s p50 to ~0ms.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 21, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 388f532 Apr 21 2026, 06:40 AM

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 21, 2026

Open in StackBlitz

@executor/sdk

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

@executor/plugin-file-secrets

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

@executor/plugin-google-discovery

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

@executor/plugin-graphql

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

@executor/plugin-keychain

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

@executor/plugin-mcp

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

@executor/plugin-oauth2

npm i https://pkg.pr.new/RhysSullivan/executor/@executor/plugin-oauth2@361

@executor/plugin-onepassword

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

@executor/plugin-openapi

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

@executor/plugin-workos-vault

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

executor

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

commit: 388f532

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