new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

AI SDK · Core · all subjects

tools and tool calling

377 notes in this subject, read out of this brain and free to use. This is page 1 of 7.

Function tool example using Zod schema

Example of a function tool: import { tool } from 'ai'; import { z } from 'zod'; const weatherTool = tool({ description: 'Get the weather in a location', inputSchema: z.object({ location: z.string().describe('The location to get the weather for'), }), execute: async ({ location }) => { return { temperature: 72, conditions: 'sunny' }; }, });

Provider-defined tools: provider schema, you execute

Provider-defined tools are where the provider specifies the tool's inputSchema and description, but you provide the execute function. Execution happens on your side. The model has been specifically trained to use these tools effectively. Use when the provider offers a tool the model is trained to use well, and you want better performance for that specific task.

experimental_refineToolInput example

Example of using experimental_refineToolInput: const result = await generateText({ model: 'openai/gpt-5-mini', tools: { search: tool({ inputSchema: z.object({ query: z.string(), category: z.string().nullable(), }), execute: async ({ query, category }) => { return search({ query, category }); }, }), }, experimental_refineToolInput: { search: input => ({ ...input, category: input.category === '' ? null : input.category, }), }, prompt: 'Search for ski jackets with no category.', });

Ready-to-use AI SDK tool packages

Available ready-to-use tool packages include: @exalabs/ai-sdk (web search), @parallel-web/ai-sdk-tools (web search and extract), @perplexity-ai/ai-sdk (web search with real-time results), @tavily/ai-sdk (search, extract, crawl, and map tools), Stripe agent tools, StackOne ToolSet (enterprise SaaS integrations), agentic (20+ tools connecting to external APIs), Amazon Bedrock AgentCore (Browser and Code Interpreter), @airweave/vercel-ai-sdk (semantic search across 35+ data sources), Composio (250+ tools), JigsawStack (30+ custom models), AI Tools Registry (Shadcn-compatible tool definitions), Toolhouse (25+ actions), and bash-tool (bash, readFile, writeFile tools).

Provider-executed tool example (OpenAI web search)

Example of a provider-executed tool: import { openai } from '@ai-sdk/openai'; import { generateText } from 'ai'; const result = await generateText({ model: openai('gpt-5.2'), tools: { web_search: openai.tools.webSearch(), }, prompt: 'What happened in the news today?', });

Provider-defined tool example (Anthropic bash)

Example of a provider-defined tool: import { anthropic } from '@ai-sdk/anthropic'; import { generateText } from 'ai'; const result = await generateText({ model: anthropic('claude-opus-4-5'), tools: { bash: anthropic.tools.bash_20250124({ execute: async ({ command }) => { return runCommand(command); }, }), }, prompt: 'List files in the current directory', });

MCP tool servers for AI SDK

Pre-built tools available as MCP servers include: Smithery (6,000+ MCPs including Browserbase and Exa), Pipedream (3,000+ integrations), and Apify (marketplace of tools for web scraping, data extraction, and browser automation).

Function tools: full control implementation

Function tools are defined entirely by you, including description, input schema, and optional execute function. They are provider-agnostic and give full control. Use when you need full control, want provider portability, or are implementing application-specific functionality.

experimental_refineToolInput: normalize tool inputs

The experimental_refineToolInput option allows you to normalize a parsed tool input before it is executed and before it appears in outputs, lifecycle callbacks, and telemetry. This is useful when different LLM providers generate slightly different tool inputs for the same tool input type. Refinement functions are typed per tool and each function receives the typed input for its tool and must return an input with the same type shape.

Multi-step calls with stopWhen

You can automatically pass tool results back to the LLM using multi-step calls with streamText and generateText.

Dynamic tools: runtime-defined input/output types

Dynamic tools are function-style tools where input and output types are not known at development time. The input parameter in the execute function is typed as unknown. Use when tools are discovered or generated at runtime and their exact TypeScript input/output types are not known when you write the code.

Tool definition: three required properties

A tool is an object with three properties: description (optional string or function that influences when the tool is picked), inputSchema (Zod or JSON schema that defines tool input and is consumed by the LLM and used to validate tool calls), and execute (optional async function called with arguments from the tool call).

Provider-executed tools: provider handles everything

Provider-executed tools run entirely on the provider's servers. You configure them, but the provider handles execution. These are sometimes called server-side tools. Examples include OpenAI's web search and Anthropic's code execution. Use when you want powerful functionality without managing the infrastructure yourself.

Tool types comparison table

