AI SDK Provider for Claude Agent SDK
Latest Release: Version 4.x supports AI SDK v7 stable with the Claude Agent SDK. Version 3.x moves to maintenance for AI SDK v6 under the ai-sdk-v6 tag.
ai-sdk-provider-claude-code lets you use Claude via the Vercel AI SDK through the official @anthropic-ai/claude-agent-sdk and the Claude Code CLI.
Version Compatibility
| Provider Version | AI SDK Version | Underlying SDK | NPM Tag | Status | Branch |
| ---------------- | -------------- | -------------------------------- | -------------------- | ----------- | ----------- |
| 4.x.x | v7 | @anthropic-ai/claude-agent-sdk | latest | Stable | main |
| 3.x.x | v6 | @anthropic-ai/claude-agent-sdk | ai-sdk-v6 | Maintenance | ai-sdk-v6 |
| 2.x.x | v5 | @anthropic-ai/claude-agent-sdk | ai-sdk-v5 | Legacy | ai-sdk-v5 |
| 1.x.x | v5 | @anthropic-ai/claude-code | v1-claude-code-sdk | Legacy | v1 |
| 0.x.x | v4 | @anthropic-ai/claude-code | ai-sdk-v4 | Legacy | ai-sdk-v4 |
Claude Code CLI: provider 4.3.3 bundles Claude Code CLI 2.1.283, the latest CLI release (2026-09-25), through its exact @anthropic-ai/[email protected] pin. If you run your own binary via pathToClaudeCodeExecutable, check the minimum Claude Code version noted on individual settings.
Install commands for each line are listed under Installation below.
Zod Compatibility
The 4.x line requires Zod ^4.1.8. Version 3.x remains available for AI SDK v6 under the ai-sdk-v6 tag.
npm install ai-sdk-provider-claude-code ai zod@^4.1.8
Note: Zod 3 support was dropped in v3.2.0 due to the underlying@anthropic-ai/[email protected]requiring Zod 4. If you need Zod 3 support, use[email protected].
Installation
1. Install and authenticate the CLI
See the official docs for platform-specific options.
curl -fsSL https://claude.ai/install.sh | bash
claude auth login
2. Add the provider
# For AI SDK v7 (4.x; current latest tag)
npm install ai-sdk-provider-claude-code ai
For AI SDK v6 maintenance (3.x)
npm install ai-sdk-provider-claude-code@ai-sdk-v6 ai@^6.0.0
For AI SDK v5
npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
For AI SDK v4 (legacy)
npm install ai-sdk-provider-claude-code@ai-sdk-v4 ai@^4.3.16
or use a specific version: npm install ai-sdk-provider-claude-code@^0.2.2
Disclaimer
This is an unofficial community provider and is not affiliated with or endorsed by Anthropic or Vercel. By using this provider:
- You understand that your data will be sent to Anthropic's servers through the Claude Agent SDK
- You agree to comply with Anthropic's Terms of Service
- You acknowledge this software is provided "as is" without warranties of any kind
Quick Start
AI SDK v7 (latest)
// npm install ai-sdk-provider-claude-code ai
import { streamText } from 'ai';
import { claudeCode } from 'ai-sdk-provider-claude-code';
const result = streamText({
model: claudeCode('haiku'),
prompt: 'Hello, Claude!',
});
const text = await result.text;
console.log(text);
AI SDK v6 (maintenance)
// npm install ai-sdk-provider-claude-code@ai-sdk-v6 ai@^6.0.0
import { streamText } from 'ai';
import { claudeCode } from 'ai-sdk-provider-claude-code';
const result = streamText({
model: claudeCode('haiku'),
prompt: 'Hello, Claude!',
});
const text = await result.text;
console.log(text);
AI SDK v5
// npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
import { streamText } from 'ai';
import { claudeCode } from 'ai-sdk-provider-claude-code';
const result = streamText({
model: claudeCode('haiku'),
prompt: 'Hello, Claude!',
});
const text = await result.text;
console.log(text);
Breaking Changes
Version 4.0.0 (AI SDK v7)
This release ports the provider to AI SDK v7 / LanguageModelV4, adds first-class Claude Agent SDK callback, query-controller, MCP, and image support, and keeps the v7 support boundaries explicit:
- Requires Node.js ≥ 22 and Zod
^4.1.8 - ESM-only package output; CommonJS
require()is no longer available - Tool failures now use spec
tool-resultparts/events withisError: trueinstead of the provider-specifictool-errorstream extension
Optional AI SDK v7 surfaces not implemented
Version 4.0.0 intentionally keeps optional provider surfaces absent unless the Claude Agent SDK has a durable provider-reference mapping:
ProviderV4.files()is not implemented yet. The AI SDK interface uploads{ type: 'data' }or{ type: 'text' }bytes and returns a reusable provider reference, but Claude Agent SDK0.3.283exposes no direct upload/reuse API for that contract. This provider forwards inline image file parts in prompts; non-image inline files (for example PDFs) emit an unsupported-file call warning and are not forwarded. It does not upload files into durable provider references.- Canonical V4 tool-result file parts are replayed into conversation history as text markers like
[File; raw file bytes are not re-sent on replay. Richer tool-result file replay, such as re-sending actual image/file bytes for tool-result file parts, is deferred.: ] ProviderV4.skills()is not implemented yet. Claude Code skills are loaded from configured user/project/local skill directories with the existingskillssetting below; there is no Agent SDK API that uploads a skill bundle and returns an AI SDK provider reference.- Workflow serialization is deferred.
@ai-sdk/[email protected]exposesWORKFLOW_SERIALIZE,WORKFLOW_DESERIALIZE, andserializeModelOptions()for provider model classes in the AI SDK v7 stack, but this provider has not added a serialization contract for provider instances or settings. Callback/function settings such ascanUseTool, hooks,logger,spawnClaudeCodeProcess, andSessionStoremethods are not JSON-serializable and must be recreated by the application. - V4
customandreasoning-fileparts are not emitted as provider output yet. Claude Agent SDK0.3.283has no durable reasoning-file artifact output that maps to AI SDKreasoning-file; assistant-historycustomandreasoning-fileparts have no Claude Code replay representation and are skipped (unknown unsupported content variants still warn).
Version 3.0.0 (AI SDK v6 Stable)
This version upgrades to AI SDK v6 stable with updated provider types:
usage.rawnow contains raw provider usage (previously inproviderMetadata['claude-code'].rawUsage)- Internal type changes for
LanguageModelV3UsageandLanguageModelV3FinishReason(transparent to most users)
Version 2.0.0 (Claude Agent SDK Migration)
This version migrates to @anthropic-ai/claude-agent-sdk with new defaults for better control:
- System prompt is no longer applied by default
- Filesystem settings (CLAUDE.md, settings.json) are no longer loaded by default
- See Migrating to Claude Agent SDK section below for migration details
Version 1.x (AI SDK v5)
See Breaking Changes Guide for details on migrating from v0.x to v1.x.
Key changes:
- Requires AI SDK v5
- New streaming API pattern
- Updated token usage properties
- Changed message types
Models
fable- Claude Fable (most capable)opus- Claude Opus (highly capable)sonnet- Claude Sonnet (balanced performance)haiku- Claude Haiku (fastest, most cost-effective)
claude-fable-5, claude-sonnet-4-6, claude-opus-4-8).
Documentation
- Session Management - Creating, resuming, forking, inspecting, and deleting sessions
- Examples - Sample scripts and patterns
- Usage Guide - Comprehensive examples and configuration (written for provider 2.x / AI SDK v5; most patterns still apply)
- Troubleshooting - Common issues and solutions (written for provider 2.x / AI SDK v5)
- Tool Streaming Support - Event semantics and performance notes (written for provider 2.x / AI SDK v5)
- Breaking Changes - v0.x to v1.x migration guide (historical)
docs/ai-sdk-v4/ and docs/ai-sdk-v5/ directories cover legacy provider versions (0.x and 1.x–2.x respectively) and are kept for reference.
Migrating to Claude Agent SDK (v2.0.0)
Version 2.0.0 migrates from @anthropic-ai/claude-code to @anthropic-ai/claude-agent-sdk. Two defaults changed:
- System prompt is no longer applied by default.
- Filesystem settings (CLAUDE.md, settings.json) are not loaded by default.
import { claudeCode } from 'ai-sdk-provider-claude-code';
const model = claudeCode('sonnet', {
systemPrompt: { type: 'preset', preset: 'claude_code' },
settingSources: ['user', 'project', 'local'],
});
CLAUDE.md requires:
systemPrompt: { type: 'preset', preset: 'claude_code' }settingSourcesincludes'project'
const model = claudeCode('sonnet', {
systemPrompt: 'You are a helpful assistant specialized in ...',
settingSources: ['project'], // or omit for no filesystem settings
});
CLI install and auth are unchanged:
curl -fsSL https://claude.ai/install.sh | bash
claude auth login
Migrating from v1.x to v2.0.0
If you're upgrading from version 1.x:
- Update the package:
npm install ai-sdk-provider-claude-code@ai-sdk-v5 - If you relied on default system prompt or CLAUDE.md, add explicit configuration:
const model = claudeCode('sonnet', {
systemPrompt: { type: 'preset', preset: 'claude_code' },
settingSources: ['user', 'project', 'local'],
});
- If you never used CLAUDE.md or custom system prompts, no changes needed - v2.0.0 works the same for you.
- Predictable behavior across environments (no hidden filesystem settings)
- Better suited for CI/CD and multi-tenant applications
- Explicit configuration over implicit defaults
- Future-proof alignment with Claude Agent SDK design
Structured Outputs
This provider supports native structured outputs via the Claude Agent SDK's outputFormat option: the CLI validates the model's answer against your schema and asks the model to retry on a mismatch. On the 4.x line (AI SDK v7), use generateText() with an output specification such as Output.object({ schema }), then destructure output from the result. streamText() with the same output setting also works; see Streaming structured output for how partial objects are delivered.
import { generateText, Output } from 'ai';
import { claudeCode } from 'ai-sdk-provider-claude-code';
import { z } from 'zod';
const UserProfileSchema = z.object({
name: z.string(),
age: z.number(),
email: z.string().describe('Email address (validate client-side)'),
});
const { output } = await generateText({
model: claudeCode('sonnet'),
output: Output.object({ schema: UserProfileSchema }),
prompt: 'Generate a user profile for a software developer',
});
console.log(output); // Matches the schema above
// { name: "Alex Chen", age: 28, email: "[email protected]" }
Benefits:
- ✅ Schema compliance (supported features) - The CLI validates output against the schema
- ✅ No JSON parsing errors - AI SDK handles validation against your schema
- ✅ No prompt engineering - Schema enforcement is native to the SDK
- ✅ Better performance - No retry/extraction logic needed
Note: Schema-less JSON output (AI SDK v7Output.json()) is not supported by Claude Code; useOutput.object()/Output.array()/Output.choice()with a schema or choices. The provider emits a V4unsupportedwarning withfeature: 'responseFormat'and treats the call as plain text. InstreamText, the text is buffered and emitted once when the response completes.
> Current CLI limitation: Some JSON Schema features can cause the Claude Code CLI to silently fall back to prose (nostructured_output). The provider mitigates the most common case:formatkeywords (date-time,uri,uuid, ... — produced by Zod's.datetime(),.email(),.url(),.uuid()) are stripped client-side before the schema is sent, with the hint folded into the field'sdescription(e.g.,(expected format: email)). Server-side enforcement offormatstill does not exist in the CLI, but the AI SDK validatesoutputagainst your original Zod schema client-side, so nothing is lost. Complex regexpatterns (lookaheads/backreferences) remain unmitigated —patternis passed through untouched because the CLI genuinely rejects some patterns. Keep generation schemas simple and enforce stricter invariants after generation.
> If you are staying on the 3.x (AI SDK v6) line, its legacy structured-output examples may still usegenerateObject()/streamObject(); new 4.x code should usegenerateText()/streamText()withOutput.
Streaming structured output
By default, streamText({ output }) delivers the object in one piece when the run finishes: the provider holds back the model's in-progress JSON and emits the CLI-validated structured_output. Tool progress, reasoning, and raw events still stream live. This is deliberate. The model can submit more than one answer in a run (for example, the CLI rejects a first attempt that fails the schema and the model retries), and text that has already been streamed cannot be taken back, so streaming each attempt would concatenate them into unparseable JSON (NoObjectGeneratedError).
To get incremental partial objects anyway, opt in with streamStructuredOutput: true:
const { partialOutputStream, output } = streamText({
model: claudeCode('sonnet', { streamStructuredOutput: true }),
output: Output.object({ schema: UserProfileSchema }),
prompt: 'Generate a user profile for a software developer',
});
for await (const partial of partialOutputStream) {
console.log(partial); // Updates as each field is typed
}
With the opt-in, a run in which the model submits more than one structured answer fails with NoObjectGeneratedError, and partialOutputStream can show fields from an attempt the CLI later rejected. Use it only where a live preview is worth an occasional failed run. To preview progress without that risk, keep the default and render previews from raw SDK events (onSdkMessage, or includeRawChunks on the AI SDK call), where the StructuredOutput tool's input_json_delta events arrive as stream events.
The opt-in mode is best-effort. If the CLI's stop event arrives before all of the JSON fragments (a non-standard ordering), the text already streamed can stay truncated even though the final structured_output is valid. The default buffered mode is not affected.
Core Features
- 🚀 Vercel AI SDK compatibility
- 🔄 Streaming support
- 💬 Multi-turn conversations
- 🎯 Native structured outputs with schema compliance for supported features
- 🛑 AbortSignal support
- 🔧 Tool management (MCP servers, permissions)
- 🧩 Callbacks (
onSdkMessage, task/hook/MCP status events,canUseTool,onElicitation) - 🎛️ Query controller access for safe live-session controls
AI SDK v7 app-level features
DevTools and OpenTelemetry/OTel telemetry registration are app-level ai package features. This provider exposes standard AI SDK v7 metadata and stream parts for them, but adds no runtime dependencies for DevTools or OTel.
Agent SDK Options (Advanced)
This provider exposes Agent SDK options directly. Key options include:
| Option | Description |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| betas | Enable beta features (e.g., ['context-1m-2025-08-07']) |
| sandbox | Configure sandbox behavior ({ enabled: true }). Cannot be combined with a settings file path (inline settings objects are fine) |
| plugins | Load custom plugins from local paths |
| resumeSessionAt | Resume session at a specific message UUID |
| resumeDropsTurn | Guard resume + resumeSessionAt with the discarded prompt UUID; consumed after success. See guarded rewind |
| enableFileCheckpointing | Enable file rewind support |
| maxBudgetUsd | Maximum budget in USD for the query |
| tools | Tool configuration (array of names or preset) |
| allowDangerouslySkipPermissions | Allow bypassing permissions |
| persistSession | When false, disables session persistence to disk (v3.2.0+) |
| spawnClaudeCodeProcess | Custom process spawner for VMs/containers (v3.2.0+) |
| permissionMode | Permission mode: 'default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk', 'auto' ('auto' and 'dontAsk' added in SDK 0.3.x; 'delegate' was removed in SDK 0.3.x and the CLI rejects it, so the provider rejects it at validation time) |
| sessionId | Use a specific session ID for deterministic tracking and correlation (v3.4.0+). Must be a valid UUID; cannot be combined with continue/resume unless forkSession is also set |
| debug | Enable programmatic debug logging from the SDK (v3.4.0+) |
| debugFile | Path to a file for SDK debug log output (v3.4.0+) |
| effort | Effort level: 'low', 'medium', 'high', 'xhigh', or 'max' |
| thinking | Thinking config: { type: 'adaptive' }, { type: 'enabled', budgetTokens?: number }, or { type: 'disabled' } |
| promptSuggestions | Enable prompt suggestions (boolean) |
| skills | Enable skills for the session: 'all' or an array of skill names (v3.5.0+) |
| settings | Inline Settings object or path to a settings JSON file (v3.5.0+) |
| managedSettings | Restrictive policy-tier settings enforced on the subprocess (v3.5.0+) |
| toolAliases | Map built-in tool names to replacement tools, e.g. { Bash: 'mcp__workspace__bash' } (v3.5.0+) |
| toolConfig | Per-tool configuration for built-in tools, e.g. { askUserQuestion: { previewFormat: 'html' } } (v3.5.0+) |
| planModeInstructions | Custom workflow instructions for plan mode (v3.5.0+) |
| title | Custom title for a new session (v3.5.0+) |
| forwardSubagentText | Forward subagent text/thinking blocks for nested transcripts (v3.5.0+) |
| agentProgressSummaries | Periodic AI-generated progress summaries for running subagents (v3.5.0+) |
| includeHookEvents | Include hook lifecycle events in the output stream (v3.5.0+) |
| permissionPrompts | Who answers permission prompts (v4.3.0+): 'host' (default) uses canUseTool or permissionPromptToolName; 'none' denies requests that would prompt and never calls canUseTool. Permission mode, rules and hooks still apply. |
| pluginDelivery | How plugin paths reach Claude Code (v4.3.0+): 'argv' (default) uses flags; 'initialize' sends them over stdin to avoid command-line length limits. Requires Claude Code 2.1.261+; the bundled binary qualifies. |
| projectConfigRoot | Trusted checkout that cwd is a worktree of (v4.3.2+). Project settings, .mcp.json, the .claude config trees and CLAUDE_PROJECT_DIR resolve from here instead of cwd, so the branch checked out in cwd cannot change what the session runs. Absolute path; requires Claude Code 2.1.275+ (the bundled binary qualifies) |
| perTaskStopAffordance | Declare that your app renders per-task stop controls wired to stopTask, so an interrupt on a live open-input query spares background tasks (v4.2.0+). A capability assertion, not a toggle — see Query controller access below |
| verbatimPrompts | Send prompts as written (v4.3.3+): no @path mention expansion or slash-command dispatch, for prompt text the end user did not type. Current CLIs also skip turn-start context attachments (nested CLAUDE.md/rules, skill and tool listings). Requires Claude Code 2.1.248+ (the bundled binary qualifies) |
| fallbackModel | Fallback model(s) if the primary is overloaded — accepts a comma-separated list to try in order. Must differ from the main model |
| onUserDialog | Callback rendering blocking CLI dialogs (request_user_dialog); see User dialogs below |
| supportedDialogKinds | Dialog kinds your onUserDialog can render; required for dialogs to be emitted at all |
| onSdkMessage | Raw callback for every observed Agent SDK SDKMessage; useful for future SDK message types and custom telemetry |
| onTaskEvent | Callback for task/subagent lifecycle events (ClaudeCodeTaskEvent); also accumulated in providerMetadata['claude-code'].taskEvents when present |
| onHookEvent | Callback for hook lifecycle events (ClaudeCodeHookEvent); set includeHookEvents: true when you want the SDK to emit hook started/progress/response messages |
| onMcpStatusChange | Callback for the initial MCP status snapshot observed from the SDK init message (ClaudeCodeMcpStatusEvent); the same request-time snapshot is surfaced in providerMetadata['claude-code'].mcpServers when available. For live status after runtime MCP changes, use controller.mcpServerStatus() while the query is live and SDK streaming input/output is active |
| onElicitation | First-class Agent SDK OnElicitation callback for MCP elicitation requests (form fields, URL auth, or other server-requested input) |
| agent | Select a named Claude Code agent persona for the main thread. Use intentionally: a configured agent can carry its own prompt/tool/model policy, separate from the AI SDK call's prompt/model surface |
| onQueryControllerCreated | Callback receiving a ClaudeCodeQueryController for safe live-query controls. Control-protocol methods require a live SDK Query, and most require SDK streaming input/output; controller.rawQuery remains available when you need the raw SDK Query |
System prompt (systemPrompt) accepts a string, a string array, or the Claude Code preset object (v3.5.0+ for the array form). In the array form, include the re-exported SYSTEM_PROMPT_DYNAMIC_BOUNDARY marker as a standalone element to split the static (cross-session cacheable) prefix from the dynamic suffix. The preset object additionally accepts excludeDynamicSections: true to strip per-user dynamic sections (working directory, git status) so the prompt caches across users.
Agent definitions (agents) use the Agent SDK's AgentDefinition type directly (v3.5.0+), which adds effort, permissionMode, background, memory, initialPrompt, skills, maxTurns, and full model ID strings on top of the previously supported fields:
disallowedTools- Tools to explicitly disallow for the agentmcpServers- MCP servers available to the agentcriticalSystemReminder_EXPERIMENTAL- Experimental critical reminder
@alpha upstream and subject to change): taskBudget ({ total: number } API-side token budget), sessionStore (mirror session transcripts to a custom storage adapter; the provider rejects combining it with persistSession: false or enableFileCheckpointing: true, and continue: true without a resume ID requires the store to implement listSessions()), sessionStoreFlush ('batched' or 'eager'), and loadTimeoutMs (resume-load timeout). The SDK's InMemorySessionStore reference implementation and the SessionStore/SessionStoreFlush types are re-exported.
The package also re-exports SDK helper functions/types used by advanced hosts: resolveSettings, filterEscalatingDefaultMode, SandboxCredentialsConfig, and SDKFilesPersistedEvent in addition to the session, hook, MCP, permission, and warm-start exports documented below.
User dialogs (onUserDialog / supportedDialogKinds)
Some CLI flows ask the host to render a blocking dialog (a request_user_dialog control request) — for example 'refusal_fallback_prompt', which asks whether to retry a refused request differently. The SDK fails closed here: a dialog kind not declared in supportedDialogKinds is never emitted, and the flow behind it degrades to its no-dialog behavior (for 'refusal_fallback_prompt', the classic refusal error ends the turn). Providing onUserDialog alone does NOT opt you in — both options are required, and passing a non-empty supportedDialogKinds without the callback throws at SDK option intake, so the provider rejects that combination at validation time (createClaudeCode/model construction throws Invalid settings).
const model = claudeCode('sonnet', {
supportedDialogKinds: ['refusal_fallback_prompt'],
onUserDialog: async (request) => {
// Each dialogKind defines its own payload/result shape; answer
// unrecognized kinds with { behavior: 'cancelled' } so the CLI
// applies the dialog's default behavior.
if (request.dialogKind === 'refusal_fallback_prompt') {
// Valid results for this kind: 'retry_fallback' | 'edit_prompt' | 'cancelled'
return { behavior: 'completed', result: 'retry_fallback' };
}
return { behavior: 'cancelled' };
},
});
The OnUserDialog, UserDialogRequest, and UserDialogResult types are re-exported. Note that UserDialogResult.result is typed unknown — the CLI validates it against the dialog kind's own result schema at runtime, and a result that doesn't match (e.g. the wrong shape or an unknown string) is silently replaced by the dialog's default (for 'refusal_fallback_prompt', 'cancelled'), so double-check the result values for each kind you handle.
Tool availability and pre-approval (tools, allowedTools, disallowedTools)
These three settings do different jobs, and you can combine them:
toolssets which built-in tools exist (['Read', 'Grep'];[]disables all built-in tools). MCP tools are controlled separately, throughmcpServersand permission rules.allowedToolsonly pre-approves tools, so they run without a permission prompt. It is not an allowlist: unlisted tools stay available and go throughpermissionMode,canUseTool, and hooks as usual.disallowedToolsdenies tools (or matching calls, such as'Bash(rm:*)'). Deny rules take precedence, so a tool listed in both lists is denied.
const model = claudeCode('sonnet', {
tools: ['Read', 'Grep', 'Bash'], // only these built-in tools exist
allowedTools: ['Read', 'Grep', 'Bash(git log:*)'], // run without prompting
disallowedTools: ['Bash(rm:*)'], // calls matching this rule are always denied
});
Permission decisions (canUseTool, permissionMode, and AI SDK toolApproval)
For hosts without an approval UI, set permissionPrompts: 'none'. Permission mode, rules and hooks still decide whether a tool can run; requests that would otherwise prompt are denied immediately, and canUseTool is never called. Omit this option (or use 'host') to route prompts to canUseTool or permissionPromptToolName. The SDK still rejects combining those two handlers, including when prompts are disabled.
SDK 0.3.x enriched the Claude Agent SDK canUseTool callback (no provider change needed — these arrive on the existing options argument):
AI SDK v7 toolApproval is not bridged to Claude Code's internal tool permission system. Use Claude Agent SDK canUseTool / permissionMode for Claude Code built-in and MCP tools; call-level toolApproval on generateText/streamText applies to app-level AI SDK tools handled by the ai package.
title— full permission prompt sentence (e.g. "Claude wants to read foo.txt"); prefer it over reconstructing fromtoolName+ inputdisplayName— short noun phrase for the tool action (e.g. "Read file"), suitable for button labelsdescription— human-readable subtitle (e.g. "Claude will have read and write access to ...")
PermissionResult (both allow and deny branches) gained an optional decisionClassification — 'user_temporary' | 'user_permanent' | 'user_reject' — describing how the decision was made; the PermissionDecisionClassification type is re-exported.
const model = claudeCode('sonnet', {
canUseTool: async (toolName, input, options) => {
// Prefer the SDK-provided prompt text over reconstructing it yourself.
const approved = await askUser({
prompt: options.title ?? Allow ${toolName}?, // "Claude wants to read foo.txt"
buttonLabel: options.displayName ?? toolName, // "Read file"
subtitle: options.description, // "Claude will have read access to ..."
});
return approved
? { behavior: 'allow', updatedInput: input, decisionClassification: 'user_temporary' }
: { behavior: 'deny', message: 'Denied by user', decisionClassification: 'user_reject' };
},
});
Upstream CLI caveats (verified on CLI 2.1.172):
> - APreToolUsehook returningpermissionDecision: 'defer'combined with acanUseToolcallback fails the tool call beforecanUseToolis ever consulted. WhencanUseToolshould handle the call, have the hook return no decision (or'allow') instead of'defer'.
- ThePermissionDeniedhook only fires for CLI-internal auto-mode classifier denials (e.g.permissionMode: 'auto'). Denials issued bycanUseTooldo not trigger it — they surface via the result message'spermission_denials, which the provider merges intofinalStep.providerMetadata['claude-code'].permissionDenials.
Agent SDK event callbacks (onSdkMessage, task/hook/MCP status, elicitation)
Use these callbacks when you need Agent SDK observability without parsing AI SDK stream parts yourself:
const model = claudeCode('sonnet', {
onSdkMessage: (message) => {
console.debug('raw SDK message:', message.type);
},
onTaskEvent: (event) => {
console.debug('task event:', event.subtype);
},
includeHookEvents: true,
onHookEvent: (event) => {
console.debug('hook event:', event.subtype);
},
onMcpStatusChange: (status) => {
console.debug('MCP init status snapshot:', status);
},
onElicitation: async (request) => {
return renderMcpElicitation(request);
},
});
onSdkMessagereceives the rawSDKMessageobjects the provider observes. Prefer this when you want to preserve new or rare SDK messages without waiting for provider-specific metadata.onTaskEventreceives normalized task/subagent events and the same objects are collected intoproviderMetadata['claude-code'].taskEventswhen any fire during the request.onHookEventreceives normalized hook lifecycle events; because the underlying SDK only emits hook lifecycle messages when requested, pair it withincludeHookEvents: true.onMcpStatusChangereceives the initial MCP server status snapshot observed during the request; final metadata includes that same request-timemcpServerssnapshot when available. For live status afterreconnectMcpServer(),toggleMcpServer(), orsetMcpServers(), callcontroller.mcpServerStatus()while the query is live and SDK streaming input/output is active.onElicitationis the first-class Agent SDKOnElicitationhook. It is for MCP-server-requested input; if you do not provide it, unhandled elicitation requests follow the SDK's default decline/cancel behavior.agentselects a named Claude Code agent for the main thread. This is different fromagents(which defines subagents). Use it only when you want the named agent's configured prompt/tools/model behavior to participate in the main request.
ClaudeCodeSettings for the full list of supported options (e.g., allowedTools, disallowedTools, hooks, canUseTool, env, settingSources).
For options not explicitly exposed, use the sdkOptions escape hatch. It overrides explicit settings,
but provider-managed fields are ignored (model, abortController, prompt, outputFormat).
If you set sdkOptions.resume, it also drives the streaming prompt session_id so the SDK
and prompt target the same session.
const model = claudeCode('sonnet', {
betas: ['context-1m-2025-08-07'],
sandbox: { enabled: true },
persistSession: false, // Don't persist session to disk
sdkOptions: {
maxBudgetUsd: 1,
resume: 'session-abc',
},
});
SDK boundaries
Provider-managed fields are set internally and ignored if passed via sdkOptions: model, abortController, prompt, and outputFormat.
agent and onElicitation are first-class ClaudeCodeSettings fields on the 4.x line. Older 3.x docs described them as sdkOptions escape hatches; use the direct settings names in new code.
Alternate SDK entry points — the Agent SDK also ships /browser (WebSocket browser transport), /bridge (remote-control session transport), and /assistant (worker/daemon harness) entry points. These are alpha surfaces with their own versioning cadence and are aimed at embedding hosts rather than AI SDK consumers, so this provider does not re-export them. Import them directly from @anthropic-ai/claude-agent-sdk/ if you need them, with the usual alpha-stability caveats.
Claude Agent SDK 0.3.x Notes
This provider depends on @anthropic-ai/[email protected] (exact pin), which bundles Claude Code CLI 2.1.283. The pin is exact rather than a caret because upstream releases have shipped broken sdk.d.ts declarations before (0.3.198–0.3.202 collapsed SDKMessage to any, fixed in 0.3.203); the weekly canary gates each pin move. The 0.3.x line introduces a few changes worth knowing about:
New peer dependencies
The Agent SDK now declares two additional peer dependencies alongside zod:
@anthropic-ai/sdk(>=0.93.0)@modelcontextprotocol/sdk(^1.29.0)
Per-platform native binaries
The Agent SDK now distributes the Claude Code runtime as per-platform native binaries via optionalDependencies (e.g., @anthropic-ai/claude-agent-sdk-darwin-arm64, -linux-x64, -win32-x64) instead of a single bundled cli.js. The right binary for your platform is selected at install time. If you use pathToClaudeCodeExecutable, executable, or executableArgs, re-validate them against your deployment — they primarily apply when pointing at a custom CLI rather than the bundled native binary. Docker/CI images that prune optionalDependencies will need to keep them enabled.
Settings isolation (settingSources)
SDK 0.3.x changed the SDK-level default: omitting settingSources now loads ALL filesystem settings (user, project, and local — matching CLI behavior). This provider preserves its documented isolation default by explicitly passing settingSources: [] when you don't set it. Opt in to filesystem settings via settingSources: ['user', 'project', 'local'] (or override through sdkOptions.settingSources).
When cwd is a git worktree of a trusted checkout, set projectConfigRoot (v4.3.2+) to that checkout's absolute path. Project settings (hooks, permissions), .mcp.json, the .claude config trees (commands, agents, skills, workflows, routines, output-styles) and CLAUDE_PROJECT_DIR then come from the trusted checkout instead of from whatever branch the worktree has checked out. The option is forwarded only when set, sdkOptions.projectConfigRoot overrides it, and validation rejects relative paths (including in that override). Requires Claude Code 2.1.275+ (the bundled binary qualifies).
Subprocess environment allowlist
SDK 0.3.x treats Options.env as a full replacement for the subprocess environment (it is no longer merged with process.env). The provider always constructs the subprocess environment from a sanitizing allowlist of process.env, then applies your env setting and sdkOptions.env on top (your values win; set a key to undefined to remove it). The allowlist is:
- Platform basics — POSIX:
HOME,LOGNAME,PATH,SHELL,TERM,USER,LANG,LC_ALL,TMPDIR; Windows:APPDATA,COMSPEC,HOMEDRIVE,HOMEPATH,LOCALAPPDATA,PATH,PATHEXT,SYSTEMDRIVE,SYSTEMROOT,TEMP,TMP,USERNAME,USERPROFILE,WINDIR - Prefix-matched — any variable starting with
ANTHROPIC_, `CLAU