---
title: "Bash Tool"
description: "Build the bash tool in lib/tools.ts. Define a Zod schema for command and args, accept a Sandbox instance, and execute commands with runCommand."
canonical_url: "https://vercel.com/academy/filesystem-agents/bash-tool"
md_url: "https://vercel.com/academy/filesystem-agents/bash-tool.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-11T07:41:11.589Z"
content_type: "lesson"
course: "filesystem-agents"
course_title: "Building Filesystem Agents"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Bash Tool

# Create the Bash Tool

Since you're building a file system agent, you need to instruct the agent on how to use bash to navigate. You'll do this by defining a new tool in `lib/tools.ts`, a function that returns a tool from the AI SDK.

## Outcome

You have a `createBashTool` function in `lib/tools.ts` that accepts a `Sandbox`, defines a Zod schema for `command` and `args`, and executes commands via `sandbox.runCommand`.

## Fast Track

1. Create `createBashTool` in `lib/tools.ts` using the `tool` helper from `ai`
2. Define a Zod input schema with `command` (string) and `args` (string array)
3. Implement `execute` to call `sandbox.runCommand` and return stdout, stderr, and exitCode

## Hands-on Exercise 1.3

Build the complete bash tool in `lib/tools.ts`.

**Requirements:**

1. Import `tool` from `ai`, `z` from `zod`, and the `Sandbox` type from `@vercel/sandbox`
2. Export a `createBashTool` function that accepts a `Sandbox` parameter
3. Return a `tool()` with a description, Zod input schema, and execute function
4. The execute function runs the command in the sandbox and returns stdout, stderr, and exitCode

**Implementation hints:**

- Via a Zod schema, you are informing the agent that it needs to generate a `command` and `args` to pass to this tool
- Use `.describe()` on each Zod field to give the LLM context about what to put there
- `sandbox.runCommand(command, args)` returns a result object. Call `.stdout()` and `.stderr()` on it (they're async).
- Import `Sandbox` as a type-only import since you only need it for the function signature

### Creating the tool

Start by defining the tool without sandbox execution. This function returns a tool to execute bash commands to explore files, but doesn't actually execute any code yet:

```ts title="lib/tools.ts"
import { tool } from 'ai';
import { z } from 'zod';

export function createBashTool() {
  return tool({
    description: `
      Execute bash commands to explore transcript and instruction files.
      Examples (not exhaustive): ls, cat, less, head, tail, grep
      `,
    inputSchema: z.object({
      command: z.string().describe('The bash command to execute'),
      args: z.array(z.string()).describe('Arguments to pass to the command')
    }),
    execute: async ({ command, args }) => {
      // code that executes when the tool is called
    }
  });
}
```

As it stands, the `execute` callback function is empty. To actually execute the bash that the LLM is generating, you need to give it a safe execution environment. This is where sandbox comes in.

To allow a sandbox instance to be passed in, update the function declaration so that it expects a sandbox as a parameter:

```ts title="lib/tools.ts"
import type { Sandbox } from '@vercel/sandbox';

export function createBashTool(sandbox: Sandbox) {
  // ...
}
```

### Define what the tool does

Pass the commands and args to the sandbox to execute:

```ts title="lib/tools.ts"
execute: async ({ command, args }) => {
  const result = await sandbox.runCommand(command, args);
  const textResults = await result.stdout();
  const stderr = await result.stderr();
  return {
    stdout: textResults,
    stderr: stderr,
    exitCode: result.exitCode,
  };
},
```

Using the `runCommand` method on `sandbox`, you run the generated command and await the standard output, error output, and exit status of the process.

Now you're almost ready to give this tool to the agent to use. But first, you need to initialize the sandbox in the agent so you can pass it to the `createBashTool` function.

## Try It

This tool isn't connected to the agent yet. You'll do that in the next lesson. For now, verify the file compiles:

1. **Check for TypeScript errors** in your editor. If `lib/tools.ts` shows no red squiggles, the types are correct.

2. **Verify the export.** `createBashTool` should be a named export that takes a `Sandbox` and returns a tool.

\*\*Note: No runtime test yet\*\*

You can't test the tool in the browser until you wire it up to the agent in the next lesson. For now, just verify it compiles.

## Commit

```bash
git add lib/tools.ts
git commit -m "feat(tools): add createBashTool with Zod schema"
```

## Done-When

- [ ] `lib/tools.ts` exports `createBashTool`
- [ ] The function accepts a `Sandbox` parameter and returns a `tool()`
- [ ] The Zod schema defines `command` (string) and `args` (string array) with `.describe()` annotations
- [ ] The execute function calls `sandbox.runCommand` and returns `{ stdout, stderr, exitCode }`
- [ ] No TypeScript errors in the file

## Solution

```ts title="lib/tools.ts"
import { tool } from 'ai';
import { z } from 'zod';
import type { Sandbox } from '@vercel/sandbox';

export function createBashTool(sandbox: Sandbox) {
  return tool({
    description: `
      Execute bash commands to explore transcript and instruction files.
      Examples (not exhaustive): ls, cat, less, head, tail, grep
      `,
    inputSchema: z.object({
      command: z.string().describe('The bash command to execute'),
      args: z.array(z.string()).describe('Arguments to pass to the command')
    }),
    execute: async ({ command, args }) => {
      const result = await sandbox.runCommand(command, args);
      const textResults = await result.stdout();
      const stderr = await result.stderr();
      return {
        stdout: textResults,
        stderr: stderr,
        exitCode: result.exitCode
      };
    }
  });
}
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
