Skip to content

OpenAPI integration utilities: spec summarize, thin client scaffold, curated provider bindings - #692

Merged
kody-bot merged 5 commits into
mainfrom
cursor/openapi-integration-utilities-fable-orchestrator-849a
Jul 9, 2026
Merged

kody-bot merged 5 commits into
mainfrom
cursor/openapi-integration-utilities-fable-orchestrator-849a

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 9, 2026 •

Copy link
Copy Markdown
Member

OpenAPI integration utilities

Ships three layers of OpenAPI support as one platform PR, built on a shared untrusted-spec core (packages/worker/src/openapi/): bounded https-only fetch (reuses readBoundedBody, redirects followed manually with https enforced on every hop), OpenAPI 3.x JSON/YAML parsing with local-only $ref resolution (no remote refs, cycle/depth/size bounds), and stable snake_case operation slugs.

Layer A — openapi_spec_summarize (integrations domain)

Fetches a bounded, untrusted OpenAPI 3.x spec URL (e.g. surface.spec from integration_discover) and returns: servers, suggestedApiBaseUrl, suggestedHosts (suggestions only — never auto-approved), security schemes mapped to Kody auth paths (integration_save + /connect/oauth + createAuthenticatedFetch, oauthClientCredentials, secretHeaders.basic, {{secret:...}} placeholders), cheap smoke-test operation suggestions, and a filterable operation inventory (operationId/slug/method/path/summary/tags) with truncation flags.

Layer B — openapi_client_scaffold (integrations domain)

Given spec + selected operation slugs + an auth binding (integration / bearerSecret / headerSecret / basicSecrets / none), emits a small dependency-free ESM module — one exported async function per operation using ambient fetch / createAuthenticatedFetch and secret placeholders (never credential values). Suitable for execute ephemeral modules and package client.ts scaffolds. No Orval / full codegen toolchains. Spec-derived strings are escaped so hostile specs cannot inject code into generated modules (covered by tests).

Layer C — curated provider bindings + runtime synthesis

  • New builtin openapi domain: openapi_binding_save / list / get / delete / refresh. A binding = name + specUrl + apiBaseUrl + auth reference + curated selection (operation slugs or raw operationIds / tags / path prefixes) + includeDestructive (default false).
  • Saving resolves the selection against the fetched spec into a stored operation snapshot (1–100 operations; more → error asking to narrow; DELETE ops excluded unless opted-in and always tagged destructive). Bindings persist as user-scoped non-secret values (_openapi:<name>); credentials stay in secrets.
  • Registry runtime-synthesizes openapi:<name> domains (mirrors mcp:<server> synthesis), callable as kody.openapi["name"].operation_slug({ params, query, headers, body }). Requests are host-pinned to the binding apiBaseUrl (base path segments preserved; absolute/protocol-relative spec paths rejected), integration auth additionally enforces the integration host allowlist, and all secret placeholders resolve through the fetch gateway (per-secret allowedHosts enforced). Integration auth retries once after a host-side token refresh on 401.
  • MCP server instructions, search accessors (source: 'openapi'), and executor proxy (kody.openapi[...], flat openapi: names rejected with guidance) wired to match the remote/mcp patterns.

Security posture

  • Specs are untrusted third-party content: bounded fetch, https-only (including every redirect hop), no remote $refs, descriptions carry verification warnings.
  • Host approval is never widened from spec servers — suggested hosts are informational; approval stays in the account security UI / integration requiredHosts / secret allowedHosts.
  • Never auto-exposes entire specs into search: bindings require an explicit curated selection, hard-capped at 100 operations.

Docs

docs/guides/openapi-integrations.md (new), integration-bootstrap.md (summarize before hand-coding when surface.spec exists), docs/contributing/architecture/openapi-bindings.md (new), primitives.yaml (openapi-bindings primitive), adding-capabilities.md, architecture index/request-lifecycle, docs/use/search.md.

