Skip to content

Use an OpenAPI service with Code Mode

Use OpenApiConnector to expose an OpenAPI service inside a durable Code Mode runtime. The connector derives one sandbox method for each operation in the OpenAPI document.

The model can discover methods with codemode.search() and request focused input types with codemode.describe(). The complete OpenAPI document does not need to enter the model context.

This page covers an Agent consuming an OpenAPI service. To publish an OpenAPI service to external MCP clients through search and execute, refer to Build a search and execute MCP server.

Prerequisites

You need a project with the durable Code Mode runtime configured. The runtime setup provides the Worker Loader binding and the CodemodeRuntime export used in this guide.

Create an OpenAPI connector

  1. Add the OpenAPI document to your project. Give each operation a unique operationId so it produces a stable sandbox method name:

    src/orders-openapi.js
    export const ordersOpenApiSpec = {
    openapi: "3.1.0",
    info: { title: "Orders API", version: "1.0.0" },
    paths: {
    "/orders/{orderId}": {
    get: {
    operationId: "get_order",
    summary: "Get an order by ID.",
    parameters: [
    {
    name: "orderId",
    in: "path",
    required: true,
    schema: { type: "string" },
    },
    ],
    },
    },
    "/orders": {
    post: {
    operationId: "create_order",
    summary: "Create an order.",
    requestBody: {
    required: true,
    content: {
    "application/json": {
    schema: {
    type: "object",
    properties: {
    productId: { type: "string" },
    quantity: { type: "integer" },
    },
    required: ["productId", "quantity"],
    },
    },
    },
    },
    },
    },
    },
    };
  2. Create the connector. Implement spec() to return the document and request() to make authenticated host-side requests:

    src/orders-connector.js
    import { OpenApiConnector } from "@cloudflare/codemode";
    import { ordersOpenApiSpec } from "./orders-openapi";
    const API_ORIGIN = "https://api.example.com";
    export class OrdersConnector extends OpenApiConnector {
    name() {
    return "orders";
    }
    instructions() {
    return "Use for reading and creating orders.";
    }
    spec() {
    return ordersOpenApiSpec;
    }
    async request(options) {
    if (!options.path.startsWith("/")) {
    throw new Error("Orders API path must start with a slash");
    }
    const url = new URL(options.path, API_ORIGIN);
    for (const [key, value] of Object.entries(options.params ?? {})) {
    if (value !== undefined) {
    url.searchParams.set(key, String(value));
    }
    }
    const response = await fetch(url, {
    method: options.method ?? "GET",
    headers: {
    ...(options.body !== undefined
    ? { "Content-Type": "application/json" }
    : {}),
    ...options.headers,
    Authorization: `Bearer ${this.env.ORDERS_API_TOKEN}`,
    },
    body:
    options.body === undefined ? undefined : JSON.stringify(options.body),
    });
    if (!response.ok) {
    throw new Error(`Orders API request failed: ${response.status}`);
    }
    if (response.status === 204) return null;
    return response.json();
    }
    tool(name, tool) {
    if (name === "create_order") {
    return { ...tool, requiresApproval: true };
    }
    return tool;
    }
    }

    Credentials remain in the host Worker. Model-written code receives connector methods and their results, not ORDERS_API_TOKEN.

    The tool() hook decorates derived operations. This example requires approval before create_order executes. You can also use the hook to add replay or rollback behavior.

  3. Import the connector and add it to the runtime:

    src/server.js
    import { AIChatAgent } from "@cloudflare/ai-chat";
    import {
    createCodemodeRuntime,
    DynamicWorkerExecutor,
    } from "@cloudflare/codemode";
    import { OrdersConnector } from "./orders-connector";
    export class Chat extends AIChatAgent {
    #runtime() {
    return createCodemodeRuntime({
    ctx: this.ctx,
    executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),
    connectors: [new OrdersConnector(this.ctx, this.env)],
    });
    }
    async onChatMessage() {
    const tools = { codemode: this.#runtime().tool() };
    // Pass tools to your model call.
    }
    }
  4. Let the model discover the operations and call the generated connector methods:

    JavaScript
    async () => {
    const matches = await codemode.search("get an order by ID");
    const docs = await codemode.describe(matches.results[0].path);
    const order = await orders.get_order({ orderId: "order-123" });
    return { docs, order };
    };

Derived method behavior

OpenApiConnector uses a sanitized operationId as the method name. If an operation has no operationId, it derives a name from the HTTP method and path. Define unique operation IDs to keep method names stable and avoid collisions.

Each generated method accepts one object:

  • Path, query, and header parameters appear as top-level fields.
  • A JSON request body appears under body.
  • Required OpenAPI parameters become required TypeScript fields.
  • Local $ref values in input schemas are resolved before types are generated.

The connector substitutes path parameters and passes a normalized { path, method, params, body, headers } object to request().

The current connector derives input types but does not derive response types from OpenAPI response schemas. Generated methods therefore return unknown unless your application provides more specific declarations through another connector implementation.

Request escape hatch

Every OpenAPI connector also exposes a low-level request() sandbox method. Use it when the OpenAPI document does not describe an operation the model needs:

JavaScript
const result = await orders.request({
path: "/orders",
method: "GET",
params: { status: "processing" },
});

Prefer derived operation methods when available. They provide discoverable descriptions and generated input types.

exposeSpec() returns false by default. Override it to return true only when model-written code needs access to the raw OpenAPI document. Large documents can produce large results and durable log entries.