Skip to content
Dashboard

Introducing agents, tool execution approval, DevTools, full MCP support, reranking, image editing, and more.

We’ve gone all in on the AI SDK. Its agentic capabilities and TypeScript-first design power our AI web research agent (Claygent) at massive scale. It's been a huge help as we build agents for sourcing, qualification, and surfacing the right accounts and prospects for our customers.
Jeff Barg Clay

Upgrading from AI SDK 5? Run npx @ai-sdk/codemod v6 to migrate automatically with minimal code changes.

Link to headingAgents

Link to headingToolLoopAgent

import { ToolLoopAgent } from 'ai';
import { weatherTool } from '@/tools/weather';
export const weatherAgent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
instructions: 'You are a helpful weather assistant.',
tools: {
weather: weatherTool,
},
});
const result = await weatherAgent.generate({
prompt: 'What is the weather in San Francisco?',
});

Link to headingCall Options

import { ToolLoopAgent } from "ai";
import { z } from "zod";
const supportAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
callOptionsSchema: z.object({
userId: z.string(),
accountType: z.enum(["free", "pro", "enterprise"]),
}),
prepareCall: ({ options, ...settings }) => ({
...settings,
instructions: `You are a helpful customer support agent.
- User Account type: ${options.accountType}
- User ID: ${options.userId}`,
}),
});
const result = await supportAgent.generate({
prompt: "How do I upgrade my account?",
options: {
userId: "user_123",
accountType: "free",
},
});

Link to headingCode Organization & UI Integration

// agents/weather-agent.ts
import { ToolLoopAgent, InferAgentUIMessage } from "ai";
import { weatherTool } from "@/tools/weather-tool";
export const weatherAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: "You are a helpful weather assistant.",
tools: { weather: weatherTool },
});
export type WeatherAgentUIMessage = InferAgentUIMessage<typeof weatherAgent>;
// app/api/chat/route.ts
import { createAgentUIStreamResponse } from "ai";
import { weatherAgent } from "@/agents/weather-agent";
export async function POST(request: Request) {
const { messages } = await request.json();
return createAgentUIStreamResponse({
agent: weatherAgent,
uiMessages: messages,
});
}

// app/page.tsx
import { useChat } from '@ai-sdk/react';
import type { WeatherAgentUIMessage } from '@/agents/weather-agent';
import { WeatherToolView } from '@/components/weather-tool-view';
export default function Chat() {
const { messages, sendMessage } = useChat<WeatherAgentUIMessage>();
return (
<div>
{messages.map((message) =>
message.parts.map((part) => {
switch (part.type) {
case 'tool-weather':
return <WeatherToolView invocation={part} />;
}
})
)}
</div>
);
}
// components/weather-tool-view.tsx
import { UIToolInvocation } from 'ai';
import { weatherTool } from '@/tools/weather-tool';
export function WeatherToolView({
invocation,
}: {
invocation: UIToolInvocation<typeof weatherTool>;
}) {
return (
<div>
Weather in {invocation.input.location} is {invocation.output?.temperature}°F
</div>
);
}

Link to headingCustom Agent Implementations

import { getWritable } from 'workflow';
import { DurableAgent } from '@workflow/ai/agent';
import { searchFlights, bookFlight, getFlightStatus } from './tools';
export async function flightBookingWorkflow() {
'use workflow';
const flightAgent = new DurableAgent({
model: 'anthropic/claude-sonnet-4.5',
system: 'You are a flight booking assistant.',
tools: {
searchFlights,
bookFlight,
getFlightStatus,
},
});
const result = await flightAgent.generate({
prompt: 'Find me a flight from NYC to London next Friday.',
writable: getWritable(),
});
}

Link to headingTool Improvements

Link to headingTool Execution Approval

import { tool } from 'ai';
import { z } from 'zod';
export const runCommand = tool({
description: 'Run a shell command',
inputSchema: z.object({
command: z.string().describe('The shell command to execute'),
}),
needsApproval: true, // Require user approval
execute: async ({ command }) => {
// Your command execution logic here
},
});

