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 · Cookbook · all subjects

agents/tools

136 notes in this subject, read out of this brain and free to use. This is page 2 of 3.

Type-safe tool result handling

Tool results can be accessed with type safety by switching on `toolResult.toolName` and skipping dynamic results. This provides access to both `toolResult.input` and `toolResult.output` with correct types based on the tool schema.

Model doesn't summarize tool results by default

When using tools with generateText, the model treats the tool call itself as its response and does not automatically summarize the tool results. To get the model to process and respond to tool results, use `stopWhen` to trigger another generation pass that sends toolResults back to the model.

Multiple tools in generateText

Multiple tools can be passed to generateText as an object where keys are tool names and values are tool definitions. The model can call any of these tools during generation based on the prompt.

Tool definition with inputSchema for structured tool calls

Tools are defined using the tool function with three properties: description (string explaining what the tool does), inputSchema (a Zod schema object defining the parameters the tool accepts), and execute (a function that receives the validated inputs and performs the action). The inputSchema ensures type safety and validates inputs before passing them to execute.

Multi-step tool calls with stopWhen

In the AI SDK's generateText function, you can enable multi-step tool calls by defining stopping conditions with the stopWhen parameter. This allows you to define the conditions for which your agent should stop when the model generates a tool call.

isStepCount stopping condition

The isStepCount function is used with stopWhen to define a maximum number of steps before the agent should stop. For example, isStepCount(5) will stop the agent after 5 steps.

Multi-step tool calling example

This example shows how to implement multi-step tool calls with generateText: import { generateText, tool, isStepCount } from 'ai'; import { z } from 'zod'; const { text, steps } = await generateText({ model: 'openai/gpt-4.1', stopWhen: isStepCount(5), tools: { weather: 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 }: { location: string }) => ({ location, temperature: 72 + Math.floor(Math.random() * 21) - 10, }), }), }, prompt: 'What is the weather in San Francisco?', });

generateText returns text and steps

When using generateText with multi-step tool calls, the function returns an object containing both text and steps properties. The steps property contains information about all the steps taken during the multi-step process.

MCP client initialization with SSE transport

To connect to a Server-Sent Events (SSE) MCP server, use createMCPClient with a transport object containing type: 'sse' and url property. Like HTTP transport, headers and authProvider can be optionally configured.

MCP client initialization with stdio transport

To connect to a stdio MCP server locally, create an Experimental_StdioMCPTransport with command and args, then pass it to createMCPClient. The transport requires a command (e.g., 'node') and args (e.g., ['src/stdio/dist/server.js']) to specify the server process to launch.

MCP tools with multiple server connections

This example shows initializing three separate MCP clients (stdio, HTTP, and SSE) simultaneously, retrieving tools from each via client.tools(), merging them into a single tools object, and using all tools together with generateText. All clients must be closed in a finally block.

MCP client initialization with HTTP transport

To connect to an HTTP MCP server, use createMCPClient with a transport object containing type: 'http' and url property. Headers can be optionally configured via a headers property, and OAuth authorization can be provided via an authProvider property.

Using MCP tools with generateText

Pass MCP tools retrieved from client.tools() to the generateText function via the tools parameter. MCP tools work like standard AI SDK tools and can be used with isStepCount() to limit tool invocations.

MCP client resource cleanup

Always call close() on an MCP client after use to release resources. This should be done in a finally block to ensure cleanup occurs even if errors occur during tool usage.

Retrieving tools from MCP client

Call the tools() method on an initialized MCP client to retrieve its available tools as an object. Multiple tool sets can be merged together, but tools with the same name in subsequent sets will override earlier definitions.

Using official MCP SDK transports with AI SDK

The official Model Context Protocol TypeScript SDK provides alternative transport implementations: StdioClientTransport, SSEClientTransport, and StreamableHTTPClientTransport. These can be used instead of direct config by passing them to createMCPClient, and are available after installing @modelcontextprotocol/sdk.

Parallel tool execution in manual agent loop

To execute multiple tools in parallel for better performance in a manual agent loop, map over toolCalls to create an array of promises that each execute a tool. Use Promise.all() to wait for all tool executions to complete in parallel, then push all results to the messages array. Example: ```ts const toolPromises = toolCalls.map(async toolCall => { if (toolCall.toolName === 'getWeather') { const toolOutput = await getWeather(toolCall.input); return { role: 'tool' as const, content: [ { toolName: toolCall.toolName, toolCallId: toolCall.toolCallId, type: 'tool-result' as const, output: { type: 'text' as const, value: toolOutput }, }, ], }; } }); const toolResults = await Promise.all(toolPromises); messages.push(...toolResults.filter(Boolean)); ```

Tool execution control in manual loop

