Advanced Topics

Code Mode

Let LLMs orchestrate multiple tool calls in a single JavaScript execution.

What is Code Mode?

Code Mode replaces the traditional multi-turn tool calling pattern with a single code tool. Instead of the LLM calling tools one at a time — each requiring a round-trip — it writes JavaScript code that orchestrates multiple tools in one execution.

Traditional MCPCode Mode
PatternLLM calls tools one by oneLLM writes JS that calls tools
Round-tripsOne per tool callOne for all operations
Complex logicMultiple turns for conditionals/loopsNative JS control flow
Token usageHigher (repeated context)Lower (single invocation)
Code Mode is experimental for now. The API may evolve as we stabilize this feature based on feedback.

Why Code Mode?

Every LLM round-trip resends all tool descriptions as context. With traditional MCP, a task that requires 5 steps with 50 tools sends the full tool catalog 5 times — that's 15,500 tokens just for tool descriptions. Code Mode sends compact TypeScript signatures in a single tool, cutting that to ~3,000 tokens.

The scaling problem

In traditional MCP, tool description overhead scales as tools × round-trips. Code Mode replaces all tools with one code tool containing compact type signatures — and usually needs fewer round-trips.

Server sizeTraditional MCPCode ModeSavings
10 tools, 3-step task~1,860 tokens in tool descriptions~920 tokens-51%
25 tools, 4-step task~6,200 tokens~1,700 tokens-73%
50 tools, 5-step task~15,500 tokens~3,000 tokens-81%
100 tools, 5-step task~31,000 tokens~5,600 tokens-82%

These numbers represent tool description overhead only. Total savings depend on the task, but the trend is clear: the more tools you have, the bigger the savings.

On a real benchmark with Claude Sonnet (9 tools, 7 tasks), Code Mode saved 15% of total tokens overall and up to 33% on complex batch operations — collapsing 8 tool calls into a single JavaScript block.

Beyond token savings

Code Mode also unlocks patterns that traditional MCP cannot do efficiently:

  • Parallel executionPromise.all() for independent calls instead of sequential round-trips
  • Conditional logicif/else branching without an extra LLM step
  • Loopsfor over data instead of repeating tool calls one by one
  • Error handlingtry/catch to handle failures mid-workflow

Setup

1. Install secure-exec

Code Mode uses secure-exec to run LLM-generated code in a secure V8 isolate:

pnpm add secure-exec

2. Enable on a handler

Add experimental_codeMode to any handler:

// server/mcp/index.ts
export default defineMcpHandler({
  experimental_codeMode: true,
})

That's it. The module replaces all your tools with a single code tool that the LLM uses to orchestrate them.

How It Works

When Code Mode is enabled:

  1. All registered tools are converted to TypeScript type definitions
  2. A code tool is created with those types embedded in its description
  3. The LLM writes JavaScript using a codemode object to call tools
  4. The code runs in a V8 isolate with only RPC access to your tools

Example: what the LLM generates

Given tools get-user, list-todos, and create-todo, the LLM receives type definitions and writes code like:

const user = await codemode.get_user({ id: "123" });
const todos = await codemode.list_todos({ userId: user.id });

if (todos.length === 0) {
  await codemode.create_todo({
    title: "Welcome task",
    userId: user.id,
  });
}

return { user, todos };

Configuration Options

Pass an options object instead of true for fine-grained control:

server/mcp/index.ts
export default defineMcpHandler({
  experimental_codeMode: {
    memoryLimit: 64,
    cpuTimeLimitMs: 10_000,
    maxResultSize: 102_400,
    progressive: false,
    description: undefined,
  },
})
memoryLimit
number
Default: 64V8 isolate memory limit in MB. Set once at first execution — call disposeCodeMode() to change.
cpuTimeLimitMs
number
Default: 10000CPU time limit per execution in milliseconds. The sandbox is killed after this duration.
maxResultSize
number
Default: 102400 (100 KB)Maximum result size in bytes before truncation. Large results are intelligently truncated — arrays by number of items, objects by number of keys.
progressive
boolean
Default: falseEnable progressive disclosure mode. See Progressive Mode below.
description
string
Default: built-in templateCustom description for the code tool. Supports {{types}} and {{count}} placeholders.

Progressive Mode

When your server exposes many tools (50+), embedding all type definitions in the code tool description becomes expensive in tokens. Progressive mode solves this by splitting into two tools:

  1. search — discovers tools by keyword, returns their signatures
  2. code — executes code using discovered tools