import { tool } from "ai";
import { z } from "zod";
const runCommand = tool({
description: "Run a shell command",
inputSchema: z.object({
command: z.string().describe("The shell command to execute"),
}),
needsApproval: async ({ command }) => command.includes("rm -rf"),
execute: async ({ command }) => {
/* command execution logic */
},
});

import { ChatAddToolApproveResponseFunction } from 'ai';
import { runCommand } from './tools/command-tool';
export function CommandToolView({
invocation,
addToolApprovalResponse,
}: {
invocation: UIToolInvocation<typeof runCommand>;
addToolApprovalResponse: ChatAddToolApproveResponseFunction;
}) {
if (invocation.state === 'approval-requested') {
return (
<div>
<p>Run command: {invocation.input.command}?</p>
<button
onClick={() =>
addToolApprovalResponse({
id: invocation.approval.id,
approved: true,
})
}
>
Approve
</button>
<button
onClick={() =>
addToolApprovalResponse({
id: invocation.approval.id,
approved: false,
})
}
>
Deny
</button>
</div>
);
}
if (invocation.state === 'output-available') {
return <div>Output: {invocation.output}</div>;
}
// Handle other states...
}

Link to headingStrict Mode

tool({
description: 'Get the weather in a location',
inputSchema: z.object({
location: z.string(),
}),
strict: true, // Enable strict validation for this tool
execute: async ({ location }) => ({
// ...
}),
});

Link to headingInput Examples

tool({
description: 'Get the weather in a location',
inputSchema: z.object({
location: z.string().describe('The location to get the weather for'),
}),
inputExamples: [
{ input: { location: 'San Francisco' } },
{ input: { location: 'London' } },
],
execute: async ({ location }) => {
// ...
},
});

Link to headingSend Custom Tool Output to the Model

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: ({ location }) => ({
temperature: 72 + Math.floor(Math.random() * 21) - 10,
}),
// toModelOutput can be sync or async
toModelOutput: async ({ input, output, toolCallId }) => {
// many other options, including json, multi-part with files and images, etc.
// (support depends on provider)
// example: send tool output as a text
return {
type: "text",
value:
`The weather in ${input.location} is ${output.temperature}°F.`,
};
},
});

Link to headingMCP

Link to headingHTTP Transport

import { createMCPClient } from '@ai-sdk/mcp';
const mcpClient = await createMCPClient({
transport: {
type: 'http',
url: '<https://your-server.com/mcp>',
headers: { Authorization: 'Bearer my-api-key' },
},
});
const tools = await mcpClient.tools();

Link to headingOAuth Authentication

import { createMCPClient, auth, OAuthClientProvider } from "@ai-sdk/mcp";
const authProvider: OAuthClientProvider = {
redirectUrl: "http://localhost:3000/callback",
clientMetadata: {
client_name: "My App",
redirect_uris: ["http://localhost:3000/callback"],
grant_types: ["authorization_code", "refresh_token"],
},
// Token and credential storage methods
tokens: async () => { /* ... */ },
saveTokens: async (tokens) => { /* ... */ },
// ... remaining OAuthClientProvider configuration
};
await auth(authProvider, { serverUrl: new URL("https://mcp.example.com") });
const client = await createMCPClient({
transport: { type: "http", url: "https://mcp.example.com", authProvider },
});

Link to headingResources and Prompts

// List and read resources
const resources = await mcpClient.listResources();
const resourceData = await mcpClient.readResource({
uri: "file:///example/document.txt",
});
// List and get prompts
const prompts = await mcpClient.experimental_listPrompts();
const prompt = await mcpClient.experimental_getPrompt({
name: "code_review",
arguments: { code: "function add(a, b) { return a + b; }" },
});

Link to headingElicitation Support

const mcpClient = await createMCPClient({
transport: { type: 'sse', url: '<https://your-server.com/sse>' },
capabilities: { elicitation: {} },
});
mcpClient.onElicitationRequest(ElicitationRequestSchema, async request => {
const userInput = await getInputFromUser(
request.params.message,
request.params.requestedSchema,
);
return {
action: 'accept',
content: userInput,
};
});

Link to headingTool Calling with Structured Output

