Runtime

How to give an LLM a code execution tool with function calling

Declare a run-command tool in your model's function-calling format, run each call in a sandbox, and return exit code and output.

On Runtime the tool definitions are already written. withruntime/tools gives four tools bound to one sandbox, each as a name, a description, a JSON Schema and a function: run a command, read a file, write a file, list a directory. Integrations for eleven agent frameworks wrap them, and each ran in its framework's own agent loop against real sandboxes on 23 September 2026 (frameworks). The sandbox behind them is a Firecracker microVM billed on the CPU it uses, $0.025 per vCPU-hour.

How tool use with a sandbox works

Function calling is a contract between your application and the model. You list tools, each with a name, a description and a JSON Schema for its arguments. The model answers with a tool call instead of text. Your application runs the call and sends the result back, and the model continues. The model never runs anything itself; your code does, and where it runs is your choice.

Running the call in a sandbox means the command the model chose executes on a separate machine with its own kernel, disk and network rules. The split of control stays clean:

Your application chooses The model chooses
Which sandbox, its size and lifetime The command to run
Its network rules and secrets The file paths to read and write
The API key and its daily limit The cwd and a per-call timeout
When the sandbox stops When it has finished

The short answer

Bind the tools to a sandbox, send their schemas to your model, and dispatch each tool call by name:

TypeScriptimport { Sandbox } from "withruntime";import { sandboxTools } from "withruntime/tools";type ToolCall = { id: string; name: string; arguments: string };await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 1800 });const tools = sandboxTools(sbx);// What your model's API needs: a name, a description and a JSON Schema each.const declarations = tools.map((t) => ({  name: t.name,  description: t.description,  schema: t.inputSchema,}));console.log(declarations.map((d) => d.name));async function dispatch(call: ToolCall) {  const tool = tools.find((t) => t.name === call.name);  if (!tool) return { id: call.id, output: `No tool named ${call.name}` };  // The model's arguments are untrusted JSON: the tool checks the fields it uses.  const result = await tool.execute(JSON.parse(call.arguments) as never);  return { id: call.id, output: JSON.stringify(result) };}// One call, as a model would send it:console.log(  await dispatch({    id: "c1",    name: "runtime_exec",    arguments: '{"command":"python3 -c \\"print(6 * 7)\\""}',  }),);
Pythonimport jsonfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolswith Sandbox.create(network={"internet": False}, timeout_seconds=1800) as sbx:    tools = {tool.__name__: tool for tool in sandbox_tools(sbx)}    print(sorted(tools))  # runtime_exec, runtime_list_files, runtime_read_file, runtime_write_file    def dispatch(name: str, arguments: str) -> str:        if name not in tools:            return f"No tool named {name}"        return json.dumps(tools[name](**json.loads(arguments)))    print(dispatch("runtime_write_file", '{"path": "hello.py", "content": "print(6 * 7)\\n"}'))    print(dispatch("runtime_exec", '{"command": "python3 hello.py"}'))

Rename the schema field to what your provider's API calls it. In Python the tools are plain functions with type hints and docstrings, which is what most Python frameworks turn into schemas themselves.

What the four tools do

Tool Arguments Returns
runtime_exec command, optional cwd and timeoutSeconds exitCode, stdout, stderr, timedOut
runtime_read_file path The file's text
runtime_write_file path, content How many bytes it wrote, and where
runtime_list_files optional path and depth Entries with path, type and size

Relative paths are under /workspace, and output is capped so one noisy command cannot fill the model's context. Tell the model to check exitCode: a call that returned is not a command that worked.

If you use a framework

The same four tools come prepared for most agent frameworks (frameworks):

Framework What you add
Vercel AI SDK, Mastra runtimeTools(sbx) from withruntime/ai
Claude Agent SDK runtimeMcpServer(sbx) from withruntime/claude-agent-sdk
OpenAI Agents SDK A sandbox client for SandboxAgent, or four function tools
LangChain, LangGraph, CrewAI sandbox_tools(sbx) wrapped with the framework's tool
Google ADK, Pydantic AI sandbox_tools(sbx) as the agent's tools
LlamaIndex FunctionTool.from_defaults over sandbox_tools(sbx)
Deep Agents RuntimeSandbox(sbx) as the agent's backend

A framework's own local shell and file tools keep running on your machine, so switch those off when the sandbox tools replace them.

Guard the tool, not the prompt

A system prompt that says "never delete files" is a request. The settings on the sandbox are enforced on the host, whatever the model writes:

  • network: { internet: false }, or an allow list of the hosts the task needs, binds root inside the sandbox too.
  • A secret gives code an API key it can use but never read: the sandbox holds a placeholder, and the proxy adds the value on HTTPS requests to that key's own hosts (security).
  • timeoutSeconds with onLeaseEnd: "stop" bounds how long the machine lives, even if your process crashes mid-loop.
  • maxCostMicros refuses a create whose first lease would cost more, and a daily spending limit on the key caps a whole day.

What it costs

Take 5,000 agent tasks a month. Each keeps a 2 vCPU, 4 GiB sandbox running for 4 minutes, and its 25 tool calls use 30 CPU-seconds in all:

TextCPU:    5,000 × 30 s / 3,600 × $0.025          = $1.04Memory: 5,000 × 240 s / 3,600 × 4 GiB × $0.0075 = $10.00Total:                                            $11.04

About two tenths of a cent a task. While the model thinks, the sandbox is billed its memory and a CPU floor of a twentieth of a vCPU, $0.03125 an hour at this size (pricing). Model tokens are billed by your model provider. New accounts get 50 free sandbox hours, no card.

Start

Terminalnpm install withruntimenpx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)'

The first run prints a link to approve in your browser; after that the SDK finds the connection by itself.

Related: a coding agent sandbox, code interpreter for chatbots, run untrusted LLM code safely, what an agent sandbox is.

Facts on this page were checked on 25 September 2026.