Documentation Index Fetch the complete documentation index at: https://mintlify.com/cloudflare/agents/llms.txt
Use this file to discover all available pages before exploring further.
A project management chat app where the LLM writes and executes code to orchestrate tools, instead of calling them one at a time. Built with @cloudflare/codemode and @cloudflare/ai-chat.
What it demonstrates
Server (src/server.ts):
AIChatAgent with createCodeTool - the LLM gets a single “write code” tool
DynamicWorkerExecutor - runs LLM-generated code in isolated Worker sandboxes
NodeServerExecutor - alternative executor using a Node.js VM (for local dev)
SQLite-backed tools (projects, tasks, sprints, comments) via SqlStorage
Switchable executor at runtime via HTTP endpoint
Client (src/client.tsx):
useAgentChat for streaming chat with message persistence
Collapsible tool cards showing generated code, results, and console output
Settings panel to switch between Dynamic Worker and Node Server executors
Kumo design system components with dark/light mode
Tools (src/tools.ts):
10 project management tools: createProject, listProjects, createTask, listTasks, updateTask, deleteTask, createSprint, listSprints, addComment, listComments
All backed by SQLite - data persists across conversations
Why Codemode?
Traditional tool calling requires the LLM to call tools one at a time:
User: "Create a project Alpha with 3 tasks"
LLM: → Call createProject("Alpha")
← Returns projectId
LLM: → Call createTask(projectId, "Task 1")
← Returns taskId
LLM: → Call createTask(projectId, "Task 2")
← Returns taskId
LLM: → Call createTask(projectId, "Task 3")
← Returns taskId
LLM: "Done!"
With Codemode, the LLM writes code to orchestrate multiple operations:
User: "Create a project Alpha with 3 tasks"
LLM: → Writes and executes code:
```typescript
const projectId = await codemode.createProject("Alpha");
await Promise.all([
codemode.createTask(projectId, "Task 1"),
codemode.createTask(projectId, "Task 2"),
codemode.createTask(projectId, "Task 3")
]);
return "Created project Alpha with 3 tasks";
← Returns result
LLM: “Done!”
Benefits:
- **Fewer round-trips** - complex operations complete in one step
- **Better composition** - LLM can use loops, conditionals, async/await
- **More control** - LLM can handle errors, retry, format results
## Server Implementation
```typescript src/server.ts
import { createWorkersAI } from "workers-ai-provider";
import { routeAgentRequest } from "agents";
import { AIChatAgent } from "@cloudflare/ai-chat";
import { createCodeTool } from "@cloudflare/codemode";
import { DynamicWorkerExecutor } from "@cloudflare/codemode/executors/dynamic-worker";
import { streamText } from "ai";
import { tools } from "./tools";
export class CodeModeAgent extends AIChatAgent {
async onChatMessage() {
const workersai = createWorkersAI({ binding: this.env.AI });
// Create executor for running LLM-generated code
const executor = new DynamicWorkerExecutor();
const result = streamText({
model: workersai("@cf/zai-org/glm-4.7-flash"),
system:
"You are a project management assistant. You can create projects, tasks, " +
"sprints, and comments. Use the codemode tool to write TypeScript code " +
"that orchestrates multiple operations efficiently.",
messages: await convertToModelMessages(this.messages),
tools: {
// Single "write code" tool instead of individual tools
...createCodeTool({
executor,
tools, // Tools available to the LLM's code
storage: new SqlStorage(this.sql) // SQLite backend
})
}
});
return result.toUIMessageStreamResponse();
}
}
import { z } from "zod" ;
import type { ToolDefinition } from "@cloudflare/codemode" ;
export const tools : Record < string , ToolDefinition > = {
createProject: {
description: "Create a new project" ,
parameters: z . object ({
name: z . string (). describe ( "Project name" )
}),
returns: z . object ({
projectId: z . string (),
name: z . string ()
})
},
listProjects: {
description: "List all projects" ,
parameters: z . object ({}),
returns: z . array ( z . object ({
projectId: z . string (),
name: z . string (),
createdAt: z . string ()
}))
},
createTask: {
description: "Create a new task in a project" ,
parameters: z . object ({
projectId: z . string (),
title: z . string (),
description: z . string (). optional ()
}),
returns: z . object ({
taskId: z . string (),
projectId: z . string (),
title: z . string ()
})
},
listTasks: {
description: "List tasks in a project" ,
parameters: z . object ({
projectId: z . string ()
}),
returns: z . array ( z . object ({
taskId: z . string (),
title: z . string (),
status: z . enum ([ "todo" , "in_progress" , "done" ])
}))
},
updateTask: {
description: "Update a task's status or details" ,
parameters: z . object ({
taskId: z . string (),
status: z . enum ([ "todo" , "in_progress" , "done" ]). optional (),
title: z . string (). optional ()
}),
returns: z . object ({
taskId: z . string (),
updated: z . boolean ()
})
}
};
Example Conversations
Simple creation:
User: Create a project called Alpha
LLM generates code:
const project = await codemode.createProject("Alpha");
return `Created project ${project.name} with ID ${project.projectId}`;
Result: "Created project Alpha with ID abc-123"
Batch operations:
User: Add 5 tasks to project xyz-789
LLM generates code:
const tasks = await Promise.all([
codemode.createTask("xyz-789", "Task 1"),
codemode.createTask("xyz-789", "Task 2"),
codemode.createTask("xyz-789", "Task 3"),
codemode.createTask("xyz-789", "Task 4"),
codemode.createTask("xyz-789", "Task 5")
]);
return `Created ${tasks.length} tasks`;
Result: "Created 5 tasks"
Complex query:
User: List all projects and their task counts
LLM generates code:
const projects = await codemode.listProjects();
const results = await Promise.all(
projects.map(async (p) => {
const tasks = await codemode.listTasks(p.projectId);
return { name: p.name, taskCount: tasks.length };
})
);
return JSON.stringify(results, null, 2);
Result: JSON with project names and task counts
Executors
Codemode supports two execution modes:
Dynamic Worker Executor (Production)
Runs code in isolated Cloudflare Workers:
import { DynamicWorkerExecutor } from "@cloudflare/codemode/executors/dynamic-worker" ;
const executor = new DynamicWorkerExecutor ();
Secure - Full sandbox isolation
Fast - V8 isolates, no cold starts
Scalable - Runs on Cloudflare’s edge
Node Server Executor (Development)
Runs code in a Node.js VM:
import { NodeServerExecutor } from "@cloudflare/codemode/executors/node-server" ;
const executor = new NodeServerExecutor ({
url: "http://localhost:3001"
});
Debugging - Full Node.js inspector support
Local - No network latency
Quick iteration - Hot reload
Start the Node executor:
npm run start:node-executor
Running the Example
Install dependencies
npm install # from repo root
npm run build # from repo root
Start the example
cd examples/codemode
npm start
Try it out
Visit http://localhost:5173 and try:
“Create a project called Alpha”
“Add 3 tasks to Alpha”
“What is 17 + 25?” (simple calculation)
“List all projects and their tasks”
(Optional) Start Node executor
For local debugging: npm run start:node-executor
Then switch to Node executor in the Settings panel.
This example uses Workers AI (no API key needed) with @cf/zai-org/glm-4.7-flash.
Key Concepts
Code Generation
The LLM receives a special “write code” tool:
{
name : "codemode" ,
description : "Execute TypeScript code with access to these tools: createProject, listProjects, createTask, ..." ,
inputSchema : z . object ({
code: z . string (). describe ( "TypeScript code to execute" )
})
}
When called, the code is:
Validated and transpiled
Executed in a secure sandbox
Results returned to the LLM
Generated code has access to tools via the codemode object:
// LLM-generated code
const project = await codemode . createProject ( "My Project" );
const tasks = await codemode . listTasks ( project . projectId );
return `Found ${ tasks . length } tasks` ;
Error Handling
The LLM can handle errors in its code:
try {
const project = await codemode . createProject ( name );
return `Created project ${ project . projectId } ` ;
} catch ( error ) {
return `Failed to create project: ${ error . message } ` ;
}
Security
Codemode is safe because:
Sandboxed execution - Code runs in isolated Workers/VMs
No file system access - Can’t read/write files
No network access - Can’t make arbitrary HTTP requests
Limited APIs - Only approved tools are available
Timeout enforcement - Code execution is time-limited
AI Chat Traditional tool calling with streaming
Dynamic Tools Client-defined tools at runtime
Workflows Multi-step workflows with approval gates
Codemode Package Full Codemode package documentation