server/mcp/index.ts
export default defineMcpHandler({
  experimental_codeMode: {
    progressive: true,
  },
})

The LLM workflow becomes:

LLM calls: search({ query: "user" })

→ Found 2/12 tools matching "user":
  codemode.get_user: (input: { id: string }) => Promise<unknown>; // Get user by ID
  codemode.list_users: () => Promise<unknown>; // List all users
Progressive mode adds an extra tool call but significantly reduces the initial prompt size. Only use it when you have a large number of tools — for small toolsets, standard mode is more efficient.

Custom Description

Override the code tool description to customize LLM instructions:

server/mcp/index.ts
export default defineMcpHandler({
  experimental_codeMode: {
    description: `You have {{count}} tools available. Write JavaScript using the codemode object.

{{types}}

Always combine related operations into a single code block.`,
  },
})

The {{types}} placeholder is replaced with the generated TypeScript definitions. The {{count}} placeholder is replaced with the number of available tools.

In progressive mode, {{types}} is not available since types are discovered via the search tool.

Security

Running LLM-generated code requires serious security measures. Code Mode implements defense in depth across 7 layers to ensure the sandbox cannot escape, access unauthorized resources, or exhaust host resources.

Sandbox Isolation

LLM-generated code runs in a separate V8 isolate via secure-exec. This is the same isolation technology used by Cloudflare Workers and similar platforms. The sandbox has:

  • No filesystem access — cannot read, write, or list files
  • No Node.js APIs — no require(), import(), process, fs, child_process, etc.
  • No environment variables — cannot read secrets or configuration
  • No host process access — cannot modify the parent process in any way

Network Restrictions

The sandbox can only communicate with the internal RPC server. All other network access is blocked:

  • Port-locked — Only the randomly-assigned RPC port is accessible. Other localhost services (databases, admin panels, other apps) are blocked.
  • Host-locked — Only 127.0.0.1 and localhost are allowed. External hosts are rejected.
  • No DNS — DNS resolution is disabled entirely.
  • No redirects — HTTP redirects are rejected (redirect: 'error'), preventing SSRF via open redirects.

RPC Authentication

Communication between the sandbox and the host uses a per-session cryptographic token:

  • 256-bit token — Generated with crypto.randomBytes(32) at RPC server startup.
  • Header-based auth — Every request must include the token via x-rpc-token header.
  • 403 on mismatch — Requests without a valid token are rejected immediately.

This prevents other local processes from calling your MCP tools through the RPC port.

Resource Limits

ResourceDefaultConfigurableProtection
CPU time10 secondscpuTimeLimitMsSandbox is killed on timeout — prevents infinite loops
Memory64 MBmemoryLimitV8 isolate hard limit — prevents OOM crashes
Result size100 KBmaxResultSizeIntelligent truncation (arrays by items, objects by keys)
Log entries200NoConsole output capped — prevents console.log flooding

Input Validation

Tool names are interpolated into the sandbox code template. To prevent code injection:

  • Strict identifier regex — Every tool name is validated against /^[\w$]+$/ before being injected into the sandbox template.
  • Sanitization — Names are sanitized upstream (get-userget_user), but a second validation layer at the template level ensures defense in depth.
  • Rejection — If a name fails validation, the execution throws immediately — no partial injection.

Summary

The sandbox can only communicate with your registered MCP tools via the authenticated RPC bridge. It cannot access the filesystem, network, environment, or any other host resources.

Usage with Other Features

Code Mode is fully compatible with other module features. Your tools remain unchanged — only the way they are exposed to the LLM changes.

// server/mcp/index.ts
export default defineMcpHandler({
  experimental_codeMode: true,
  middleware: async (event) => {
    const user = await getUser(event)
    if (!user) {
      throw createError({ statusCode: 401 })
    }
    event.context.user = user
  },
})

Middleware runs before tool execution — your tools access event.context as usual. Tools with enabled guards are excluded from the generated type definitions and the codemode object.

Tool Name Sanitization

MCP tool names (kebab-case) are automatically converted to valid JavaScript identifiers for the codemode object:

MCP NameJavaScript Name
get-userget_user
list-todoslist_todos
123-tool_123_tool
deletedelete_

Reserved JavaScript words are suffixed with _. Names starting with a digit are prefixed with _.

Cleanup

Call disposeCodeMode() during shutdown to release resources (V8 runtime, RPC server):

import { disposeCodeMode } from '#imports'

// In a shutdown hook or cleanup function
disposeCodeMode()

Next Steps

Copyright © 2026