Tool type comparison: Function Tools - Execution: Your code, Schema: You define, Portability: Works with any provider, Model Training: General tool use, Setup: You implement everything. Dynamic Tools - Execution: Your code, Schema: Runtime-defined, Portability: Works with any provider, Model Training: General tool use, Setup: You load or generate the tool. Provider-Defined Tools - Execution: Your code, Schema: Provider defines, Portability: Provider-specific, Model Training: Optimized for the tool, Setup: You implement execute. Provider-Executed Tools - Execution: Provider's servers, Schema: Provider defines, Portability: Provider-specific, Model Training: Optimized for the tool, Setup: Configuration only.

Publishing tool package example

Example of exporting tools from a tool package (my-tools/index.ts): import { tool } from 'ai'; import { z } from 'zod'; export const myTool = tool({ description: 'A helpful tool', inputSchema: z.object({ query: z.string(), }), execute: async ({ query }) => { return result; }, });

Publishing tool packages to npm

You can publish your own tool packages to npm by exporting your tool objects from your package. Anyone can then install and use your tools by importing them. The AI SDK Tool Package Template provides a ready-to-use starting point for publishing your own tools.

Dynamic tool example with runtime types

Example of a dynamic tool: import { dynamicTool } from 'ai'; import { z } from 'zod'; const runtimeTool = dynamicTool({ description: 'Execute a tool loaded at runtime', inputSchema: z.object({}), execute: async input => { return runRuntimeTool(input); }, });

Supported schema types for tools

The AI SDK supports the following schemas for tool input definition: Zod v3 and v4 directly or via zodSchema(), Valibot via valibotSchema() from @ai-sdk/valibot, Standard JSON Schema compatible schemas, and raw JSON schemas via jsonSchema().

Tools work with generateText and streamText

Tools can be used with generateText and streamText by passing one or more tools to the tools parameter.

Tool results returned as tool result objects

If the LLM decides to use a tool and the tool has an execute function, it is run automatically when the call is generated. The output of the tool calls are returned using tool result objects.

Using ready-made tool packages example

Example of using a ready-made tool package: import { generateText, isStepCount } from 'ai'; import { searchTool } from 'some-tool-package'; const { text } = await generateText({ model: 'anthropic/claude-haiku-4.5', prompt: 'When was Vercel Ship AI?', tools: { webSearch: searchTool, }, stopWhen: isStepCount(10), });

Tool packages as npm distributions

Tools are JavaScript objects and can be packaged and distributed through npm like any other library. This makes it easy to share reusable tools across projects and with the community.

tools configuration in streamText

The streamText function accepts a tools property containing an object where each key is a tool name and each value is a tool defined with the tool function. When the model determines it needs to use a tool, it generates a tool call with the necessary input. The execute function is automatically run, and the tool output is added to the messages as a tool message.

Svelte quickstart example with weather tool

A complete example showing a weather tool integrated into streamText. The tool accepts a location parameter via Zod schema, has a description for the model, and an execute function that simulates fetching weather data and returns an object with location and temperature properties.

Multiple tools in streamText configuration

The tools parameter in streamText accepts an object where multiple tools can be defined with different names as keys. Each tool is created with the tool() function. The model can call any of these tools when appropriate for the conversation.

Zod schema required for tool inputSchema

Tool definitions require an inputSchema property defined using Zod schemas. The schema specifies required parameters and their types. Use z.object() to define parameters, z.string() for strings, z.number() for numbers, and .describe() to add descriptions for each field that help the model understand what input is needed.

Tool definition structure with tool function

Tools are defined using the tool function from 'ai' package. Each tool object has: description (string explaining when to use it), inputSchema (Zod schema defining required inputs), and execute (async function that runs on server and returns tool output).

streamText - tools parameter

The tools parameter in streamText is an object where keys are tool names and values are tool() function results. Multiple tools can be defined in a single tools object, allowing the model to choose which tool to invoke based on the conversation context.

tool function - inputSchema with Zod

The inputSchema property uses Zod for schema validation. The schema specifies required input fields and their types. Fields can have describe() calls to provide additional context to the model about what each input represents, helping the model extract the correct values from conversation context.

tool function - execute function behavior

The execute function is an async function that runs server-side when a tool is invoked. It receives the extracted input parameters and returns results that are automatically added to the messages as a tool message. This allows tools to fetch real data from external APIs or databases.

Tool message parts naming convention

Tool invocation parts in the message.parts array are named 'tool-{toolName}', where {toolName} is the key used when defining the tool in the tools object. For example, a tool defined as 'weather' produces parts with type 'tool-weather'.

Tool results in message flow

When a model invokes a tool, the tool's execute function runs server-side. The tool output is automatically added to the messages as a tool message, which is sent back to the model in subsequent generations. This creates a feedback loop where the model can use tool results to inform its responses.

Pitfall: Tool call without continuing generation

When an agent generates a tool call, it completes its generation at that point. The tool results are not automatically sent back to the agent for further processing. To enable the agent to use tool results in its response, you must configure stopWhen with isStepCount to allow additional generation steps after tool execution.