Tool execution in a manual loop is handled explicitly by checking the finish reason. When the finish reason is 'tool-calls', iterate through each tool call from `await result.toolCalls`, execute the appropriate tool based on toolName, and add the tool result to the messages array as a tool message with role 'tool', containing the toolName, toolCallId, type 'tool-result', and output.

Tool definition without execute function in manual loop

When defining tools for a manual agent loop, define the tool without an execute function. Include only the description and inputSchema properties. The execute function is omitted because tool execution is handled explicitly in the loop code.

Gemini search grounding with google_search tool

Compatible Gemini models can enable search grounding to access latest information using Google search. Use model 'google/gemini-2.5-flash' with generateText, include tools object with `google_search: google.tools.googleSearch({})`. The response returns text, sources, and providerMetadata.google containing groundingMetadata and safetyRatings.

Perplexity Sonar models for web search

Perplexity's Sonar models combine real-time web search with natural language processing. Each response is grounded in current web data and includes detailed citations. Use model 'perplexity/sonar-pro' with generateText, pass a prompt, and retrieve text and sources from the response.

ToolLoopAgent for OpenAI web search with tool introspection

Use ToolLoopAgent with OpenAI's web_search tool to extract query and sources from tool calls. The tool accepts a `searchContextSize` parameter (e.g., 'low'). Call agent.generate() with a prompt to get text, sources, and toolResults. Iterate toolResults checking for `toolResult.toolName === 'web_search'` to access toolResult.output which contains `action.query` and `sources`.

OpenAI web_search tool with generateText

OpenAI's Responses API provides a built-in web search tool called `web_search` via the `openai` provider. It returns `text` and `sources`. Example usage: import openai from '@ai-sdk/openai', call generateText with model 'openai/gpt-5-mini', include tools object with `web_search: openai.tools.webSearch({})`, and console.log the text and sources returned.

Exa webSearch ready-made tool installation and usage

Install Exa webSearch tool with `pnpm install @exalabs/ai-sdk`. Import webSearch from '@exalabs/ai-sdk' and pass it to generateText, streamText, or an agent in the tools object as `webSearch: webSearch()`. Use `stopWhen: isStepCount(3)` to control multi-step generation. Get API key from https://dashboard.exa.ai/api-keys. See Exa AI SDK documentation for more configuration options.

Two approaches to building web search agents

There are two approaches to building a web search agent with the AI SDK: use a model with native web-searching capabilities, or use a tool to access the web and return search results. Native search is faster with no additional cost but offers less control and is limited to supporting models. Tool-based search provides flexibility and greater control over search queries, customization of search strategy, and works with any LLM supporting tool calling, but incurs additional costs for the search API.

Web search tools require multi-step generation with stopWhen

Unlike native web search, using web search tools requires multiple steps: first generation to call the tool and extract search queries, second generation to process results and generate a response. Use `stopWhen: isStepCount(n)` with n greater than 1 to automatically send tool results back to the language model alongside the original question.

Parallel Web searchTool and extractTool ready-made tools

Install Parallel Web tools with `pnpm install @parallel-web/ai-sdk-tools`. Parallel Web provides two tools: `searchTool` for web search and `extractTool` for extracting web page content. Import both and pass to generateText, streamText, or agent as `webSearch: searchTool` and `webExtract: extractTool`. Use `stopWhen: isStepCount(3)`. Get API key from https://parallel.ai.

Perplexity Search ready-made tool installation and usage

Install Perplexity Search tool with `pnpm install @perplexity-ai/ai-sdk`. Import perplexitySearch and pass to generateText, streamText, or agent as `search: perplexitySearch()`. Perplexity Search provides real-time web search with advanced filtering options including domain, language, date range, and recency filters. Use `stopWhen: isStepCount(3)`. Get API key from https://www.perplexity.ai/account/api/keys. See Perplexity Search API documentation for more options.

Tavily tavilySearch and tavilyExtract ready-made tools

Install Tavily tools with `pnpm install @tavily/ai-sdk`. Import tavilySearch and tavilyExtract from '@tavily/ai-sdk' and pass to generateText, streamText, or agent as `webSearch: tavilySearch()` and `webExtract: tavilyExtract()`. Use `stopWhen: isStepCount(3)`. Get API key from https://app.tavily.com. See Tavily AI SDK Documentation for more customization options.

You.com youSearch, youResearch, youContents ready-made tools

Install You.com AI SDK plugin with `pnpm install @youdotcom-oss/ai-sdk-plugin`. You.com provides three tools: `youSearch` for real-time web search with advanced filtering, `youResearch` for deep research with cited sources and configurable effort (lite to exhaustive), and `youContents` for extracting webpage content in markdown or HTML. Pass to generateText, streamText, or agent. Use `stopWhen: isStepCount(5)`. Get API key from https://you.com/platform/api-keys. See You.com AI SDK Plugin documentation on GitHub for more configuration options.

