Vercel AI SDK 6: The Agent Abstraction That Unifies TypeScript AI Development
Quick answer (AEO): Vercel AI SDK 6 introduces a first-class Agent abstraction — define an agent with model, instructions, and tools, then reuse it across your application. The SDK provides a unified interface for streaming, generation, tool calling, and structured outputs across 15+ model providers. Key additions: MCP support, human-in-the-loop tool approval, AI Gateway integration, and a built-in agent loop. Breaking changes from v5 include the message-parts model, tool-call streaming lifecycle, and provider-adapter contracts.
Why an Agent abstraction matters
Before AI SDK 6, building an AI agent in TypeScript meant orchestrating primitives manually: generate text, parse tool calls, execute tools, feed results back, check termination conditions, handle errors, manage streaming. Every team built slightly different agent loops.
AI SDK 6 encapsulates this into a single, composable abstraction:
import { Agent, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
// Define once, reuse everywhere
const researchAgent = new Agent({
model: anthropic('claude-sonnet-4-6'),
instructions: `You are a research assistant. Search for information,
synthesize findings, and provide cited answers.`,
tools: {
search: tool({
description: 'Search the web for information',
parameters: z.object({
query: z.string().describe('Search query'),
}),
execute: async ({ query }) => {
return await searchService.query(query);
},
}),
cite: tool({
description: 'Add a citation to the response',
parameters: z.object({
url: z.string().url(),
title: z.string(),
relevance: z.string(),
}),
execute: async ({ url, title, relevance }) => {
return { url, title, relevance };
},
}),
},
// Built-in agent loop — continues until task complete
maxSteps: 10,
});
// Use the agent — streaming, tool execution, and loop management handled
const result = await researchAgent.run({
messages: [{ role: 'user', content: 'What is the current state of WebGPU adoption?' }],
}); The agent manages its own tool-execution loop: call model → parse tool calls → execute tools → feed results back → repeat until done. No manual orchestration.
The message-parts model (breaking change)
AI SDK 6 replaces the simple { role, content } message format with a parts-based model. This is the most impactful breaking change from v5:
// v5: Simple string content
const message = {
role: 'assistant',
content: 'Here is the answer...',
};
// v6: Multi-part messages
const message = {
role: 'assistant',
parts: [
{ type: 'text', text: 'Let me search for that...' },
{ type: 'tool-call', toolName: 'search', args: { query: 'WebGPU adoption 2026' } },
{ type: 'tool-result', toolName: 'search', result: { /* ... */ } },
{ type: 'text', text: 'Based on my research, here is what I found:' },
],
}; Why this matters: Real agent interactions aren’t linear text. They interleave reasoning, tool calls, tool results, and final answers. The parts model makes this explicit in the data structure, enabling:
- Granular streaming — stream each part independently.
- UI rendering — show tool calls in-progress, results as they arrive, text as it generates.
- Replay and debugging — inspect exactly what happened at each step.
useChat with message-parts
The useChat React hook now natively supports parts:
import { useChat } from 'ai/react';
function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <p key={i}>{part.text}</p>;
case 'tool-call':
return (
<ToolCallIndicator
key={i}
tool={part.toolName}
args={part.args}
status={part.status} // 'pending' | 'running' | 'complete'
/>
);
case 'tool-result':
return <ToolResult key={i} data={part.result} />;
}
})}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
</div>
);
} streamText: the server primitive
streamText replaces StreamingTextResponse as the primary server-side streaming interface:
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
export async function POST(request: Request) {
const { messages } = await request.json();
const result = streamText({
model: openai('gpt-4o'),
messages,
tools: { /* ... */ },
// New: callback for each step in the agent loop
onStepFinish: ({ stepType, text, toolCalls, toolResults }) => {
console.log(`Step: ${stepType}`, { text, toolCalls, toolResults });
},
});
// Returns a streaming Response — works with any framework
return result.toDataStreamResponse();
} The streamText primitive handles:
- Token-level streaming to the client.
- Tool call detection and execution mid-stream.
- Multi-step agent loops with automatic continuation.
- Structured output validation with Zod schemas.
Tool calls with Zod validation
Every tool in AI SDK 6 uses Zod schemas for parameter validation. This isn’t just type safety — it’s runtime validation of what the model generates:
import { tool } from 'ai';
import { z } from 'zod';
const createIssue = tool({
description: 'Create a GitHub issue',
parameters: z.object({
title: z.string().min(1).max(256),
body: z.string().max(65536),
labels: z.array(z.string()).max(10).optional(),
assignees: z.array(z.string()).max(10).optional(),
milestone: z.number().int().positive().optional(),
}),
// Only executes if Zod validation passes
execute: async (params) => {
const issue = await octokit.rest.issues.create({
owner: 'org',
repo: 'repo',
...params,
});
return { id: issue.data.id, url: issue.data.html_url };
},
}); If the model generates invalid parameters (wrong type, out of range, missing required field), the SDK catches it before execution and can either retry or surface the error.
Provider adapters: 15+ models, one interface
AI SDK 6 ships adapters for 15+ model providers with a consistent interface:
import { anthropic } from '@ai-sdk/anthropic';
import { openai } from '@ai-sdk/openai';
import { google } from '@ai-sdk/google';
import { mistral } from '@ai-sdk/mistral';
// Same agent definition, swap the model
const agent = new Agent({
model: anthropic('claude-sonnet-4-6'), // or:
// model: openai('gpt-4o'),
// model: google('gemini-2.5-pro'),
// model: mistral('mistral-large-latest'),
instructions: '...',
tools: { /* ... */ },
}); AI Gateway integration
For routing across providers with a single string:
import { gateway } from '@ai-sdk/gateway';
// Route through AI Gateway — handles failover, rate limiting, caching
const agent = new Agent({
model: gateway('anthropic/claude-sonnet-4.6'),
// Automatically falls back to alternatives on failure
instructions: '...',
tools: { /* ... */ },
}); The gateway string format (provider/model) means you can switch models by changing a config value — no code changes, no adapter swaps.
Human-in-the-loop tool approval
For agents that can take consequential actions, AI SDK 6 supports approval workflows:
const deployAgent = new Agent({
model: anthropic('claude-sonnet-4-6'),
tools: {
readLogs: tool({
description: 'Read application logs',
parameters: z.object({ service: z.string(), lines: z.number() }),
execute: async ({ service, lines }) => readLogs(service, lines),
}),
deploy: tool({
description: 'Deploy a service to production',
parameters: z.object({
service: z.string(),
version: z.string(),
environment: z.enum(['staging', 'production']),
}),
// Requires human approval before execution
experimental_requireConfirmation: true,
execute: async ({ service, version, environment }) => {
return await deployService(service, version, environment);
},
}),
},
}); When the agent calls deploy, execution pauses until a human approves or rejects. The streaming response includes approval-pending events that your UI can render.
MCP support: connecting to external tool servers
Model Context Protocol (MCP) support means agents can connect to external tool servers:
import { Agent } from 'ai';
import { mcpClient } from 'ai/mcp';
// Connect to an MCP server that provides tools
const githubTools = await mcpClient({
transport: 'stdio',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-github'],
});
const agent = new Agent({
model: anthropic('claude-sonnet-4-6'),
tools: {
...githubTools, // All tools from the MCP server
...localTools, // Your custom tools
},
}); MCP tools are discovered dynamically — the agent receives tool descriptions from the server at runtime, enabling plugin architectures where tool capabilities grow without agent code changes.
ToolLoopAgent: autonomous agent loops
For fully autonomous agents that run until a task is complete:
import { ToolLoopAgent } from 'ai';
const codeReviewAgent = new ToolLoopAgent({
model: anthropic('claude-sonnet-4-6'),
instructions: `Review the pull request. Check for bugs, security issues,
and performance problems. Provide actionable feedback.`,
tools: { readFile, listFiles, searchCode, createReviewComment },
// Agent decides when it's done
terminationCondition: 'model-decides',
// Safety: maximum iterations
maxIterations: 25,
// Callback per iteration for monitoring
onIteration: ({ iteration, toolCalls }) => {
metrics.trackAgentStep(iteration, toolCalls);
},
});
const review = await codeReviewAgent.run({
input: `Review PR #482: "Add rate limiting to API endpoints"`,
}); Breaking changes from v5: migration guide
1. Message-parts model
// v5
const msg = { role: 'assistant', content: 'Hello' };
// v6
const msg = { role: 'assistant', parts: [{ type: 'text', text: 'Hello' }] }; 2. Tool-call streaming lifecycle
Tool calls now stream their status: pending → running → complete | error. UI code that renders tool calls needs to handle these states.
3. Provider-adapter contracts
Custom provider adapters must implement the new LanguageModelV2 interface (renamed from LanguageModelV1).
4. Streaming wire format
The server-to-client streaming format changed. If you built custom stream parsers, they need updating. Using useChat or useCompletion handles this automatically.
What this means for TypeScript AI engineers
1. Agent development is now framework-level
You don’t need to build your own agent loop. AI SDK 6’s Agent abstraction handles the common case. Focus on defining tools and instructions, not orchestration plumbing.
2. Provider portability is real
The 15+ provider adapters and AI Gateway mean you can switch models without rewriting application code. This matters for cost optimization, performance tuning, and risk mitigation (if one provider goes down).
3. The message-parts migration is worth it
The breaking change is painful, but the parts model enables dramatically better UIs. Agent interactions with visible tool calls, progress indicators, and structured results make AI applications feel more trustworthy.
4. Human-in-the-loop is the production pattern
Every production agent that can take consequential actions needs approval workflows. AI SDK 6 makes this a first-class concern rather than something you bolt on later.
Related reading: Cloudflare Agents SDK v0.3.0 and Workers AI, MCP reaches 97 million downloads, and multi-agent orchestration patterns.