import { Output, ToolLoopAgent, tool } from "ai";
import { z } from "zod";
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: {
weather: tool({
description: "Get the weather in a location",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => {
// ...
},
}),
},
output: Output.object({
schema: z.object({
summary: z.string(),
temperature: z.number(),
recommendation: z.string(),
}),
}),
});
const { output } = await agent.generate({
prompt: "What is the weather in San Francisco and what should I wear?",
});

Link to headingOutput Types

Link to headingDevTools

Link to headingSetup

import { wrapLanguageModel, gateway } from 'ai';
import { devToolsMiddleware } from '@ai-sdk/devtools';
const devToolsEnabledModel = wrapLanguageModel({
model: gateway('anthropic/claude-sonnet-4.5'),
middleware: devToolsMiddleware(),
});

import { generateText } from 'ai';
const result = await generateText({
model: devToolsEnabledModel,
prompt: 'What is love?',
});

Link to headingInspecting Your Runs

Link to headingReranking

import { rerank } from 'ai';
import { cohere } from '@ai-sdk/cohere';
const documents = [
'sunny day at the beach',
'rainy afternoon in the city',
'snowy night in the mountains',
];
const { ranking } = await rerank({
model: cohere.reranking('rerank-v3.5'),
documents,
query: 'talk about rain',
topN: 2,
});
console.log(ranking);
// [
// { originalIndex: 1, score: 0.9, document: 'rainy afternoon in the city' },
// { originalIndex: 0, score: 0.3, document: 'sunny day at the beach' }
// ]

Link to headingStructured Document Reranking

import { rerank } from 'ai';
import { cohere } from '@ai-sdk/cohere';
const documents = [
{ from: 'Paul Doe', subject: 'Follow-up', text: '20% discount offer...' },
{
from: 'John McGill',
subject: 'Missing Info',
text: 'Oracle pricing: $5000/month',
},
];
const { rerankedDocuments } = await rerank({
model: cohere.reranking('rerank-v3.5'),
documents,
query: 'Which pricing did we get from Oracle?',
topN: 1,
});

Link to headingStandard JSON Schema

import { generateText, Output } from 'ai';
import { type } from 'arktype';
const result = await generateText({
model: 'anthropic/claude-sonnet-4.5',
output: Output.object({
schema: type({
recipe: {
name: 'string',
ingredients: type({ name: 'string', amount: 'string' }).array(),
steps: 'string[]',
},
}),
}),
prompt: 'Generate a lasagna recipe.',
});

Link to headingProvider Tools

Link to headingAnthropic Provider Tools

import { anthropic } from "@ai-sdk/anthropic";
// Memory Tool - store and retrieve information
const memory = anthropic.tools.memory_20250818({
execute: async (action) => {
// Implement memory storage logic
// Supports: view, create, str_replace, insert, delete, rename
},
});
// Tool Search (Regex) - find tools by pattern
const toolSearchRegex = anthropic.tools.toolSearchRegex_20251119();
// Tool Search (BM25) - find tools with natural language
const toolSearchBm25 = anthropic.tools.toolSearchBm25_20251119();
// Code Execution Tool - run code in sandbox
const codeExecution = anthropic.tools.codeExecution_20250825();

import {
anthropic,
forwardAnthropicContainerIdFromLastStep,
} from "@ai-sdk/anthropic";
const getWeather = tool({
description: "Get weather for a city.",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ temp: 22 }),
providerOptions: {
anthropic: { allowedCallers: ["code_execution_20250825"] },
},
});
const result = await generateText({
model: anthropic("claude-sonnet-4-5"),
tools: {
code_execution: anthropic.tools.codeExecution_20250825(),
getWeather,
},
prepareStep: forwardAnthropicContainerIdFromLastStep,
});

Link to headingOpenAI Provider Tools

import { openai } from "@ai-sdk/openai";
// Shell Tool - execute shell commands
const shell = openai.tools.shell({
execute: async ({ action }) => {
// action.commands: string[] - commands to execute
// action.timeoutMs: optional timeout
// action.maxOutputLength: optional max chars to return
},
});
// Apply Patch Tool - file operations with diffs
const applyPatch = openai.tools.applyPatch({
execute: async ({ callId, operation }) => {
// operation.type: 'create_file' | 'update_file' | 'delete_file'
// operation.path: file path
// operation.diff: diff content (for create/update)
},
});
// MCP Tool - connect to MCP servers
const mcp = openai.tools.mcp({
serverLabel: "my-mcp-server",
serverUrl: "[https://mcp.example.com](https://mcp.example.com/)",
allowedTools: ["tool1", "tool2"],
});

