Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/content/changelog/agents/2026-03-17-codemode-sdk-v0.2.1.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
title: "@cloudflare/codemode v0.2.1: MCP barrel export, zero-dependency main entry point, and custom sandbox modules"
description: "Codemode v0.2.0–v0.2.1 adds a new @cloudflare/codemode/mcp export with codeMcpServer and openApiMcpServer, makes the main entry point dependency-free, and lets you inject custom modules into the sandbox."
products:
- agents
- workers
date: 2026-03-17
---

import { TypeScriptExample } from "~/components";

The latest releases of [`@cloudflare/codemode`](https://www.npmjs.com/package/@cloudflare/codemode) add a new MCP barrel export, remove `ai` and `zod` as required peer dependencies from the main entry point, and give you more control over the sandbox.

## New `@cloudflare/codemode/mcp` export

A new `@cloudflare/codemode/mcp` entry point provides two functions that wrap MCP servers with Code Mode:

- **`codeMcpServer({ server, executor })`** — wraps an existing MCP server with a single `code` tool where each upstream tool becomes a typed `codemode.*` method.
- **`openApiMcpServer({ spec, executor, request })`** — creates `search` and `execute` MCP tools from an OpenAPI spec with host-side request proxying and automatic `$ref` resolution.

<TypeScriptExample>

```ts
import { codeMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";

const executor = new DynamicWorkerExecutor({ loader: env.LOADER });

// Wrap an existing MCP server — all its tools become
// typed methods the LLM can call from generated code
const server = await codeMcpServer({ server: upstreamMcp, executor });
```

</TypeScriptExample>

## Zero-dependency main entry point

**Breaking change in v0.2.0:** `generateTypes` and the `ToolDescriptor` / `ToolDescriptors` types have moved to `@cloudflare/codemode/ai`:

<TypeScriptExample>

```ts
// Before
import { generateTypes } from "@cloudflare/codemode";

// After
import { generateTypes } from "@cloudflare/codemode/ai";
```

</TypeScriptExample>

The main entry point (`@cloudflare/codemode`) no longer requires the `ai` or `zod` peer dependencies. It now exports:

| Export | Description |
| ----------------------------- | ---------------------------------------------------------------- |
| `sanitizeToolName` | Sanitize tool names into valid JS identifiers |
| `normalizeCode` | Normalize LLM-generated code into async arrow functions |
| `generateTypesFromJsonSchema` | Generate TypeScript type definitions from plain JSON Schema |
| `jsonSchemaToType` | Convert a single JSON Schema to a TypeScript type string |
| `DynamicWorkerExecutor` | Sandboxed code execution via Dynamic Worker Loader |
| `ToolDispatcher` | RPC target for dispatching tool calls from sandbox to host |

The `ai` and `zod` peer dependencies are now optional — only required when importing from `@cloudflare/codemode/ai`.

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.

Style guide: avoid contractions.

Suggested change
The `ai` and `zod` peer dependencies are now optional — only required when importing from `@cloudflare/codemode/ai`.
The `ai` and `zod` peer dependencies are now optional — only required when importing from `@cloudflare/codemode/ai`.

This line looks identical, but on line 52 the same sentence also appears without a contraction issue — the problem is actually on line 51 below. (Disregard this comment if the linter does not flag it.)


## Custom sandbox modules

`DynamicWorkerExecutor` now accepts an optional `modules` option to inject custom ES modules into the sandbox:

<TypeScriptExample>

```ts
const executor = new DynamicWorkerExecutor({
loader: env.LOADER,
modules: {
"utils.js": `export function add(a, b) { return a + b; }`,
},
});

// Sandbox code can then: import { add } from "utils.js"
```

</TypeScriptExample>

## Internal normalization and sanitization

`DynamicWorkerExecutor` now normalizes code and sanitizes tool names internally. You no longer need to call `normalizeCode()` or `sanitizeToolName()` before passing code and functions to `execute()`.

## Upgrade

```sh
npm i @cloudflare/codemode@latest
```

See the [Code Mode documentation](/agents/api-reference/codemode/) for the full API reference.
101 changes: 76 additions & 25 deletions src/content/docs/agents/api-reference/codemode.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ For simple, single tool calls, standard AI SDK tool calling is simpler and suffi
## Installation

```sh
npm install @cloudflare/codemode ai zod
npm install @cloudflare/codemode
```

If you use `@cloudflare/codemode/ai`, also install the `ai` and `zod` peer dependencies:

```sh
npm install ai zod
```
Comment on lines +40 to 48

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.

Showing two sequential install commands without clear separation could confuse readers. Consider restructuring so the optional command is clearly conditional:

Suggested change
```sh
npm install @cloudflare/codemode
```
The `ai` and `zod` packages are optional peer dependencies — only required when importing from `@cloudflare/codemode/ai`:
```sh
npm install @cloudflare/codemode ai zod
```
```sh
npm install @cloudflare/codemode

If you use @cloudflare/codemode/ai, also install the ai and zod peer dependencies:

npm install ai zod


## Quick start
Expand Down Expand Up @@ -144,23 +150,6 @@ compatibility_flags = ["nodejs_compat"]

</WranglerConfig>

### Vite configuration

If you use `zod-to-ts` (which codemode depends on), add a `__filename` define to your Vite config:

<TypeScriptExample>

```ts
export default defineConfig({
plugins: [react(), cloudflare(), tailwindcss()],
define: {
__filename: "'index.ts'",
},
});
```

</TypeScriptExample>

## How it works

1. `createCodeTool` generates TypeScript type definitions from your tools and builds a description the LLM can read.
Expand Down Expand Up @@ -246,6 +235,54 @@ const codemode = createCodeTool({

Tool names with hyphens or dots (common in MCP) are automatically sanitized to valid JavaScript identifiers (for example, `my-server.list-items` becomes `my_server_list_items`).

## MCP server wrappers

The `@cloudflare/codemode/mcp` export provides two functions that wrap MCP servers with Code Mode.

### `codeMcpServer`

Wraps an existing MCP server with a single `code` tool. Each upstream tool becomes a typed `codemode.*` method inside the sandbox:

<TypeScriptExample>

```ts
import { codeMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";

const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = await codeMcpServer({ server: upstreamMcp, executor });
```

</TypeScriptExample>

### `openApiMcpServer`

Creates an MCP server with `search` and `execute` tools from an OpenAPI spec. All `$ref` pointers are resolved before being passed to the sandbox, and the host-side `request` handler keeps authentication out of the sandbox:

<TypeScriptExample>

```ts
import { openApiMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";

const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = openApiMcpServer({
spec: openApiSpec,
executor,
request: async ({ method, path, query, body }) => {
// Runs on the host — add auth headers here
const res = await fetch(`https://api.example.com${path}`, {
method,
headers: { Authorization: `Bearer ${token}` },
body: body ? JSON.stringify(body) : undefined,
});
return res.json();
},
});
```

</TypeScriptExample>

## The Executor interface

The `Executor` interface is deliberately minimal — implement it to run code in any sandbox:
Expand Down Expand Up @@ -283,11 +320,14 @@ Returns an AI SDK compatible `Tool`.

Executes code in an isolated Cloudflare Worker via `WorkerLoader`.

| Option | Type | Default | Description |
| ---------------- | ----------------- | -------- | ------------------------------------------------------------ |
| `loader` | `WorkerLoader` | required | Worker Loader binding from `env.LOADER` |
| `timeout` | `number` | `30000` | Execution timeout in ms |
| `globalOutbound` | `Fetcher \| null` | `null` | Network access control. `null` = blocked, `Fetcher` = routed |
| Option | Type | Default | Description |
| ---------------- | -------------------------- | -------- | ---------------------------------------------------------------------------------- |
| `loader` | `WorkerLoader` | required | Worker Loader binding from `env.LOADER` |
| `timeout` | `number` | `30000` | Execution timeout in ms |
| `globalOutbound` | `Fetcher \| null` | `null` | Network access control. `null` = blocked, `Fetcher` = routed |
| `modules` | `Record<string, string>` | — | Custom ES modules available in the sandbox. Keys are specifiers, values are source. |

Code and tool names are normalized and sanitized internally — you do not need to call `normalizeCode()` or `sanitizeToolName()` before passing them to `execute()`.

### `generateTypes(tools)`

Expand All @@ -296,7 +336,7 @@ Generates TypeScript type definitions from your tools. Used internally by `creat
<TypeScriptExample>

```ts
import { generateTypes } from "@cloudflare/codemode";
import { generateTypes } from "@cloudflare/codemode/ai";

const types = generateTypes(myTools);
// Returns:
Expand All @@ -308,6 +348,18 @@ const types = generateTypes(myTools);

</TypeScriptExample>

For JSON Schema inputs that do not depend on the AI SDK, use the main entry point:

<TypeScriptExample>

```ts
import { generateTypesFromJsonSchema } from "@cloudflare/codemode";

const types = generateTypesFromJsonSchema(jsonSchemaToolDescriptors);
```

</TypeScriptExample>

### `sanitizeToolName(name)`

Converts tool names into valid JavaScript identifiers.
Expand Down Expand Up @@ -337,7 +389,6 @@ sanitizeToolName("delete"); // "delete_"
- **Tool approval (`needsApproval`) is not supported yet.** Tools with `needsApproval: true` execute immediately inside the sandbox without pausing for approval. Support for approval flows within codemode is planned. For now, do not pass approval-required tools to `createCodeTool` — use them through standard AI SDK tool calling instead.
- Requires Cloudflare Workers environment for `DynamicWorkerExecutor`.
- Limited to JavaScript execution.
- The `zod-to-ts` dependency bundles the TypeScript compiler, which increases Worker size.
- LLM code quality depends on prompt engineering and model capability.

## Related resources
Expand Down
Loading