# Code mode > **Experimental:** This feature is experimental. Breaking changes may occur without a major version bump until the API is stable. Code mode gives an agent a single tool that runs a short TypeScript program in a sandbox. Instead of calling tools one at a time, the model writes a program that orchestrates your existing tools as `external_*` functions, batching calls with `Promise.all`, aggregating with `reduce`, branching, and doing math in a real runtime, then returns a single result. `createCodeMode` returns this tool with the default id `execute_typescript`. The id is configurable, so an agent can have several code mode tools at once, each scoped to a different set of tools (see [Scoping tools across multiple code tools](#scoping-tools-across-multiple-code-tools)). ## When to use code mode Use code mode when a task touches several tools at once or needs real computation between calls: - Fewer round-trips: a task that touches several tools runs in one tool call instead of one model turn per tool. - Correct math: sums, averages, and other arithmetic run as JavaScript, not as token prediction. - Planning up front: filtering, aggregation, and branching happen inside the program rather than across separate turns. ## How it works Your tools keep running on the host with full validation, request context, and tracing. Only the model's orchestration code runs in the sandbox. Each `external_*` call is bridged back to the real tool on the host, so dangerous tools, approvals, and validation behave exactly as they do for normal tool calls. The program runs in a [Workspace sandbox](https://mastra.ai/docs/workspace/overview). A sandbox is required, because code mode runs model-authored code and the execution boundary must be chosen deliberately. Pass one via `sandbox`, or run the agent in a workspace that provides one. To execute on the host machine, pass `new LocalSandbox()` explicitly. This runs the program as a host `node` process with host privileges, so only use it for trusted or local development. ## Quickstart `createCodeMode` returns the tool plus generated instructions. With no `id`, the tool is named `execute_typescript`. Add both to your agent: ```typescript import { Agent } from '@mastra/core/agent' import { createCodeMode, createTool } from '@mastra/core/tools' import { LocalSandbox } from '@mastra/core/workspace' import { z } from 'zod' const getTopProducts = createTool({ id: 'getTopProducts', description: 'Get top selling products', inputSchema: z.object({ limit: z.number() }), outputSchema: z.object({ products: z.array(z.object({ id: z.string(), name: z.string(), totalSales: z.number() })), }), execute: async ({ limit }) => fetchTopProducts(limit), }) const getProductRatings = createTool({ id: 'getProductRatings', description: 'Get ratings for a product', inputSchema: z.object({ productId: z.string() }), outputSchema: z.object({ ratings: z.array(z.object({ score: z.number() })) }), execute: async ({ productId }) => fetchRatings(productId), }) const { tool, instructions } = createCodeMode({ tools: { getTopProducts, getProductRatings }, sandbox: new LocalSandbox(), // required; runs on the host — see "How it works" }) const agent = new Agent({ name: 'shop-assistant', instructions: ['You are a helpful shopping assistant.', instructions], model: 'openai/gpt-5.4', tools: { execute_typescript: tool }, }) ``` Asked "What are the top 5 products and the average rating for each?", the model emits one `execute_typescript` call instead of many separate tool calls: ```typescript const top = await external_getTopProducts({ limit: 5 }) const ratings = await Promise.all( top.products.map(p => external_getProductRatings({ productId: p.id })), ) return top.products.map((product, i) => { const scores = ratings[i].ratings.map(r => r.score) const avg = scores.reduce((sum, s) => sum + s, 0) / scores.length return { name: product.name, sales: product.totalSales, averageRating: Math.round(avg * 100) / 100, } }) ``` All five rating lookups run in parallel, the averages are computed in JavaScript, and the agent receives one structured result. ## Configuration ```typescript const { tool, instructions } = createCodeMode({ tools: { getTopProducts, getProductRatings }, // exposed as external_*; only these can be called sandbox, // required WorkspaceSandbox, unless the agent runs in a workspace that provides one timeout: 30_000, // optional execution timeout in ms (default 30000) id: 'execute_typescript', // optional tool id (default "execute_typescript") }) ``` | Option | Type | Description | | --------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | `tools` | `ToolsInput` | Tools exposed to the program as `external_`. Only these may be called. | | `sandbox` | `WorkspaceSandbox` | Sandbox to run the program in. Required unless the agent runs in a workspace that provides one. Pass `new LocalSandbox()` to run on the host. | | `timeout` | `number` | Execution timeout in milliseconds. Default `30000`. | | `id` | `string` | The generated tool's id. Default `execute_typescript`. | ## Result The tool returns a `CodeModeToolResult`: ```typescript type CodeModeToolResult = { success: boolean result?: unknown // value returned by the program logs?: string[] // captured console output error?: { message: string; name?: string; line?: number } } ``` ## Inspecting the instructions The generated `instructions` contain the usage contract and a typed `external_*` declaration for each tool, derived from your tool schemas. Print them to see exactly what the model receives: ```typescript import { createCodeModeInstructions } from '@mastra/core/tools' console.log(createCodeModeInstructions({ tools: { getTopProducts, getProductRatings } })) ``` ## Scoping tools across multiple code tools `createCodeMode` captures its own allow-list. Call it more than once to give an agent several code tools, each scoped to a different subset of tools. A program can only call the `external_*` functions for the tools passed to its own `createCodeMode` call, so the subsets stay isolated. Give each tool a distinct `id` so their ids do not collide, and add each tool's instructions to the agent: ```typescript const sales = createCodeMode({ id: 'sales_code', tools: { listRecentOrders, getCustomer }, sandbox, }) const inventory = createCodeMode({ id: 'inventory_code', tools: { listProducts, getSupplier }, sandbox, }) const agent = new Agent({ name: 'ops-assistant', instructions: ['You are an ops assistant.', sales.instructions, inventory.instructions], model: 'openai/gpt-5.4', tools: { sales_code: sales.tool, inventory_code: inventory.tool }, }) ``` A program run by `sales_code` cannot call an inventory tool, and the reverse holds too. Use this for least-privilege scoping and to keep each tool's prompt surface small. ## Tips - Keep tools focused, so each does one thing well and the model composes them in code. - Code mode helps most when calls can be parallelized with `Promise.all`. - Use `console.log` for debugging. Logs are captured in the result. ## Related - [Tools](https://mastra.ai/docs/agents/using-tools) - [Workspace overview](https://mastra.ai/docs/workspace/overview)