使用 OpenApiConnector 在持久 Code Mode 运行时内暴露 OpenAPI 服务。连接器为 OpenAPI 文档中的每个操作推导一个沙箱方法。
模型可用 codemode.search() 发现方法,用 codemode.describe() 请求聚焦的输入类型。完整 OpenAPI 文档无需进入模型上下文。
本页介绍 Agent 消费 OpenAPI 服务。要通过 search 与 execute 向外部 MCP client 发布 OpenAPI 服务,请参阅构建 search and execute MCP server。
项目需配置 持久 Code Mode 运行时。运行时设置提供本指南使用的 Worker Loader 绑定与 CodemodeRuntime 导出。
-
将 OpenAPI 文档加入项目。为每个 operation 提供唯一
operationId,以产生 stable sandbox method 名:src/orders-openapi.jsjs 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"], }, }, }, }, }, }, }, };src/orders-openapi.tsts 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"], }, }, }, }, }, }, }, } as const; -
创建 connector。实现
spec()返回文档,request()发起已认证的 host-side 请求:src/orders-connector.jsjs 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; } }src/orders-connector.tsts import { OpenApiConnector, type ConnectorTool, type OpenApiRequestOptions, } from "@cloudflare/codemode"; import { ordersOpenApiSpec } from "./orders-openapi"; const API_ORIGIN = "https://api.example.com"; export class OrdersConnector extends OpenApiConnector<Env> { override name() { return "orders"; } protected override instructions() { return "Use for reading and creating orders."; } protected override spec() { return ordersOpenApiSpec; } protected override async request(options: OpenApiRequestOptions) { 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(); } protected override tool(name: string, tool: ConnectorTool): ConnectorTool { if (name === "create_order") { return { ...tool, requiresApproval: true }; } return tool; } }凭证保留在 host Worker。Model 代码收到 connector method 及其结果,而非
ORDERS_API_TOKEN。tool()hook 装饰 derived operation。本例在create_order执行前要求 approval。也可用该 hook 添加 replay 或 rollback 行为。 -
导入 connector 并加入 runtime:
src/server.jsjs 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. } }src/server.tsts import { AIChatAgent } from "@cloudflare/ai-chat"; import { createCodemodeRuntime, DynamicWorkerExecutor, } from "@cloudflare/codemode"; import { OrdersConnector } from "./orders-connector"; export class Chat extends AIChatAgent<Env> { #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. } } -
让 model 发现 operation 并调用生成的 connector method:
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 }; };
OpenApiConnector 使用清理后的 operationId 作为方法名。无 operationId 时从 HTTP 方法与路径推导。定义唯一操作 ID 以保持方法名稳定并避免冲突。
每个生成方法接受一个对象:
- Path、query、header 参数为 top-level 字段。
- JSON 请求体在
body下。 - 必填 OpenAPI 参数成为必填 TypeScript 字段。
- Input schema 中的 local
$ref在生成类型前 resolve。
连接器替换路径参数并向 request() 传递规范化的 { path, method, params, body, headers }。
当前 connector 推导 input 类型但不从 OpenAPI response schema 推导 response 类型。因此生成 method 返回 unknown,除非应用通过其他 connector 实现提供更具体声明。
每个 OpenAPI connector 还暴露 low-level request() sandbox method。OpenAPI 文档未描述 model 所需 operation 时使用:
const result = await orders.request({
path: "/orders",
method: "GET",
params: { status: "processing" },
});有派生操作方法时优先使用。它们提供可发现的描述与生成的输入类型。
exposeSpec() 默认返回 false。仅当模型代码需要原始 OpenAPI 文档时重写返回 true。大文档可能产生大结果与持久日志条目。