Accessing tool calls and results from streamText result

The result object returned by streamText has toolCalls and toolResults properties that can be accessed. These are awaited to retrieve arrays of tool calls made by the agent and their corresponding results from the execute functions.

Zod schema for tool input validation

Tool inputSchema is defined using Zod (z.object, z.string, z.number) to create type-safe schemas. Fields can have describe() method calls to provide descriptions that help the LLM understand what each parameter is for.

tools property in streamText configuration

The streamText function accepts a tools property in its configuration object. The tools property is an object where keys are tool names and values are tool definitions created with the tool() function. Tools allow the LLM to make function calls during generation.

Example: streamText with weather tool

import { ModelMessage, streamText, tool } from 'ai'; import { z } from 'zod'; const result = streamText({ model: __MODEL__, messages, tools: { weather: tool({ description: 'Get the weather in a location (fahrenheit)', inputSchema: z.object({ location: z .string() .describe('The location to get the weather for'), }), execute: async ({ location }) => { const temperature = Math.round(Math.random() * (90 - 32) + 32); return { location, temperature, }; }, }), }, }); This example demonstrates defining and using a simple tool that simulates fetching weather data.

Example: Multiple tool definitions in streamText

const result = streamText({ model: __MODEL__, messages, tools: { weather: tool({ description: 'Get the weather in a location (fahrenheit)', inputSchema: z.object({ location: z.string().describe('The location to get the weather for'), }), execute: async ({ location }) => { const temperature = Math.round(Math.random() * (90 - 32) + 32); return { location, temperature }; }, }), convertFahrenheitToCelsius: tool({ description: 'Convert a temperature in fahrenheit to celsius', inputSchema: z.object({ temperature: z.number().describe('The temperature in fahrenheit to convert'), }), execute: async ({ temperature }) => { const celsius = Math.round((temperature - 32) * (5 / 9)); return { celsius }; }, }), }, stopWhen: isStepCount(5), }); This example shows how multiple tools can be defined and used together, allowing the agent to chain tool calls across multiple steps.

tool function creates AI tool definitions

The tool function from the 'ai' package creates tool definitions for LLM tool calling. A tool definition includes: a description string explaining when to use the tool, an inputSchema defined with Zod that specifies required parameters, and an execute function that runs asynchronously on the server.

isStepCount function controls multi-step tool execution

The isStepCount function is used with the stopWhen option in streamText to control how many steps a model can execute. By default, stopWhen is set to isStepCount(1), which stops after the first step when there are tool results. Setting stopWhen to isStepCount(5) allows the model to execute up to 5 steps, enabling it to use tool results to trigger additional generations.

streamText accepts tools configuration option

The streamText function accepts a 'tools' option in its configuration object. The tools option is an object where keys are tool names and values are tool definitions created with the tool function.

Tool parts naming convention

Tool parts in message responses are always named with the pattern 'tool-{toolName}', where {toolName} is the key used when defining the tool in the tools object. For example, a tool defined as 'weather' creates parts with type 'tool-weather'.

Zod schema validation for tool inputs

Tool inputSchema is defined using Zod (z object) to specify required parameters. For example, z.object({ location: z.string().describe('...') }) defines a tool that requires a location string parameter. The describe method provides additional context to help the model understand what each parameter is for.

Tool execute function runs asynchronously on server

The execute function in a tool definition is an asynchronous function that runs on the server side. This allows it to call external APIs, query databases, or perform other server-side operations to retrieve real data.

TanStack Start quickstart example with weather tool

This example shows a complete streamText call with tools configuration: ```tsx const result = streamText({ model: __MODEL__, messages: await convertToModelMessages(messages), stopWhen: isStepCount(5), tools: { weather: tool({ description: 'Get the weather in a location (fahrenheit)', inputSchema: z.object({ location: z .string() .describe('The location to get the weather for'), }), execute: async ({ location }) => { const temperature = Math.round(Math.random() * (90 - 32) + 32); return { location, temperature, }; }, }), }, }); ```

Log tool results with onStepEnd callback

The `onStepEnd` callback fires after each LLM step and can be used to log tool results. Example: within `streamText`, use `onStepEnd: async ({ toolResults }) => { if (toolResults.length) { console.log(JSON.stringify(toolResults, null, 2)); } }`. This is useful for quick debugging without opening the DevTools UI.

streamText with tools and isStepCount example

