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
- Create
createBashToolinlib/tools.tsusing thetoolhelper fromai - Define a Zod input schema with
command(string) andargs(string array) - Implement
executeto callsandbox.runCommandand return stdout, stderr, and exitCode
Hands-on Exercise 1.3
Build the complete bash tool in lib/tools.ts.
Requirements:
- Import
toolfromai,zfromzod, and theSandboxtype from@vercel/sandbox - Export a
createBashToolfunction that accepts aSandboxparameter - Return a
tool()with a description, Zod input schema, and execute function - 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
commandandargsto 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
Sandboxas 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:
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:
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:
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:
-
Check for TypeScript errors in your editor. If
lib/tools.tsshows no red squiggles, the types are correct. -
Verify the export.
createBashToolshould be a named export that takes aSandboxand returns a tool.
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
git add lib/tools.ts
git commit -m "feat(tools): add createBashTool with Zod schema"Done-When
lib/tools.tsexportscreateBashTool- The function accepts a
Sandboxparameter and returns atool() - The Zod schema defines
command(string) andargs(string array) with.describe()annotations - The execute function calls
sandbox.runCommandand returns{ stdout, stderr, exitCode } - No TypeScript errors in the file
Solution
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
};
}
});
}Was this helpful?