Test plan

  • npm run validate green locally (format, lint, typecheck, unit tests, Playwright E2E, MCP E2E); CI Validate green on every push.
  • New focused suites (100+ new tests): parse/summarize/fetch (fixtures incl. YAML, $ref cycles, hostile inputs, redirect downgrade attempts, slug dedup collisions), scaffold generation (generated modules imported and executed against recording fetch: URL/method/header/body assertions, auth-header override protection, code-injection safety), binding schema + selection semantics (slug and raw operationId matching), save/list/get/delete/refresh round-trip, synthesis schema shape (required path/query/header params, cookie note), operation request execution (host pinning incl. base-path preservation, per-auth headers, gateway invocation, 401 refresh retry with body cancel, bounded response), executor proxy, and search accessor formatting.
  • Manual smoke against the real Swagger Petstore 3.0 spec: parse → summarize (auth mapping + smoke suggestions) → scaffold (module generated) → selection resolution by tag.
  • All Bugbot and CodeRabbit findings addressed across two review rounds (base-path URL bug, schema-level https, operationId selection, redirect downgrade, dedupe collision, header/query schema gaps, 401 body leak, json content-type strictness).
System recap — adds a new primitive (high risk)

Mode: recap · Base: main @ 6e5e315b · Head: 9b7ba16d

Classification: adds — introduces the openapi-bindings primitive (user-scoped OpenAPI provider bindings with runtime-synthesized openapi:<name> domains); primitives.yaml updated in this PR.

Primitives touched

Primitive Group Impact
openapi-bindings assistant adds — new primitive: binding store, openapi domain, runtime synthesis, operation executor
capability-registry assistant extends — new 'openapi' capability source; registry merge + cache key include OpenAPI bindings
capabilities-execute runtime extends — kody.openapi[...] proxy namespace in generated executor modules
values assistant composes — bindings persist as _openapi:<name> user-scoped values
secrets assistant composes — operation requests resolve {{secret:...}} placeholders through the fetch gateway
mcp-server surfaces composes — server instructions document the kody.openapi[...] call shape and openapi domain

System map

An agent summarizes an untrusted OpenAPI spec, saves a curated binding into the values store, and the capability registry synthesizes openapi:<name> domains whose operations execute through the secret-resolving fetch gateway.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	mcpServer["mcp-server<br/>MCP endpoint"]:::touched
	capabilityRegistry["capability-registry<br/>Capability registry"]:::extended
	openapiBindings["openapi-bindings<br/>OpenAPI provider bindings"]:::added
	valuesStore["values<br/>Values store"]:::touched
	capabilitiesExecute["capabilities-execute<br/>Execute runtime"]:::extended
	secrets["secrets<br/>Secrets + host approval"]:::touched
	mcpServer -->|"openapi_spec_summarize / openapi_binding_save"| openapiBindings
	openapiBindings -->|"_openapi:&lt;name&gt; user-scoped config"| valuesStore
	capabilityRegistry -->|"synthesize openapi:&lt;name&gt; domains from bindings"| openapiBindings
	capabilitiesExecute -->|"kody.openapi[name].operation(input)"| capabilityRegistry
	openapiBindings -->|"gateway fetch resolves {{secret:...}}, enforces allowedHosts"| secrets
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Change flow

sequenceDiagram
	participant Agent
	participant Summarize as openapi_spec_summarize
	participant Bind as openapi_binding_save
	participant Registry as capability registry
	participant Exec as execute (kody.openapi)
	participant Gateway as fetch gateway
	Agent->>Summarize: specUrl (untrusted, bounded https fetch)
	Summarize-->>Agent: auth mapping + operation slugs + suggested hosts
	Agent->>Bind: name + apiBaseUrl + auth ref + curated selection
	Bind-->>Agent: snapshot saved (≤100 ops, destructive off by default)
	Agent->>Exec: kody.openapi["name"].op({ params, query, body })
	Exec->>Registry: synthesized openapi:name capability
	Registry->>Gateway: host-pinned request with secret placeholders
	Gateway-->>Agent: { status, ok, contentType, body, truncated }
Loading

Invariants

  • Per-user isolation preserved: binding reads/writes and operation execution are userId-scoped (values store + gateway props).
  • integration-host-allowlist untouched and enforced for integration-auth operations; requests additionally host-pinned to binding apiBaseUrl; spec servers never widen approval; secret allowedHosts enforced in the gateway.
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added OpenAPI integrations: spec summarization, ephemeral client scaffolding, and durable provider bindings.
    • Users can now save, list, get, refresh, and delete OpenAPI bindings, and invoke operations via kody.openapi["<name>"].<operation>(input).
    • Search and execution now surface OpenAPI-backed capabilities alongside existing tools.
  • Documentation

    • Added/updated OpenAPI integration and architecture guides, including end-to-end flow and security posture.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds OpenAPI 3.x parsing/summarization, curated OpenAPI bindings with save/list/get/delete/refresh capabilities, synthesized openapi:<name> domains, kody.openapi[...] execution support, and matching documentation and search/instruction updates.