Example showing tool definition and execution within streamText: ```ts import { streamText, tool, isStepCount } from 'ai'; import { z } from 'zod'; const result = streamText({ model, prompt: "What's the weather in New York in celsius?", tools: { weather: tool({ description: 'Get the weather in a location (fahrenheit)', inputSchema: z.object({ location: z.string().describe('The location to get the weather for'), }), execute: async ({ location }) => ({ location, temperature: Math.round(Math.random() * (90 - 32) + 32), }), }), }, stopWhen: isStepCount(5), onStepEnd: async ({ toolResults }) => { if (toolResults.length) { console.log(JSON.stringify(toolResults, null, 2)); } }, }); ``` This demonstrates tool definition with Zod schema, tool execution, stopping after a step count, and logging tool results.

Tool definition with tool() function

Tools are defined using the tool() function with a description, inputSchema (using Zod), and an async execute function. The execute function receives the parsed input and context object with tool context values. Example: tool({ description: 'Execute Python code', inputSchema: z.object({ code: z.string() }), execute: async ({ code }) => ({ output: 'result' }) })

experimental_sandbox parameter for tool execution environments

Pass experimental_sandbox when an agent tool needs a command or code execution environment. The experimental sandbox is a per-call value, provided to generate(), stream(), or the agent UI stream helper. Tools must explicitly delegate operations to the experimental sandbox by calling experimental_sandbox.run(). The experimental sandbox description is not added to the model prompt automatically; include it in the prompt or instructions when needed.

Tool contextSchema for passing server-side values

Tools can declare a contextSchema using Zod to receive server-side values such as credentials, scoped permissions, or default settings. These values are passed through the toolsContext parameter in agent.generate() or agent.stream() calls. The execute function receives the context via the second parameter's context property.

toolApproval configuration for requiring approval before tool execution

Configure approval on the ToolLoopAgent with the toolApproval option as an object mapping tool names to approval types such as 'user-approval'. When set, the agent requests approval before executing the specified tool.

toolChoice option controls agent tool usage

The toolChoice option controls how the agent uses tools. Valid values are: 'auto' (default, let the model decide), 'required' (force tool use), or 'none' (disable tools). It can also be an object { type: 'tool', toolName: 'toolName' } to force use of a specific tool.

Anthropic Memory Tool interface and commands

The Anthropic Memory Tool (memory_20250818) provides Claude with structured memory management. The execute function receives action objects containing command, path, and other fields depending on the command. Commands are: view, create, str_replace, insert, delete, rename. All paths are scoped to /memories. The execute function maps these commands to a storage backend (filesystem, database, or other persistence layer) and returns results as strings.

@ai-sdk/policy-opa package overview

@ai-sdk/policy-opa is a package that moves tool authorization rules from application code into Open Policy Agent (OPA) policies written in .rego files. It sits entirely on top of the public toolApproval callback and uses the same tool-approval-request / tool-approval-response flow as built-in approvals. The package requires one of two optional peer dependencies: @open-policy-agent/opa-wasm for in-process WASM evaluation or @open-policy-agent/opa for HTTP client to a running OPA server.

Policy decision types in OPA tool approval

OPA policy evaluation maps to four standard approval statuses: allow (runs the tool), deny (returns a denied result the model can reason about), requires-approval (pauses and waits for human approval), and not-applicable (normalizes to allow by default; use 'default decision := { "decision": "deny" }' to default-deny instead).

wasmPolicyClient configuration

wasmPolicyClient({ wasm, data? }) is an async function that loads a compiled OPA WASM bundle in-process. It takes required wasm bytes and optional data parameter. No network call occurs per decision. It is a good fit when shipping the policy with the app or fetching from object storage at startup. Hot-reload requires rebuilding the WASM and re-instantiating the client.

PolicyDecisionEvent structure

Each PolicyDecisionEvent carries: toolCall (the tool call information), decision (object with type and reason), enforced (whether the policy was being enforced), and effective (what the SDK actually acted on). The decision.type can be 'approved', 'denied', 'user-approval', or 'not-applicable'. Compare decision against effective to spot drift between policy intent and actual behavior.

OPA policy input shape for tool approval

The default OPA input shape for tool approval is { tool: { name }, args, messages, runtimeContext }. The input object carries messages (full model and tool-call history), allowing rules to factor in what already happened: prior tool calls, sequence of actions, or running totals. This shape can be overridden with the toInput option.

httpPolicyClient configuration

httpPolicyClient({ url, headers? }) is a sync function that creates a client against a running OPA server. It takes required url parameter and optional headers parameter for Styra DAS / EOPA authentication. One HTTP round-trip occurs per decision. It is a good fit when policies change frequently and hot-reload is wanted without redeploying, or when multiple services share one OPA.

opaPolicy function signature

opaPolicy({ client, path, toInput? }) returns a toolApproval configuration. It takes required client (PolicyClient instance), required path (string, the policy path to evaluate), and optional toInput (function to transform tool call and runtime context into the input shape the policy expects). It fails closed: errors return denied with the error message as reason, and never reject or abort the run.

Give your agent this brain