Custom Exa web search tool implementation

To build a custom Exa search tool: install `exa-js` with `pnpm install exa-js`. Create a tool using the `tool()` function with description and inputSchema (query as string, min 1, max 100 chars). In execute, call `exa.searchAndContents(query, { livecrawl: 'always', numResults: 3 })` and return array of results with title, url, content (first 1000 chars), and publishedDate. Pass to generateText with `stopWhen: isStepCount(5)`. Works with any model supporting tools.

Custom Firecrawl web scraping tool implementation

To build a custom Firecrawl scraping tool: install `@mendable/firecrawl-js` with `pnpm install @mendable/firecrawl-js`. Create a FirecrawlApp with apiKey from environment. Use `tool()` function with inputSchema containing `urlToCrawl` (string, URL format, min 1, max 100 chars). In execute, call `app.crawlUrl(urlToCrawl, { limit: 1, scrapeOptions: { formats: ['markdown', 'html'] } })`. Check crawlResponse.success and return crawlResponse.data, or throw error with crawlResponse.error. Pass to generateText with `stopWhen: isStepCount(5)`. Works with any model supporting tools.

Tools parameter in generateText

The tools parameter in generateText accepts an object where each key is a tool name and the value is an object with description, inputSchema, and execute properties. description is a string explaining what the tool does. inputSchema is a Zod schema object that defines the parameters the tool accepts. execute is an async function that takes the parsed parameters and returns the tool result.

Tool definition with Zod schema

Tools are defined with a name, description, inputSchema using Zod z.object(), and an execute async function. The inputSchema specifies each parameter as a Zod field with type and describe() method. For example, z.string().describe('The value in celsius') creates a string parameter with documentation.

generateText returns text and toolResults

The generateText function returns an object with text and toolResults properties. text contains the generated text response, and toolResults is an array of tool execution results that can be mapped to extract result values.

Tool result handling in assistant response

The assistant response content can be generated from either the text property or by mapping toolResults and joining the result values with newlines: text || toolResults.map(toolResult => toolResult.result).join('\n')

Parallel tool calling support

Some language models support calling tools in parallel. This is particularly useful when multiple tools are independent of each other and can be executed in parallel during the same generation step.

Processing tool results from parallel calls

After generateText completes with parallel tool calls, the response contains both text and toolResults. The assistant message can be constructed using either the generated text (if available) or by joining the results from all tool calls with newlines. This allows handling cases where the model generates accompanying text or only tool results.

React Server Component client for tool calling

The client component uses useState to manage conversation history and input state. It calls the continueConversation server action with the updated message history including the user's input, then updates the conversation state with the response. The maxDuration export is set to 30 seconds to allow streaming responses.

Parallel tool calling with generateText

The generateText function from the AI SDK supports parallel tool calling. In the server action, define tools as an object where each tool has a description, inputSchema (using Zod), and an execute function. The model can call multiple tools in parallel during the same generation step. The response includes both text and toolResults, which can be combined for the assistant message.

Tool definition structure with Zod schema

