ai
Version:
AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.
343 lines (278 loc) • 12.7 kB
text/mdx
---
title: Tools
description: Use first-party AI SDK tools to add capabilities to agents and applications.
---
# Tools
The AI SDK provides first-party tools for agents and applications. Each tool
includes its input schema, model-facing description, and
execution logic, ready to use with `generateText`, `streamText`, or `ToolLoopAgent`.
These tools work with any model that supports function calling.
Install tools from their individual `@ai-sdk/tool-*` packages.
For defining your own tools and configuring tool execution, see
[Tools and Tool Calling](/docs/ai-sdk-core/tools-and-tool-calling).
## Bash Tool
`experimental_createBashTool` lets agents run Bash commands to search, analyze, and transform files.
Supply an `Experimental_SandboxSession` to choose where commands run. Use
`@ai-sdk/sandbox-just-bash` for an in-memory environment,
`@ai-sdk/sandbox-vercel` for Vercel Sandbox, or your own implementation.
The tool has no direct dependency on either runtime.
### Installation
Install the tool and the sandbox adapter you want to use. The following examples
use just-bash:
<InstallPackages packages="ai @ai-sdk/tool-bash @ai-sdk/sandbox-just-bash" />
Requires Node.js 22.13 or newer.
### Usage
```ts
import { createJustBashNetworkSandboxSession } from '@ai-sdk/sandbox-just-bash';
import { experimental_createBashTool as createBashTool } from '@ai-sdk/tool-bash';
import { generateText, isStepCount } from 'ai';
const session = await createJustBashNetworkSandboxSession();
try {
const sandbox = session.restricted();
await sandbox.writeTextFile({
path: 'notes.txt',
content: 'Apples: 10\nPears: 20\n',
});
const bash = await createBashTool({ sandbox });
const result = await generateText({
model, // Your configured model with function calling support.
tools: { bash },
stopWhen: isStepCount(5),
prompt: 'Read notes.txt and write a summary to summary.txt.',
});
console.log(result.text);
console.log(await sandbox.readTextFile({ path: 'summary.txt' }));
} finally {
await session.destroy();
}
```
### Vercel Sandbox
Install `@ai-sdk/sandbox-vercel` and `@vercel/sandbox` to use an existing native
Vercel Sandbox. Wrap it with the adapter before passing it to the tool:
```ts
import { createVercelSandboxSessionFromNativeSandbox } from '@ai-sdk/sandbox-vercel';
import { experimental_createBashTool as createBashTool } from '@ai-sdk/tool-bash';
import { Sandbox } from '@vercel/sandbox';
const nativeSandbox = await Sandbox.create();
try {
const sandbox = createVercelSandboxSessionFromNativeSandbox(nativeSandbox);
await sandbox.writeTextFile({
path: 'notes.txt',
content: 'Hello from Vercel Sandbox',
});
const bash = await createBashTool({ sandbox });
// Use bash with generateText, streamText, or ToolLoopAgent here.
} finally {
await nativeSandbox.stop();
}
```
You can also create a managed session with
`createVercelNetworkSandboxSession()` from `@ai-sdk/sandbox-vercel` and pass
`session.restricted()`. For an existing native just-bash `Sandbox`, use
`createJustBashSandboxSessionFromNativeSandbox()` from `@ai-sdk/sandbox-just-bash`.
Raw runtime instances need their respective adapters; a custom
`Experimental_SandboxSession` can be passed directly.
### Options
`experimental_createBashTool()` requires an options object:
<PropertiesTable
content={[
{
name: 'sandbox',
type: 'Experimental_SandboxSession',
description: 'Session for command execution and file operations.',
},
{
name: 'workingDirectory',
type: 'string',
isOptional: true,
description:
"Absolute starting directory for each command, created if needed. When omitted, uses the sandbox's default directory.",
},
{
name: 'maxOutputLength',
type: 'number',
isOptional: true,
description:
'Positive integer limiting characters retained from each output stream before a truncation notice. Defaults to `30000`.',
},
{
name: 'timeout',
type: 'number',
isOptional: true,
description:
'Timeout per command execution, in milliseconds (integer from `1` to `2147483647`). No tool-level timeout by default.',
},
]}
/>
The model calls the tool with `{ command: string }` and receives
`{ stdout, stderr, exitCode }`. Nonzero exit codes are returned so the model can
inspect failures and recover. AI SDK cancellation is forwarded to the session.
Output truncation happens after execution and does not limit execution memory or
duration.
### Command timeout
Set `timeout` to cancel commands that run longer than the configured duration:
```ts
const bash = await createBashTool({
sandbox,
timeout: 10_000,
});
```
Each command gets a fresh timeout starting when its execution begins. This does
not include tool initialization or time waiting for model output or approval.
The timeout is combined with the caller's abort signal; either can cancel the
command. The sandbox must honor cancellation. Its result or error is passed
through; for example, just-bash returns exit code `124` when a running command is
cancelled. SDK and sandbox timeouts still apply when this option is omitted.
### Files and lifecycle
Commands use the sandbox's default working directory unless `workingDirectory`
is provided. Without an override, creating the tool runs no setup commands.
Seed files through `sandbox.writeTextFile({ path, content })` before running the
tool. With an explicit working directory, use absolute paths to write files there.
The sandbox controls which files commands can access. Available programs, network
access, shell state, and persistence depend on the supplied sandbox. Its
description is included in the tool's instructions.
Tools using the same session share files. Creating a tool does not reset existing
files or create a new sandbox. Use separate sessions for independent workflows.
The caller creates, reconnects, and destroys the sandbox.
`experimental_createBashTool()` returns the tool directly, ready to use as `tools: { bash }`.
Use the supplied sandbox's `readTextFile({ path })` to read generated text files;
it returns `null` if the file is missing. Relative read paths depend on the
sandbox adapter; just-bash resolves them against its default directory. With an
explicit working directory, use absolute paths to read files from that directory.
The same session provides other file operations.
## Web Fetch Tool
`experimental_createWebFetchTool` lets agents read web pages and documents by URL.
### Installation
<InstallPackages packages="ai @ai-sdk/tool-web-fetch" />
Requires Node.js 22.13 or newer.
### Usage
```ts
import { experimental_createWebFetchTool as createWebFetchTool } from '@ai-sdk/tool-web-fetch';
import { generateText, isStepCount } from 'ai';
const result = await generateText({
model, // Your configured model with function calling support.
tools: { webFetch: createWebFetchTool() },
stopWhen: isStepCount(5),
prompt: 'Summarize https://ai-sdk.dev/docs/introduction.',
});
```
### Options
`experimental_createWebFetchTool()` accepts an optional options object:
<PropertiesTable
content={[
{
name: 'allowedDomains',
type: 'string[]',
isOptional: true,
description:
"Only fetch from these domains and their subdomains, e.g. `['example.com']`. List a subdomain such as `docs.example.com` to allow only that subdomain. Also applies to redirects. Cannot be combined with `blockedDomains`.",
},
{
name: 'blockedDomains',
type: 'string[]',
isOptional: true,
description:
'Never fetch from these domains or their subdomains. Also applies to redirects.',
},
{
name: 'requireUrlInUserMessage',
type: 'boolean',
isOptional: true,
description:
'Only fetch URLs that appear in user messages. URLs in instructions, system messages, assistant messages, and tool results are not included. Defaults to `false`.',
},
{
name: 'maxLength',
type: 'number',
isOptional: true,
description:
'Maximum characters returned per call. Longer content can be read with `offset`. Defaults to `50000`.',
},
{
name: 'maxBytes',
type: 'number',
isOptional: true,
description: 'Maximum response size in bytes. Defaults to 10 MiB.',
},
{
name: 'timeout',
type: 'number',
isOptional: true,
description:
'Timeout in milliseconds for fetching each URL, including redirects. Defaults to `30000`.',
},
{
name: 'fetch',
type: 'FetchFunction',
isOptional: true,
description:
'Custom fetch implementation, for example to route requests through a proxy. It must validate resolved addresses itself.',
},
{
name: 'defuddleOptions',
type: 'DefuddleOptions',
isOptional: true,
description:
'Options for the [defuddle](https://github.com/kepano/defuddle) HTML content extractor, such as `contentSelector`.',
},
]}
/>
The model calls the tool with `{ url: string, offset?: number }` and receives
one of the following results:
- `{ type: 'document', url, finalUrl, contentType, title?, content, offset, totalLength, nextOffset? }`
for HTML and text.
- `{ type: 'error', url, errorCode, message, statusCode? }` when fetching fails.
Errors are returned so the model can recover, for example by trying another URL.
The model receives documents as text with the URL, title, and content type,
followed by the content. AI SDK cancellation aborts the request.
`experimental_createWebFetchTool()` returns the tool directly, ready to use as `tools: { webFetch }`.
### Content handling
Requests prefer markdown using the `Accept: text/markdown` header, which many
documentation sites support. Otherwise, HTML pages are converted to markdown
with [defuddle](https://github.com/kepano/defuddle), which removes navigation,
footers, and other boilerplate. Text formats such as plain text, JSON, XML, and
CSV are returned as is. PDFs, images, audio, video, and other binary formats
are not supported.
JavaScript is not executed. Pages that render their content in the browser may
return little or no content, in which case the model is told that the page may
require JavaScript.
The extractor sometimes selects the wrong part of a page. If you fetch pages
with a known structure, set `defuddleOptions.contentSelector` to choose the main
content element:
```ts
const webFetch = createWebFetchTool({
defuddleOptions: { contentSelector: 'article' },
});
```
Content longer than `maxLength` is truncated. The model is told to call the tool
again with `offset` to continue reading. Converted documents are cached for a
few minutes per tool instance, so reading at an offset does not fetch the page
again.
### Security
The tool makes GET requests without cookies or credentials, and rejects URLs
that contain credentials. Each request and redirect is validated against
private and reserved addresses such as `localhost` and cloud metadata
endpoints when they appear as IP literals or blocked local hostnames. The
default transport does not validate DNS results. On Node.js, inject
`guarded-fetch` to block hostnames resolving to private addresses and
DNS rebinding, unless your application controls the destinations or already
enforces equivalent SSRF protection through its fetch implementation or network
egress policy. URLs selected by a model remain untrusted:
```ts
import { createGuardedFetch } from 'guarded-fetch';
import { experimental_createWebFetchTool as createWebFetchTool } from '@ai-sdk/tool-web-fetch';
const fetch = createGuardedFetch();
const webFetch = createWebFetchTool({ fetch });
// Reuse the transport and call await fetch.close() at application shutdown.
```
Restrict network egress at the platform level, especially on other runtimes.
See [Secure URL Fetching](/docs/advanced/secure-url-fetching).
Fetched content is untrusted and can contain prompt injections. When an agent
has access to private data, a model could also be instructed to send that data
to another site, for example in a URL query string. Restrict fetching with
`allowedDomains`, enable `requireUrlInUserMessage` so only URLs from user
messages can be fetched, or require [tool approval](/docs/ai-sdk-core/tools-and-tool-calling#tool-execution-approval).
`requireUrlInUserMessage` ignores URLs in tool results because the model can
place arbitrary text there, for example through tool inputs that are reflected
in outputs. As a result, the model cannot follow links from search results or
fetched pages in this mode.