A Code Mode MCP server lets any Model Context Protocol (MCP) client use model-written code without providing its own sandbox. Instead of advertising one tool per operation, the server exposes code execution as its tool interface and runs the generated JavaScript in an isolated Worker. Generated code can compose several operations and return only the final result, so tool definitions and intermediate data stay out of the model's context. For the design rationale, refer to Code Mode: give agents an entire API in 1,000 tokens ↗︎.
| Pattern | MCP tools | Model-facing API | Use when |
|---|---|---|---|
| Single code tool | code |
Typed methods for every upstream tool | You already have an MCP server with a manageable set of tools. |
| Search and execute | search, execute |
OpenAPI document and request function | You have a large API whose complete schema should stay out of context. |
Use codeMcpServer() when an existing MCP server already defines the operations and schemas the model needs. Use openApiMcpServer() when a large OpenAPI catalog needs progressive discovery and a fixed model-context footprint.
Both functions currently return an SDK v1 server. Serve it with createLegacyMcpHandler, as shown in Wrap an existing MCP server and Publish a large API with search and execute.
The single-tool pattern wraps an existing MCP server with codeMcpServer(). Instead of advertising each upstream tool separately, the server advertises one code tool.
The code tool description contains generated TypeScript definitions for every upstream tool. The model writes JavaScript against a codemode namespace:
async () => {
const projects = await codemode.list_projects({ status: "active" });
const tasks = [];
for (const project of projects) {
tasks.push(...(await codemode.list_tasks({ projectId: project.id })));
}
return tasks.filter((task) => task.status === "blocked");
};The MCP client makes one outer tool call. Inside the sandbox, the code can make dependent upstream calls, filter intermediate data, and return only the final result.
This pattern works well when the generated type declarations fit comfortably in the code tool description. The model receives those declarations when it loads the MCP tool.
You need a Cloudflare Workers project and an existing McpServer.
-
Install Code Mode and the MCP dependencies:
npm i @cloudflare/codemode agents @modelcontextprotocol/sdk zodyarn add @cloudflare/codemode agents @modelcontextprotocol/sdk zodpnpm add @cloudflare/codemode agents @modelcontextprotocol/sdk zodbun add @cloudflare/codemode agents @modelcontextprotocol/sdk zod -
Add a Worker Loader binding and the
nodejs_compatcompatibility flag:{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "codemode-mcp-server", "main": "src/server.ts", "upload_source_maps": true, // Set this to today's date "compatibility_date": "2026-11-06", "compatibility_flags": [ "nodejs_compat" ], "worker_loaders": [ { "binding": "LOADER" } ] }name = "codemode-mcp-server" main = "src/server.ts" upload_source_maps = true # Set this to today's date compatibility_date = "2026-11-06" compatibility_flags = ["nodejs_compat"] [[worker_loaders]] binding = "LOADER" -
Create the upstream server and pass it to
codeMcpServer():src/server.jsjs import { DynamicWorkerExecutor } from "@cloudflare/codemode"; import { codeMcpServer } from "@cloudflare/codemode/mcp"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { createLegacyMcpHandler } from "agents/mcp"; import { z } from "zod"; function createOrderServer() { const server = new McpServer({ name: "orders", version: "1.0.0", }); server.registerTool( "get_order", { description: "Get an order by ID", inputSchema: { orderId: z.string().describe("Order ID"), }, }, async ({ orderId }) => ({ structuredContent: { id: orderId, status: "processing", }, content: [ { type: "text", text: JSON.stringify({ id: orderId, status: "processing" }), }, ], }), ); return server; } export default { async fetch(request, env, ctx) { const upstream = createOrderServer(); const executor = new DynamicWorkerExecutor({ loader: env.LOADER }); const server = await codeMcpServer({ server: upstream, executor, }); return createLegacyMcpHandler(server, { route: "/mcp" })(request, env, ctx); }, };src/server.tsts import { DynamicWorkerExecutor } from "@cloudflare/codemode"; import { codeMcpServer } from "@cloudflare/codemode/mcp"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { createLegacyMcpHandler } from "agents/mcp"; import { z } from "zod"; function createOrderServer() { const server = new McpServer({ name: "orders", version: "1.0.0", }); server.registerTool( "get_order", { description: "Get an order by ID", inputSchema: { orderId: z.string().describe("Order ID"), }, }, async ({ orderId }) => ({ structuredContent: { id: orderId, status: "processing", }, content: [ { type: "text", text: JSON.stringify({ id: orderId, status: "processing" }), }, ], }), ); return server; } export default { async fetch(request, env, ctx): Promise<Response> { const upstream = createOrderServer(); const executor = new DynamicWorkerExecutor({ loader: env.LOADER }); const server = await codeMcpServer({ server: upstream, executor, }); return createLegacyMcpHandler(server, { route: "/mcp" })( request, env, ctx, ); }, } satisfies ExportedHandler<Env>; -
Deploy the Worker:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy -
In an MCP client, connect to
https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/mcp. Verify that the server exposes one tool namedcode.
The model can use the generated codemode namespace inside the code tool:
async () => {
const order = await codemode.get_order({ orderId: "order-123" });
return { id: order.id, status: order.status };
};When an upstream tool returns structuredContent, Code Mode exposes that value directly. Text-only content is joined and parsed as JSON when possible. Upstream MCP errors become exceptions that model-written code can catch. Mixed text and binary content remains in its MCP result structure.
If you provide a custom description, use {{types}} where the generated TypeScript declarations should appear. Use {{example}} where the SDK should insert an example call based on the first upstream MCP tool. Both placeholders are optional.
codeMcpServer() does not provide durable approval for each upstream tool call. It invokes upstream handlers from inside the outer code tool.
Enforce authorization and any required per-operation approval in each upstream handler before applying side effects. Do not include credentials in tool results.
A large API can have thousands of operations. Including every operation in one tool description would still consume substantial context. The search-and-execute pattern separates capability discovery from authenticated API calls.
The server exposes two MCP tools:
searchruns generated code against an OpenAPI document. It returns only the operations, parameters, or schemas needed for the task.executeruns generated code with an authenticated request function. It can call the selected operations, compose responses, and return a focused result.
The complete OpenAPI document stays inside the sandbox. Only what the search code returns enters the model context.
Authentication stays in the host request callback. The generated code receives a request function, not the credential.
The Cloudflare API MCP server uses this pattern to expose the Cloudflare API through search and execute. For the design rationale and context savings, refer to Code Mode: give agents an entire API in 1,000 tokens ↗︎.
You need a Cloudflare Workers project, an OpenAPI 3.x document, and a host-side method for authenticating API requests.
-
Install Code Mode and the MCP dependencies:
npm i @cloudflare/codemode agents @modelcontextprotocol/sdk zodyarn add @cloudflare/codemode agents @modelcontextprotocol/sdk zodpnpm add @cloudflare/codemode agents @modelcontextprotocol/sdk zodbun add @cloudflare/codemode agents @modelcontextprotocol/sdk zod -
Add a Worker Loader binding and the
nodejs_compatcompatibility flag:{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "openapi-codemode-mcp", "main": "src/server.ts", "upload_source_maps": true, // Set this to today's date "compatibility_date": "2026-11-06", "compatibility_flags": [ "nodejs_compat" ], "worker_loaders": [ { "binding": "LOADER" } ] }name = "openapi-codemode-mcp" main = "src/server.ts" upload_source_maps = true # Set this to today's date compatibility_date = "2026-11-06" compatibility_flags = ["nodejs_compat"] [[worker_loaders]] binding = "LOADER" -
Load the OpenAPI document on the host. Create the MCP server with an authenticated
requestfunction:src/server.jsjs import { DynamicWorkerExecutor } from "@cloudflare/codemode"; import { openApiMcpServer } from "@cloudflare/codemode/mcp"; import { createLegacyMcpHandler } from "agents/mcp"; const SPEC_URL = "https://api.example.com/openapi.json"; const API_ORIGIN = "https://api.example.com"; let specCache; async function loadSpec() { if (specCache) return specCache; const response = await fetch(SPEC_URL); if (!response.ok) { throw new Error(`OpenAPI request failed: ${response.status}`); } specCache = await response.json(); return specCache; } export default { async fetch(request, env, ctx) { const authorization = request.headers.get("Authorization"); if (!authorization?.startsWith("Bearer ")) { return new Response("Bearer token required", { status: 401 }); } const server = openApiMcpServer({ spec: await loadSpec(), executor: new DynamicWorkerExecutor({ loader: env.LOADER }), name: "example-api", version: "1.0.0", request: async (options) => { if (!options.path.startsWith("/")) { throw new Error("API path must start with a slash"); } const url = new URL(`${API_ORIGIN}${options.path}`); for (const [key, value] of Object.entries(options.query ?? {})) { if (value !== undefined) { url.searchParams.set(key, String(value)); } } const headers = { Authorization: authorization }; if (options.contentType) { headers["Content-Type"] = options.contentType; } else if (options.body !== undefined) { headers["Content-Type"] = "application/json"; } const response = await fetch(url, { method: options.method, headers, body: options.body === undefined ? undefined : options.rawBody ? options.body : JSON.stringify(options.body), }); if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } if (response.status === 204) return null; const responseType = response.headers.get("Content-Type") ?? ""; return responseType.includes("application/json") ? await response.json() : await response.text(); }, }); return createLegacyMcpHandler(server, { route: "/mcp" })(request, env, ctx); }, };src/server.tsts import { DynamicWorkerExecutor } from "@cloudflare/codemode"; import { openApiMcpServer } from "@cloudflare/codemode/mcp"; import { createLegacyMcpHandler } from "agents/mcp"; const SPEC_URL = "https://api.example.com/openapi.json"; const API_ORIGIN = "https://api.example.com"; let specCache: Record<string, unknown> | undefined; async function loadSpec(): Promise<Record<string, unknown>> { if (specCache) return specCache; const response = await fetch(SPEC_URL); if (!response.ok) { throw new Error(`OpenAPI request failed: ${response.status}`); } specCache = (await response.json()) as Record<string, unknown>; return specCache; } export default { async fetch(request, env, ctx): Promise<Response> { const authorization = request.headers.get("Authorization"); if (!authorization?.startsWith("Bearer ")) { return new Response("Bearer token required", { status: 401 }); } const server = openApiMcpServer({ spec: await loadSpec(), executor: new DynamicWorkerExecutor({ loader: env.LOADER }), name: "example-api", version: "1.0.0", request: async (options) => { if (!options.path.startsWith("/")) { throw new Error("API path must start with a slash"); } const url = new URL(`${API_ORIGIN}${options.path}`); for (const [key, value] of Object.entries(options.query ?? {})) { if (value !== undefined) { url.searchParams.set(key, String(value)); } } const headers: Record<string, string> = { Authorization: authorization }; if (options.contentType) { headers["Content-Type"] = options.contentType; } else if (options.body !== undefined) { headers["Content-Type"] = "application/json"; } const response = await fetch(url, { method: options.method, headers, body: options.body === undefined ? undefined : options.rawBody ? (options.body as string) : JSON.stringify(options.body), }); if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } if (response.status === 204) return null; const responseType = response.headers.get("Content-Type") ?? ""; return responseType.includes("application/json") ? await response.json() : await response.text(); }, }); return createLegacyMcpHandler(server, { route: "/mcp" })( request, env, ctx, ); }, } satisfies ExportedHandler<Env>; -
Deploy the Worker:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy -
In an MCP client, connect to
https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/mcp. Include the bearer token required by your Worker. -
List the MCP tools. Verify that the server exposes
searchandexecute.
Call search before execute. Search code can inspect the document without making API requests:
async () => {
const spec = await codemode.spec();
return Object.entries(spec.paths)
.filter(([path]) => path.includes("/orders"))
.map(([path, operations]) => ({
path,
methods: Object.keys(operations),
}));
};Local OpenAPI $ref values resolve inside the sandbox when code calls codemode.spec(). External references remain unresolved.
The execute tool includes the same codemode.spec() method and the host-provided codemode.request() method:
async () => {
const response = await codemode.request({
method: "GET",
path: "/orders",
query: { status: "processing", limit: 20 },
});
return response.items.map(({ id, status }) => ({ id, status }));
};The host callback receives method, path, optional query, optional body, optional contentType, and optional rawBody fields. For exact types, refer to the openApiMcpServer() API.
The search and execute tools use fixed example snippets. An optional description is appended to the execute tool description. This function does not use the {{types}} or {{example}} placeholders supported by codeMcpServer().
The example reads the bearer token before creating the MCP server. Its request callback adds that token to outbound requests. The token never enters the sandbox.
openApiMcpServer() does not provide durable approval for each request inside execute. Enforce authorization and any required per-operation approval in the host callback before applying side effects. Validate paths instead of accepting arbitrary origins.
Do not include secrets in the OpenAPI document or API results. Both are available to model-written code.
Model-written code runs in an isolated Worker. DynamicWorkerExecutor blocks direct outbound fetch() and connect() calls by default, so generated code reaches external systems only through upstream MCP tools or the host-provided request callback.
Code execution does not replace authorization. Enforce permissions and any required approval inside upstream tool handlers or the host request callback before applying side effects. Do not expose credentials through tool results or OpenAPI documents.
Have model-written code select, map, aggregate, or paginate data before returning. The publisher limits final MCP responses to approximately 6,000 estimated tokens and marks truncated responses with --- TRUNCATED ---.
Truncation does not reduce API work already performed. Return focused identifiers, status fields, counts, and errors that support the model's next decision.