Changes

OpenAPI Provider Bindings

Layer / File(s) Summary
OpenAPI documentation
docs/contributing/adding-capabilities.md, docs/contributing/architecture/*, docs/guides/*, docs/use/search.md
Documents synthesized openapi:<name> domains, binding storage/security model, bootstrap and search authentication flows, and the OpenAPI integrations guide.
OpenAPI spec types, fetching, and parsing
packages/worker/src/openapi/spec-types.ts, fetch-spec.ts, parse-spec.ts, *.node.test.ts, packages/worker/package.json
Adds parsed-spec types, HTTPS-bounded spec fetching, OpenAPI 3.x JSON/YAML parsing, and tests for refs, dedupe, caps, redirects, and fetch limits.
Spec summarization for discovery
packages/worker/src/openapi/summarize-spec.ts, summarize-spec.node.test.ts
Derives compact summaries with auth suggestions, smoke-test operations, filtering, and truncation.
Binding schema, auth, and storage
packages/worker/src/openapi/auth-binding.ts, binding-shared.ts, binding-service.ts, *.node.test.ts
Adds OpenAPI auth binding, binding/selection validation and resolution helpers, size limits, summaries, and list/get storage functions.
Ephemeral client scaffolding and integrations capabilities
packages/worker/src/openapi/scaffold-client.ts, packages/worker/src/mcp/capabilities/integrations/openapi-*.ts, packages/worker/src/mcp/capabilities/integrations/domain.ts
Generates ephemeral client modules and adds openapi_spec_summarize/openapi_client_scaffold MCP capabilities wired into integrationsDomain.
OpenAPI operation execution and domain synthesis
packages/worker/src/mcp/capabilities/openapi-provider/*.ts
Synthesizes per-operation capabilities from bindings and executes pinned requests with auth injection and 401 token-refresh retry.
Binding CRUD and refresh capabilities
packages/worker/src/mcp/capabilities/openapi/*.ts
Adds the openapi domain plus save/list/get/delete/refresh capabilities with operation diffing on refresh.
Capability registry and type wiring
packages/worker/src/mcp/capabilities/{registry,types,builtin-domains,domain-metadata,define-capability,build-capability-registry,meta/meta-list-capabilities}.ts
Wires openapi domains/capabilities into builtin domains, capability types, and the dynamic registry cache/build logic.
Executor/proxy and Kody registry wiring
packages/worker/src/mcp/{executor,run-kody-registry,kody-remote-types,server-instructions}.ts
Extends the generated proxy and registry metadata to expose kody.openapi[...] dispatch and provider status.
Search and execute formatting
packages/worker/src/mcp/tools/{search-format,search}.ts
Extends search results, detail formatting, and recommended-next-step generation for openapi-sourced capabilities.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SaveCapability as openapi_binding_save
  participant SpecFetcher as fetchOpenApiSpecText
  participant Storage as values-store
  participant Executor
  participant Upstream as Upstream API

  User->>SaveCapability: specUrl, selection, auth
  SaveCapability->>SpecFetcher: fetch and parse spec
  SaveCapability->>Storage: saveValue(binding)
  User->>Executor: kody.openapi["name"].operation(input)
  Executor->>Storage: getOpenApiBinding(name)
  Executor->>Upstream: pinned request with auth headers
  Upstream-->>Executor: 401 Unauthorized
  Executor->>Upstream: refresh token
  Executor->>Upstream: retry request
  Upstream-->>Executor: response
  Executor-->>User: bounded parsed result
Loading

Possibly related PRs

  • kentcdodds/kody#272: Both PRs modify the MCP search implementation (packages/worker/src/mcp/tools/search.ts) and related capability routing.
  • kentcdodds/kody#279: Both PRs modify the capability-search/types surface in packages/worker/src/mcp/capabilities/types.ts and packages/worker/src/mcp/tools/search-format.ts.
  • kentcdodds/kody#280: Both PRs modify the MCP search/format surfaces in packages/worker/src/mcp/tools/search.ts and search-format.ts to add new match/output categories.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main OpenAPI summarization, client scaffolding, and curated binding additions.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/openapi-integration-utilities-fable-orchestrator-849a

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kody-bot
kody-bot marked this pull request as ready for review July 9, 2026 18:15
@github-actions

github-actions Bot commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-692.kody-a99.workers.dev

Worker: kody-pr-692
D1: kody-pr-692-db
KV: kody-pr-692-oauth-kv

Mocks:

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review

@kentcdodds

Copy link
Copy Markdown
Member Author

Bugbot review

Comment thread packages/worker/src/mcp/capabilities/integrations/openapi-spec-summarize.ts Outdated
…rce https at schema level for openapi_spec_summarize
Comment thread packages/worker/src/openapi/binding-shared.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b435127. Configure here.

Comment thread packages/worker/src/mcp/capabilities/openapi-provider/index.ts
Comment thread packages/worker/src/mcp/capabilities/openapi-provider/index.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (8)
packages/worker/src/mcp/capabilities/types.ts (1)

43-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

method typed too loosely.

CapabilityOpenApiMetadata.method is string rather than a narrower HTTP-verb union (matching readOnly: method === 'get' || method === 'head' usage seen in openapi-provider/index.ts). A typed union would catch typos at compile time.

Optional typing tweak
 export type CapabilityOpenApiMetadata = {
 	bindingName: string
 	kodyName: string
 	operationSlug: string
-	method: string
+	method: 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch' | 'trace'
 	path: string
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/types.ts` around lines 43 - 49,
CapabilityOpenApiMetadata.method is currently too broad, so tighten it to a
HTTP-verb union instead of plain string. Update the CapabilityOpenApiMetadata
type in types.ts to use a narrower method type that matches the values consumed
by openapi-provider/index.ts (where readOnly checks method === 'get' || method
=== 'head'), and adjust any related call sites or constants such as
CapabilityOpenApiMetadata and readOnly usage so the compiler catches invalid
verbs at build time.
packages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.ts (1)

11-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

httpsUrlSchema and httpMethodSchema are duplicated in openapi-spec-summarize.ts.

Both files define identical httpMethodSchema enums and near-identical httpsUrlSchema refinements, but with different error messages ('Must be an https URL.' here vs 'URL must use https.' in the spec-summarize file). Extracting these to a shared module would prevent divergence and ensure consistent validation error messaging across both capabilities.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.ts`
around lines 11 - 31, The validation schemas in openapi-client-scaffold.ts are
duplicated in openapi-spec-summarize.ts, causing drift in the httpMethodSchema
enum and httpsUrlSchema error text. Move these shared validators into a common
module and import them from both capabilities so the protocol check and method
list stay identical, and make sure the shared httpsUrlSchema uses one consistent
error message everywhere.
packages/worker/src/openapi/scaffold-client.ts (1)

310-317: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract isJsonContentType into a shared helper
packages/worker/src/openapi/scaffold-client.ts and packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts each define their own version. Consolidating them would keep body-serialization rules in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/openapi/scaffold-client.ts` around lines 310 - 317,
Extract the duplicated isJsonContentType logic into a shared helper and update
both scaffold-client and operation-request to import it from that single
location. Keep the existing JSON detection behavior intact, and remove the local
function definitions so body-serialization rules stay synchronized between
scaffold-client and openapi-provider/operation-request.
packages/worker/src/mcp/capabilities/openapi-provider/index.ts (1)

66-103: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Required query parameters are dropped from the generated schema.

requiredPathParams is tracked for path params, but parameter.required is ignored for query params, so the query object never carries a required list. Operations with mandatory query params won't advertise them to callers, leading to avoidable upstream 4xx responses.

♻️ Track and emit required query params
 	const pathProperties: Record<string, unknown> = {}
 	const queryProperties: Record<string, unknown> = {}
 	const requiredPathParams: Array<string> = []
+	const requiredQueryParams: Array<string> = []
 
 	for (const parameter of operation.parameters) {
 		const propertySchema = {
 			...asSchemaObject(parameter.schema),
 			...(parameter.description ? { description: parameter.description } : {}),
 		}
 		if (parameter.location === 'path') {
 			pathProperties[parameter.name] = propertySchema
 			if (parameter.required) requiredPathParams.push(parameter.name)
 		} else if (parameter.location === 'query') {
 			queryProperties[parameter.name] = propertySchema
+			if (parameter.required) requiredQueryParams.push(parameter.name)
 		}
 	}
 	if (Object.keys(queryProperties).length > 0) {
 		properties.query = {
 			type: 'object',
 			properties: queryProperties,
+			...(requiredQueryParams.length > 0
+				? { required: requiredQueryParams }
+				: {}),
 			additionalProperties: true,
 		}
+		if (requiredQueryParams.length > 0) required.push('query')
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/openapi-provider/index.ts` around lines
66 - 103, The generated schema in openapi-provider’s parameter handling drops
mandatory query params because only path params are collected into required
lists. Update the logic in the operation parameter loop and the
`properties.query` निर्माण so `parameter.required` is tracked for query entries
as well, then emit a `required` array inside the `query` object alongside
`pathProperties`/`requiredPathParams` handling. Make sure the `query` schema
advertises required names when `operation.parameters` includes required query
parameters, while preserving the existing `params` behavior for path params.
packages/worker/src/mcp/capabilities/openapi/openapi-binding-roundtrip.node.test.ts (1)

265-294: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Auth-required test only covers save and list.

The requireMcpUser gate is also present in openapi_binding_get, openapi_binding_delete, and openapi_binding_refresh, but the unauthenticated test only exercises save and list. Adding the remaining capabilities to this test would provide full coverage of the auth gate.

♻️ Proposed addition
 await expect(
 	openapiBindingSaveCapability.handler(
 		{
 			name: 'widgets',
 			specUrl: SPEC_URL,
 			apiBaseUrl: API_BASE,
 			auth: { kind: 'none' },
 			selection: { tags: ['widgets'] },
 		},
 		unauth,
 	),
 ).rejects.toThrow(/Authenticated MCP user/)

 await expect(
 	openapiBindingListCapability.handler({}, unauth),
 ).rejects.toThrow(/Authenticated MCP user/)
+
+await expect(
+	openapiBindingGetCapability.handler({ name: 'widgets' }, unauth),
+).rejects.toThrow(/Authenticated MCP user/)
+
+await expect(
+	openapiBindingDeleteCapability.handler({ name: 'widgets' }, unauth),
+).rejects.toThrow(/Authenticated MCP user/)
+
+await expect(
+	openapiBindingRefreshCapability.handler({ name: 'widgets' }, unauth),
+).rejects.toThrow(/Authenticated MCP user/)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/worker/src/mcp/capabilities/openapi/openapi-binding-roundtrip.node.test.ts`
around lines 265 - 294, The unauthenticated coverage in openapi binding
capability tests only checks openapiBindingSaveCapability and
openapiBindingListCapability, but the same requireMcpUser gate also protects
openapiBindingGetCapability, openapiBindingDeleteCapability, and
openapiBindingRefreshCapability. Extend the existing “require authenticated
user” test to call those three handlers with the same unauthenticated context
and assert they reject with the same Authenticated MCP user error.
packages/worker/src/mcp/capabilities/openapi/openapi-binding-get.ts (1)

32-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider typing auth and selection in the output schema.

Both auth and selection are exposed as z.unknown(), providing no output contract. If the underlying OpenApiBinding types for these fields are well-defined (e.g., a discriminated union for auth kinds), using those schemas here would give consumers a clearer contract and catch serialization issues early.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/openapi/openapi-binding-get.ts` around
lines 32 - 33, The output schema in openapi-binding-get currently exposes auth
and selection as unknown, so replace those placeholders with the concrete Zod
schemas that match the OpenApiBinding types. Update the schema definition in
openapi-binding-get to use the existing auth and selection type definitions or
shared schema helpers, so the returned contract is explicit and validated. Make
sure the field names auth and selection remain the same while narrowing them to
the correct shapes.
packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts (1)

119-166: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

JSON.stringify comparison for operation fingerprinting is fragile but acceptable.

Using JSON.stringify to compare fingerprintOperation results works because both sides use the same function, ensuring consistent key ordering. However, if parameters or requestBody objects are ever constructed through different code paths (e.g., from different spec versions with different key orders), false-positive "changed" diffs could occur. This is a low-risk concern given the current data flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts`
around lines 119 - 166, The operation diffing in diffOperationSnapshots relies
on JSON.stringify over fingerprintOperation, which can produce false “changed”
results when nested objects have different key ordering. Update the comparison
to use a stable deep-equality approach or normalize the fingerprint data before
comparing, and keep the logic localized to diffOperationSnapshots and
fingerprintOperation so the added/removed/changed slug detection remains
unchanged.
packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts (1)

513-523: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the dead Content-Length block.

Lines 517-523 read Content-Length, compute contentLength, and branch on > maxBytes, but the body is empty (only a comment). This is a no-op that reads as forgotten/incomplete logic — the actual bounding is done by the streaming loop below. Drop it to avoid confusion.

♻️ Proposed cleanup
 ): Promise<{ text: string; truncated: boolean }> {
-	const contentLengthHeader = response.headers.get('Content-Length')
-	if (contentLengthHeader != null) {
-		const contentLength = Number(contentLengthHeader)
-		if (Number.isFinite(contentLength) && contentLength > maxBytes) {
-			// Still stream up to the cap so callers get a useful preview.
-		}
-	}
-
 	if (response.body == null) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts`
around lines 513 - 523, Remove the dead Content-Length handling in
readBoundedBodyAllowTruncate: the block that reads
response.headers.get('Content-Length'), computes contentLength, and checks it
against maxBytes has no effect because the actual truncation is handled by the
streaming loop below. Delete that empty conditional/comment-only block so the
function’s behavior is clearer and only the real bounding logic remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts`:
- Around line 97-136: The 401 refresh retry flow in operation-request.ts leaves
the original response body unread when tryRefreshIntegrationAccessToken succeeds
and executeGatewayFetch is called again. In the 401-handling block inside the
operation request logic, cancel or fully consume the first 401 response before
reassigning response and reissuing the fetch, so the original stream is released
before the retry.

In `@packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts`:
- Around line 96-108: The refresh flow’s saveValue call is missing the userEmail
field that the save handler already passes, so update the openapi binding
refresh path in openapi-binding-refresh.ts to include user.email when calling
saveValue. Use the existing user object from requireMcpUser and keep the
arguments consistent with the other save path so any audit/indexing/bucket logic
in saveValue sees the email.

In `@packages/worker/src/openapi/binding-shared.ts`:
- Around line 317-336: The selection logic in operationMatchesSelection is
matching selection.operationIds against operation.slug only, so raw OpenAPI
operationId values will not work as expected. Update the selector naming and
usage in operationMatchesSelection/OpenApiBindingSelection to either compare
against both operation.slug and the original operationId field, or rename the
selector to operationSlugs so it clearly matches the generated slug behavior.

In `@packages/worker/src/openapi/fetch-spec.ts`:
- Around line 46-50: The fetch flow in fetchSpec currently follows redirects
automatically, which can bypass the HTTPS-only restriction after
assertHttpsSpecUrl validates the initial URL. Update fetchSpec to stop using
automatic redirect following and instead either reject redirects outright or
handle them manually by checking each redirect target remains HTTPS before
continuing. Keep the fix localized around the fetchImpl call in fetchSpec and
preserve the bounded HTTPS behavior expected by the OpenAPI fetch path.

In `@packages/worker/src/openapi/parse-spec.ts`:
- Around line 223-228: `dedupeSlug` currently deduplicates only by base slug
count, so a generated slug can still collide with an existing natural slug;
update `dedupeSlug` to track all allocated slugs globally and keep incrementing
until an unused slug is found. Adjust the `parseOperations` call site to use the
revised uniqueness check so operation slugs remain unique before they are later
indexed by `scaffoldOpenApiClient`.

In `@packages/worker/src/openapi/scaffold-client.ts`:
- Around line 310-317: The isJsonContentType helper is too broad because the
includes('json') check incorrectly treats application/json-seq as regular JSON.
Update isJsonContentType in scaffold-client.ts to keep the explicit
application/json and +json suffix checks, but replace the substring match with
an explicit text/json-style check instead. Use the existing isJsonContentType
function as the fix point so the scaffolded client only JSON.stringifies bodies
for intended JSON content types.

---

Nitpick comments:
In
`@packages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.ts`:
- Around line 11-31: The validation schemas in openapi-client-scaffold.ts are
duplicated in openapi-spec-summarize.ts, causing drift in the httpMethodSchema
enum and httpsUrlSchema error text. Move these shared validators into a common
module and import them from both capabilities so the protocol check and method
list stay identical, and make sure the shared httpsUrlSchema uses one consistent
error message everywhere.

In `@packages/worker/src/mcp/capabilities/openapi-provider/index.ts`:
- Around line 66-103: The generated schema in openapi-provider’s parameter
handling drops mandatory query params because only path params are collected
into required lists. Update the logic in the operation parameter loop and the
`properties.query` निर्माण so `parameter.required` is tracked for query entries
as well, then emit a `required` array inside the `query` object alongside
`pathProperties`/`requiredPathParams` handling. Make sure the `query` schema
advertises required names when `operation.parameters` includes required query
parameters, while preserving the existing `params` behavior for path params.

In `@packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts`:
- Around line 513-523: Remove the dead Content-Length handling in
readBoundedBodyAllowTruncate: the block that reads
response.headers.get('Content-Length'), computes contentLength, and checks it
against maxBytes has no effect because the actual truncation is handled by the
streaming loop below. Delete that empty conditional/comment-only block so the
function’s behavior is clearer and only the real bounding logic remains.

In `@packages/worker/src/mcp/capabilities/openapi/openapi-binding-get.ts`:
- Around line 32-33: The output schema in openapi-binding-get currently exposes
auth and selection as unknown, so replace those placeholders with the concrete
Zod schemas that match the OpenApiBinding types. Update the schema definition in
openapi-binding-get to use the existing auth and selection type definitions or
shared schema helpers, so the returned contract is explicit and validated. Make
sure the field names auth and selection remain the same while narrowing them to
the correct shapes.

In `@packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts`:
- Around line 119-166: The operation diffing in diffOperationSnapshots relies on
JSON.stringify over fingerprintOperation, which can produce false “changed”
results when nested objects have different key ordering. Update the comparison
to use a stable deep-equality approach or normalize the fingerprint data before
comparing, and keep the logic localized to diffOperationSnapshots and
fingerprintOperation so the added/removed/changed slug detection remains
unchanged.

In
`@packages/worker/src/mcp/capabilities/openapi/openapi-binding-roundtrip.node.test.ts`:
- Around line 265-294: The unauthenticated coverage in openapi binding
capability tests only checks openapiBindingSaveCapability and
openapiBindingListCapability, but the same requireMcpUser gate also protects
openapiBindingGetCapability, openapiBindingDeleteCapability, and
openapiBindingRefreshCapability. Extend the existing “require authenticated
user” test to call those three handlers with the same unauthenticated context
and assert they reject with the same Authenticated MCP user error.

In `@packages/worker/src/mcp/capabilities/types.ts`:
- Around line 43-49: CapabilityOpenApiMetadata.method is currently too broad, so
tighten it to a HTTP-verb union instead of plain string. Update the
CapabilityOpenApiMetadata type in types.ts to use a narrower method type that
matches the values consumed by openapi-provider/index.ts (where readOnly checks
method === 'get' || method === 'head'), and adjust any related call sites or
constants such as CapabilityOpenApiMetadata and readOnly usage so the compiler
catches invalid verbs at build time.

In `@packages/worker/src/openapi/scaffold-client.ts`:
- Around line 310-317: Extract the duplicated isJsonContentType logic into a
shared helper and update both scaffold-client and operation-request to import it
from that single location. Keep the existing JSON detection behavior intact, and
remove the local function definitions so body-serialization rules stay
synchronized between scaffold-client and openapi-provider/operation-request.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 13726208-87ad-4fa5-8a6f-267aecdb8b0c

📥 Commits

Reviewing files that changed from the base of the PR and between 6e5e315 and 35c888e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (56)
  • docs/contributing/adding-capabilities.md
  • docs/contributing/architecture/index.md
  • docs/contributing/architecture/openapi-bindings.md
  • docs/contributing/architecture/primitives.yaml
  • docs/contributing/architecture/request-lifecycle.md
  • docs/guides/README.md
  • docs/guides/integration-bootstrap.md
  • docs/guides/openapi-integrations.md
  • docs/use/search.md
  • packages/worker/package.json
  • packages/worker/src/mcp/capabilities/build-capability-registry.ts
  • packages/worker/src/mcp/capabilities/builtin-domains.ts
  • packages/worker/src/mcp/capabilities/define-capability.ts
  • packages/worker/src/mcp/capabilities/domain-metadata.ts
  • packages/worker/src/mcp/capabilities/integrations/domain.ts
  • packages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.node.test.ts
  • packages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.ts
  • packages/worker/src/mcp/capabilities/integrations/openapi-spec-summarize.node.test.ts
  • packages/worker/src/mcp/capabilities/integrations/openapi-spec-summarize.ts
  • packages/worker/src/mcp/capabilities/meta/meta-list-capabilities.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/index.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/operation-request.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/synthesize.node.test.ts
  • packages/worker/src/mcp/capabilities/openapi/domain.ts
  • packages/worker/src/mcp/capabilities/openapi/index.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-delete.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-get.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-list.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-roundtrip.node.test.ts
  • packages/worker/src/mcp/capabilities/openapi/openapi-binding-save.ts
  • packages/worker/src/mcp/capabilities/registry.ts
  • packages/worker/src/mcp/capabilities/types.ts
  • packages/worker/src/mcp/executor.node.test.ts
  • packages/worker/src/mcp/executor.ts
  • packages/worker/src/mcp/kody-remote-types.ts
  • packages/worker/src/mcp/run-kody-registry.ts
  • packages/worker/src/mcp/server-instructions.ts
  • packages/worker/src/mcp/tools/search-format.node.test.ts
  • packages/worker/src/mcp/tools/search-format.ts
  • packages/worker/src/mcp/tools/search.ts
  • packages/worker/src/openapi/auth-binding.ts
  • packages/worker/src/openapi/binding-service.ts
  • packages/worker/src/openapi/binding-shared.node.test.ts
  • packages/worker/src/openapi/binding-shared.ts
  • packages/worker/src/openapi/fetch-spec.node.test.ts
  • packages/worker/src/openapi/fetch-spec.ts
  • packages/worker/src/openapi/openapi-domain-id.ts
  • packages/worker/src/openapi/parse-spec.node.test.ts
  • packages/worker/src/openapi/parse-spec.ts
  • packages/worker/src/openapi/scaffold-client.node.test.ts
  • packages/worker/src/openapi/scaffold-client.ts
  • packages/worker/src/openapi/spec-types.ts
  • packages/worker/src/openapi/summarize-spec.node.test.ts
  • packages/worker/src/openapi/summarize-spec.ts

Comment on lines +96 to +108
await saveValue({
env: ctx.env,
userId: user.userId,
userEmail: user.email,
name: buildOpenApiBindingValueName(next.name),
value: serialized,
scope: 'user',
description: `OpenAPI provider binding for ${next.name}`,
storageContext: {
sessionId: ctx.callerContext.storageContext?.sessionId ?? null,
appId: ctx.callerContext.storageContext?.appId ?? null,
},
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Missing userEmail in refresh's saveValue call.

The save handler passes userEmail: user.email to saveValue, but the refresh handler omits it. The user object is available at line 65 (requireMcpUser). If saveValue uses userEmail for audit logging, indexing, or bucket creation, the refresh path silently skips it.

🐛 Proposed fix
 			await saveValue({
 				env: ctx.env,
 				userId: user.userId,
+				userEmail: user.email,
 				name: buildOpenApiBindingValueName(next.name),
 				value: serialized,
 				scope: 'user',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await saveValue({
env: ctx.env,
userId: user.userId,
userEmail: user.email,
name: buildOpenApiBindingValueName(next.name),
value: serialized,
scope: 'user',
description: `OpenAPI provider binding for ${next.name}`,
storageContext: {
sessionId: ctx.callerContext.storageContext?.sessionId ?? null,
appId: ctx.callerContext.storageContext?.appId ?? null,
},
})
await saveValue({
env: ctx.env,
userId: user.userId,
userEmail: user.email,
name: buildOpenApiBindingValueName(next.name),
value: serialized,
scope: 'user',
description: `OpenAPI provider binding for ${next.name}`,
storageContext: {
sessionId: ctx.callerContext.storageContext?.sessionId ?? null,
appId: ctx.callerContext.storageContext?.appId ?? null,
},
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.ts`
around lines 96 - 108, The refresh flow’s saveValue call is missing the
userEmail field that the save handler already passes, so update the openapi
binding refresh path in openapi-binding-refresh.ts to include user.email when
calling saveValue. Use the existing user object from requireMcpUser and keep the
arguments consistent with the other save path so any audit/indexing/bucket logic
in saveValue sees the email.

Comment thread packages/worker/src/openapi/binding-shared.ts
Comment thread packages/worker/src/openapi/fetch-spec.ts Outdated
Comment thread packages/worker/src/openapi/parse-spec.ts Outdated
Comment thread packages/worker/src/openapi/scaffold-client.ts
…uery param schemas, 401 body cancel, strict json content-type
@kody-bot
kody-bot merged commit 8a68e67 into main Jul 9, 2026
5 checks passed
@kody-bot
kody-bot deleted the cursor/openapi-integration-utilities-fable-orchestrator-849a branch July 9, 2026 19:37
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.

3 participants