Skip to main content
A Box is a real Linux container: shell, filesystem, ports, and a browser. This guide wraps those capabilities as Vercel AI SDK tools, so any streamText call can hand the model a computer instead of just a chat window. You define the tools once against a Box instance, then spread them into tools. The model decides when to run code, read a file, or start a server.

1. Installation

Get a Box API key from the Upstash Console:
.env.local

2. One box per conversation

Tools need a box to act on. Give each conversation its own, so every tool call in it lands on the same filesystem. A box auto-pauses when idle and resumes on the next call, so an open chat costs nothing while nobody is typing. Derive the box name from the authenticated user and the chat id, and hash the pair. A box name is an address: anyone who can guess or supply it gets a shell on that box, so a name taken straight from the request body would let one user attach to another user’s conversation.
lib/box.ts
userId has to come from your session on the server. Never from the request body. For one-shot computation with no state to keep, use an EphemeralBox instead. Step 6 shows that variant.

3. Define the tools

Each tool is a thin wrapper over one Box capability. Keep the returned objects small and JSON-serializable: they go back into the model’s context on the next step.
lib/box-tools.ts
The file tools are rooted at /workspace/home, and paths outside it are rejected. exec can write anywhere, but anything you move through the files API has to live under that root. Tell the model so in the system prompt.
runCommand waits for the command to exit, which is why startServer exists: a foreground npm run dev would block the tool call forever and the model would never reach exposePort. startServer detaches the process, confirms something is actually listening, and returns the crash log when nothing is. Take only the tools you need. A support bot that answers numeric questions wants runCode alone. A coding agent wants the whole set.

4. Wire them into a route

stopWhen: stepCountIs(...) is what lets the model chain calls: write a file, run it, read the output, then answer.
app/api/chat/route.ts

5. Add a UI

useChat renders tool calls as tool-* parts, so you can show each step as the model works.
app/page.tsx
Ask it to “plot the first 20 Fibonacci numbers and serve the chart on port 8000” and you can watch it write a script, run it, start a server, and hand back a URL.

6. One-off runs with an EphemeralBox

When there is no state worth keeping between calls, skip the per-chat box entirely. An EphemeralBox is created for the tool call and deleted when it returns, so nothing persists and nothing leaks.
lib/code-interpreter.ts
ttl: 120 deletes the box after two minutes even if the finally never runs. Always set a timeout on exec.code too: without one, an infinite loop hangs the request until the backend gives up.

Next steps

  • Filesystem and shell for the full API behind these tools.
  • Browser to add page reading and screenshots to the toolset.
  • Public URLs for authenticating the URLs exposePort hands out.
  • Snapshots to boot each conversation from a pre-installed environment.