# Code mode

> **Alpha:** This feature is in alpha. 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.5',
  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_<id>`. 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.5',
  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)