Profile
Back to NewsBack
GitHub Trending 24 min
Reader Mode
ben-vargas/ai-sdk-provider-claude-code: Vercel AI SDK community provider for Claude Agent SDK

ben-vargas/ai-sdk-provider-claude-code: Vercel AI SDK community provider for Claude Agent SDK

4 hours ago

stable status npm version install size npm downloads Node.js ≥ 22 License: MIT

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
Please ensure you have appropriate permissions and comply with all applicable terms when using this provider.

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-result parts/events with isError: true instead of the provider-specific tool-error stream 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 SDK 0.3.283 exposes 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 existing skills setting 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] exposes WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE, and serializeModelOptions() 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 as canUseTool, hooks, logger, spawnClaudeCodeProcess, and SessionStore methods are not JSON-serializable and must be recreated by the application.
  • V4 custom and reasoning-file parts are not emitted as provider output yet. Claude Agent SDK 0.3.283 has no durable reasoning-file artifact output that maps to AI SDK reasoning-file; assistant-history custom and reasoning-file parts 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.raw now contains raw provider usage (previously in providerMetadata['claude-code'].rawUsage)
  • Internal type changes for LanguageModelV3Usage and LanguageModelV3FinishReason (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)
You can also use full model identifiers directly (e.g., 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)
The 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.
Restore old behavior explicitly:
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' }
  • settingSources includes 'project'
New recommended behavior (explicit config):
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:

  1. Update the package: npm install ai-sdk-provider-claude-code@ai-sdk-v5
  2. 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'],
   });
  1. If you never used CLAUDE.md or custom system prompts, no changes needed - v2.0.0 works the same for you.
Benefits of v2.0.0:
  • 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 v7 Output.json()) is not supported by Claude Code; use Output.object() / Output.array() / Output.choice() with a schema or choices. The provider emits a V4 unsupported warning with feature: 'responseFormat' and treats the call as plain text. In streamText, 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 (no structured_output). The provider mitigates the most common case: format keywords (date-time, email, 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's description (e.g., (expected format: email)). Server-side enforcement of format still does not exist in the CLI, but the AI SDK validates output against your original Zod schema client-side, so nothing is lost. Complex regex patterns (lookaheads/backreferences) remain unmitigated — pattern is 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 use generateObject() / streamObject(); new 4.x code should use generateText() / streamText() with Output.

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 agent
  • mcpServers - MCP servers available to the agent
  • criticalSystemReminder_EXPERIMENTAL - Experimental critical reminder
Alpha options (v3.5.0+, marked @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:

  • tools sets which built-in tools exist (['Read', 'Grep']; [] disables all built-in tools). MCP tools are controlled separately, through mcpServers and permission rules.
  • allowedTools only pre-approves tools, so they run without a permission prompt. It is not an allowlist: unlisted tools stay available and go through permissionMode, canUseTool, and hooks as usual.
  • disallowedTools denies 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 from toolName + input
  • displayName — short noun phrase for the tool action (e.g. "Read file"), suitable for button labels
  • description — 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):
> - A PreToolUse hook returning permissionDecision: 'defer' combined with a canUseTool callback fails the tool call before canUseTool is ever consulted. When canUseTool should handle the call, have the hook return no decision (or 'allow') instead of 'defer'.
- The PermissionDenied hook only fires for CLI-internal auto-mode classifier denials (e.g. permissionMode: 'auto'). Denials issued by canUseTool do not trigger it — they surface via the result message's permission_denials, which the provider merges into finalStep.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);
  },
});
  • onSdkMessage receives the raw SDKMessage objects the provider observes. Prefer this when you want to preserve new or rare SDK messages without waiting for provider-specific metadata.
  • onTaskEvent receives normalized task/subagent events and the same objects are collected into providerMetadata['claude-code'].taskEvents when any fire during the request.
  • onHookEvent receives normalized hook lifecycle events; because the underlying SDK only emits hook lifecycle messages when requested, pair it with includeHookEvents: true.
  • onMcpStatusChange receives the initial MCP server status snapshot observed during the request; final metadata includes that same request-time mcpServers snapshot when available. For live status after reconnectMcpServer(), toggleMcpServer(), or setMcpServers(), call controller.mcpServerStatus() while the query is live and SDK streaming input/output is active.
  • onElicitation is the first-class Agent SDK OnElicitation hook. It is for MCP-server-requested input; if you do not provide it, unhandled elicitation requests follow the SDK's default decline/cancel behavior.
  • agent selects a named Claude Code agent for the main thread. This is different from agents (which defines subagents). Use it only when you want the named agent's configured prompt/tools/model behavior to participate in the main request.
See 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)
npm 7+ installs these automatically; if your package manager does not auto-install peers (or you pin versions), add them to your project explicitly.

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
... (README truncated for length)
Chat with me