Link to headingGoogle Provider Tools

import { google } from "@ai-sdk/google";
// Google Maps Tool - location-aware grounding
const googleMaps = google.tools.googleMaps();
// Vertex RAG Store Tool - retrieve from RAG corpora
const vertexRagStore = google.tools.vertexRagStore({
ragCorpus: "projects/{project}/locations/{location}/ragCorpora/{rag_corpus}",
topK: 5, // optional: number of contexts to retrieve
});
// File Search Tool - search in file stores
const fileSearch = google.tools.fileSearch({
fileSearchStoreNames: ["fileSearchStores/my-store-123"],
topK: 10, // optional: number of chunks to retrieve
metadataFilter: "author=John Doe", // optional: AIP-160 filter
});

Link to headingxAI Provider Tools

import { xai } from "@ai-sdk/xai";
// Web Search Tool - search the web
const webSearch = xai.tools.webSearch({
allowedDomains: [
"[wikipedia.org](http://wikipedia.org/)",
"[github.com](http://github.com/)",
], // optional: max 5
excludedDomains: ["[example.com](http://example.com/)"], // optional: max 5
enableImageUnderstanding: true, // optional
});
// X Search Tool - search X posts
const xSearch = xai.tools.xSearch({
allowedXHandles: ["elonmusk", "xai"], // optional: max 10
fromDate: "2025-01-01", // optional
toDate: "2025-12-31", // optional
enableImageUnderstanding: true, // optional
enableVideoUnderstanding: true, // optional
});
// Code Execution Tool - run code
const codeExecution = xai.tools.codeExecution();
// View Image Tool - analyze images
const viewImage = xai.tools.viewImage();
// View X Video Tool - analyze X videos
const viewXVideo = xai.tools.viewXVideo();

Link to headingImage Editing

import { generateImage } from "ai";
import { blackForestLabs } from "@ai-sdk/black-forest-labs";
const { images } = await generateImage({
model: blackForestLabs.image("flux-2-pro"),
prompt: {
text: "Edit this to make it two tanukis on a date",
images: ["https://www.example.com/tanuki.png"],
},
});

"Edit this to make it two tanukis on a date"
"Edit this to make it two tanukis on a date"

Note: experimental_generateImage has been promoted to stable and renamed to generateImage.

Link to headingRaw Finish Reason & Extended Usage

Link to headingRaw Finish Reason

const { finishReason, rawFinishReason } = await generateText({
model: 'anthropic/claude-sonnet-4.5',
prompt: 'What is love?',
});
// finishReason: 'other' (mapped)
// rawFinishReason: 'end_turn' (provider-specific)

Link to headingExtended Usage Information

const { usage } = await generateText({
model: 'anthropic/claude-sonnet-4.5',
prompt: 'What is love?',
});
// Input token details
usage.inputTokenDetails.noCacheTokens; // Non-cached input tokens
usage.inputTokenDetails.cacheReadTokens; // Tokens read from cache
usage.inputTokenDetails.cacheWriteTokens; // Tokens written to cache
// Output token details
usage.outputTokenDetails.textTokens; // Text generation tokens
usage.outputTokenDetails.reasoningTokens; // Reasoning tokens (where supported)
// Raw provider usage
usage.raw; // Complete provider-specific usage object

Link to headingLangChain Adapter Rewrite

import { toBaseMessages, toUIMessageStream } from '@ai-sdk/langchain';
import { createUIMessageStreamResponse } from 'ai';
const langchainMessages = await toBaseMessages(messages);
const stream = await graph.stream({ messages: langchainMessages });
return createUIMessageStreamResponse({
stream: toUIMessageStream(stream),
});

Link to headingMigrating to AI SDK 6

npx @ai-sdk/codemod upgrade v6

Link to headingGetting started

I’m super hyped for v6. The move from streamText to composable agents is tasteful, and so are the new APIs around type-safety, MCP, and agent preparation. The amount of care the team has put into API design is wild.
Josh Upstash

Link to headingContributors