Each tool in the tools object requires: description (string describing what the tool does), inputSchema (a Zod object schema defining the tool's parameters with descriptions), and execute (an async function that receives the parsed parameters and returns the result). Parameters in the schema should use describe() to document their purpose.

Client application is responsible for proper elicitation request handling

It is up to the client application to handle elicitation requests properly. The MCP client simply surfaces these requests from the server to your application code. The client must implement the logic to collect user input according to the schema.

MCP elicitation enables servers to request additional information from clients

Elicitation is a mechanism where MCP servers can request additional information from the client during tool execution. This allows servers to collect user input like registration information during a tool call.

Create MCP client with elicitation capability enabled

To enable elicitation handling, pass a capabilities object with an elicitation property when creating the MCP client: capabilities: { elicitation: {} }. This tells the client to accept and surface elicitation requests from the server.

Register elicitation request handler with mcpClient.onElicitationRequest()

Use mcpClient.onElicitationRequest(ElicitationRequestSchema, async request => { ... }) to register a handler that will be called when the MCP server requests additional information. The handler receives request.params.message and request.params.requestedSchema.

Elicitation handler must return action and content

The elicitation handler must return an object with an action field set to one of: 'accept' (user provided the requested information, must include content with the data), 'decline' (user chose not to provide the information), or 'cancel' (user cancelled the operation entirely).

MCP elicitation example with user registration

import { createMCPClient, ElicitationRequestSchema } from '@ai-sdk/mcp'; import { generateText } from 'ai'; const mcpClient = await createMCPClient({ transport: { type: 'sse', url: 'http://localhost:8083/sse', }, capabilities: { elicitation: {}, }, }); mcpClient.onElicitationRequest(ElicitationRequestSchema, async request => { console.log('Server is requesting:', request.params.message); console.log('Expected schema:', request.params.requestedSchema); const userData = await promptUserForInput(request.params.requestedSchema); return { action: 'accept', content: userData, }; }); try { const tools = await mcpClient.tools(); const { text } = await generateText({ model: 'openai/gpt-4o-mini', tools, prompt: 'Register a new user account', }); console.log('Response:', text); } finally { await mcpClient.close(); } async function promptUserForInput( schema: unknown, ): Promise<Record<string, unknown>> { return { username: 'johndoe', email: 'john@example.com', password: 'sec••••••23', newsletter: true, }; }

Elicitation handler responsibilities

The elicitation handler should: (1) parse request.params.requestedSchema to understand what data the server needs, (2) implement appropriate user input collection such as CLI prompt, web form, GUI dialog, or other input mechanism, (3) validate the input matches the requested schema, (4) return the appropriate action and content.

Handle tool-error type in stream iteration

When iterating through the stream from streamText, check for chunk.type === 'tool-error' to handle errors that occur with tools. Access the error via part.error.

ActiveTools type definition

ActiveTools<TOOLS extends ToolSet> is defined as ReadonlyArray<keyof TOOLS & string> | undefined. It limits a generation step to the listed tool names. undefined means no tool restriction is applied.

generateText toolApproval parameter

The toolApproval parameter is optional and accepts ToolApprovalConfiguration. It enables approval workflows for tool calls with options: 'not-applicable' (default, runs tool without approval metadata), 'approved', 'denied', or their object forms with reason. Can use GenericToolApprovalFunction (callback for all tool calls) or per-tool SingleToolApprovalFunction receiving tool input, toolCallId, messages, toolContext, and runtimeContext.

generateText experimental_toolCallers parameter

The experimental_toolCallers parameter is optional and configures which caller tools may invoke each tool. The callback receives typed references for caller-capable tools and returns an object keyed by callee tool name. Include 'direct' to keep a tool directly callable by the model. Local-only callees are hidden from direct model calls.

generateText experimental_refineToolInput parameter

The experimental_refineToolInput parameter is optional and accepts ToolInputRefinement<TOOLS>, a mapping of tool names to functions that refine parsed tool inputs. Each function receives typed input and must return the same input type shape. The refined input is used for tool execution, output parts, lifecycle callbacks, and telemetry.

generateText toolsContext parameter

The toolsContext parameter accepts a per-tool context map keyed by tool name (InferToolSetContext<TOOLS>). It is required when at least one tool defines contextSchema and not accepted when no tools need context.

generateText repairToolCall parameter

The repairToolCall parameter is optional and accepts a function (options: ToolCallRepairOptions) => Promise<LanguageModelV4ToolCall | null>. It attempts to repair a tool call that failed to parse, returning either a repaired tool call or null if unrepairable.

ToolCallRepairOptions structure

ToolCallRepairOptions contains: instructions (Instructions|undefined), system (Instructions|undefined, deprecated use instructions), messages (ModelMessage[]), toolCall (LanguageModelV4ToolCall that failed to parse), tools (TOOLS available), inputSchema (function returning JSONSchema7 for a tool by toolName), and error (NoSuchToolError|InvalidToolInputError that occurred).

onToolExecutionStart callback event structure

The onToolExecutionStart callback receives a ToolExecutionStartEvent with fields: callId (string, unique identifier for the generation call), toolCall (TypedToolCall<TOOLS> with toolName, toolCallId, input, and metadata), messages (Array<ModelMessage> sent to the model, excluding system prompt and assistant response), and toolContext (InferToolContext<TOOLS[toolName]> narrowed to the specific tool's context type). Errors thrown in this callback are silently caught and do not break the generation flow.

onToolExecutionEnd callback event structure

The onToolExecutionEnd callback receives a ToolExecutionEndEvent with fields: callId (string), toolCall (TypedToolCall<TOOLS>), toolExecutionMs (number, wall-clock duration in milliseconds), messages (Array<ModelMessage>), toolContext (InferToolContext<TOOLS[toolName]>), and toolOutput (ToolOutput<TOOLS> discriminated union where type 'tool-result' has output field, and type 'tool-error' has error field). Errors thrown in this callback are silently caught.

Deprecated tool callback aliases

experimental_onToolCallStart and experimental_onToolCallFinish are deprecated aliases. Use onToolExecutionStart and onToolExecutionEnd instead. The deprecated aliases are only used as fallback when the current callbacks are not provided.

ToolExecutionOptions structure

ToolExecutionOptions has: toolCallId (string), messages (ModelMessage[] - messages sent to language model for the response containing the tool call, excluding system prompt and assistant response), and abortSignal (optional AbortSignal for operation cancellation).

Give your agent this brain