Repository navigation
OpenAPI integration utilities: spec summarize, thin client scaffold, curated provider bindings - #692
Conversation
…penapi:<name> synthesis, and docs
📝 WalkthroughWalkthroughThis PR adds OpenAPI 3.x parsing/summarization, curated OpenAPI bindings with save/list/get/delete/refresh capabilities, synthesized ChangesOpenAPI Provider Bindings
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
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
🔎 Preview deployed: https://kody-pr-692.kody-a99.workers.dev Worker: Mocks:
|
|
@coderabbitai review |
|
Bugbot review |
…rce https at schema level for openapi_spec_summarize
…values as well as slugs
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ 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.
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (8)
packages/worker/src/mcp/capabilities/types.ts (1)
43-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
methodtyped too loosely.
CapabilityOpenApiMetadata.methodisstringrather than a narrower HTTP-verb union (matchingreadOnly: method === 'get' || method === 'head'usage seen inopenapi-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
httpsUrlSchemaandhttpMethodSchemaare duplicated inopenapi-spec-summarize.ts.Both files define identical
httpMethodSchemaenums and near-identicalhttpsUrlSchemarefinements, 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 winExtract
isJsonContentTypeinto a shared helper
packages/worker/src/openapi/scaffold-client.tsandpackages/worker/src/mcp/capabilities/openapi-provider/operation-request.tseach 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 winRequired query parameters are dropped from the generated schema.
requiredPathParamsis tracked for path params, butparameter.requiredis ignored forqueryparams, so thequeryobject never carries arequiredlist. 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 winAuth-required test only covers save and list.
The
requireMcpUsergate is also present inopenapi_binding_get,openapi_binding_delete, andopenapi_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 valueConsider typing
authandselectionin the output schema.Both
authandselectionare exposed asz.unknown(), providing no output contract. If the underlyingOpenApiBindingtypes 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.stringifycomparison for operation fingerprinting is fragile but acceptable.Using
JSON.stringifyto comparefingerprintOperationresults works because both sides use the same function, ensuring consistent key ordering. However, ifparametersorrequestBodyobjects 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 winRemove the dead Content-Length block.
Lines 517-523 read
Content-Length, computecontentLength, 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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (56)
docs/contributing/adding-capabilities.mddocs/contributing/architecture/index.mddocs/contributing/architecture/openapi-bindings.mddocs/contributing/architecture/primitives.yamldocs/contributing/architecture/request-lifecycle.mddocs/guides/README.mddocs/guides/integration-bootstrap.mddocs/guides/openapi-integrations.mddocs/use/search.mdpackages/worker/package.jsonpackages/worker/src/mcp/capabilities/build-capability-registry.tspackages/worker/src/mcp/capabilities/builtin-domains.tspackages/worker/src/mcp/capabilities/define-capability.tspackages/worker/src/mcp/capabilities/domain-metadata.tspackages/worker/src/mcp/capabilities/integrations/domain.tspackages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.node.test.tspackages/worker/src/mcp/capabilities/integrations/openapi-client-scaffold.tspackages/worker/src/mcp/capabilities/integrations/openapi-spec-summarize.node.test.tspackages/worker/src/mcp/capabilities/integrations/openapi-spec-summarize.tspackages/worker/src/mcp/capabilities/meta/meta-list-capabilities.tspackages/worker/src/mcp/capabilities/openapi-provider/index.tspackages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.tspackages/worker/src/mcp/capabilities/openapi-provider/operation-request.tspackages/worker/src/mcp/capabilities/openapi-provider/synthesize.node.test.tspackages/worker/src/mcp/capabilities/openapi/domain.tspackages/worker/src/mcp/capabilities/openapi/index.tspackages/worker/src/mcp/capabilities/openapi/openapi-binding-delete.tspackages/worker/src/mcp/capabilities/openapi/openapi-binding-get.tspackages/worker/src/mcp/capabilities/openapi/openapi-binding-list.tspackages/worker/src/mcp/capabilities/openapi/openapi-binding-refresh.tspackages/worker/src/mcp/capabilities/openapi/openapi-binding-roundtrip.node.test.tspackages/worker/src/mcp/capabilities/openapi/openapi-binding-save.tspackages/worker/src/mcp/capabilities/registry.tspackages/worker/src/mcp/capabilities/types.tspackages/worker/src/mcp/executor.node.test.tspackages/worker/src/mcp/executor.tspackages/worker/src/mcp/kody-remote-types.tspackages/worker/src/mcp/run-kody-registry.tspackages/worker/src/mcp/server-instructions.tspackages/worker/src/mcp/tools/search-format.node.test.tspackages/worker/src/mcp/tools/search-format.tspackages/worker/src/mcp/tools/search.tspackages/worker/src/openapi/auth-binding.tspackages/worker/src/openapi/binding-service.tspackages/worker/src/openapi/binding-shared.node.test.tspackages/worker/src/openapi/binding-shared.tspackages/worker/src/openapi/fetch-spec.node.test.tspackages/worker/src/openapi/fetch-spec.tspackages/worker/src/openapi/openapi-domain-id.tspackages/worker/src/openapi/parse-spec.node.test.tspackages/worker/src/openapi/parse-spec.tspackages/worker/src/openapi/scaffold-client.node.test.tspackages/worker/src/openapi/scaffold-client.tspackages/worker/src/openapi/spec-types.tspackages/worker/src/openapi/summarize-spec.node.test.tspackages/worker/src/openapi/summarize-spec.ts
| 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, | ||
| }, | ||
| }) |
There was a problem hiding this comment.
🗄️ 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.
| 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.
…uery param schemas, 401 body cancel, strict json content-type

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 (reusesreadBoundedBody, redirects followed manually with https enforced on every hop), OpenAPI 3.x JSON/YAML parsing with local-only$refresolution (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.specfromintegration_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 ambientfetch/createAuthenticatedFetchand secret placeholders (never credential values). Suitable for execute ephemeral modules and packageclient.tsscaffolds. 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
openapidomain: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)._openapi:<name>); credentials stay in secrets.openapi:<name>domains (mirrorsmcp:<server>synthesis), callable askody.openapi["name"].operation_slug({ params, query, headers, body }). Requests are host-pinned to the bindingapiBaseUrl(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-secretallowedHostsenforced). Integration auth retries once after a host-side token refresh on 401.source: 'openapi'), and executor proxy (kody.openapi[...], flatopenapi:names rejected with guidance) wired to match the remote/mcp patterns.Security posture
$refs, descriptions carry verification warnings.servers— suggested hosts are informational; approval stays in the account security UI / integrationrequiredHosts/ secretallowedHosts.Docs
docs/guides/openapi-integrations.md(new),integration-bootstrap.md(summarize before hand-coding whensurface.specexists),docs/contributing/architecture/openapi-bindings.md(new),primitives.yaml(openapi-bindingsprimitive),adding-capabilities.md, architecture index/request-lifecycle,docs/use/search.md.Test plan
npm run validategreen locally (format, lint, typecheck, unit tests, Playwright E2E, MCP E2E); CI Validate green on every push.System recap — adds a new primitive (high risk)
Mode: recap · Base:
main@6e5e315b· Head:9b7ba16dClassification: adds — introduces the
openapi-bindingsprimitive (user-scoped OpenAPI provider bindings with runtime-synthesizedopenapi:<name>domains);primitives.yamlupdated in this PR.Primitives touched
openapi-bindingsopenapidomain, runtime synthesis, operation executorcapability-registry'openapi'capability source; registry merge + cache key include OpenAPI bindingscapabilities-executekody.openapi[...]proxy namespace in generated executor modulesvalues_openapi:<name>user-scoped valuessecrets{{secret:...}}placeholders through the fetch gatewaymcp-serverkody.openapi[...]call shape andopenapidomainSystem 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:<name> user-scoped config"| valuesStore capabilityRegistry -->|"synthesize openapi:<name> 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:#fffChange 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 }Invariants
integration-host-allowlistuntouched and enforced for integration-auth operations; requests additionally host-pinned to bindingapiBaseUrl; specserversnever widen approval; secretallowedHostsenforced in the gateway.Summary by CodeRabbit
New Features
kody.openapi["<name>"].<operation>(input).Documentation