openpencil/vendor/anthropic-agent-sdk/README_API.md

77 KiB

Agent SDK reference - Rust

API reference for the Rust Agent SDK, including all functions, types, and traits.


Installation

Add to your Cargo.toml:

[dependencies]
anthropic-agent-sdk = "0.2"

With MCP SDK server support:

[dependencies]
anthropic-agent-sdk = { version = "0.2", features = ["rmcp"] }

Functions

query()

One-shot query function for stateless interactions. For multi-turn conversations, use ClaudeSDKClient instead.

pub async fn query(
    prompt: impl Into<String>,
    options: Option<ClaudeAgentOptions>,
) -> Result<impl Stream<Item = Result<Message>>>

Parameters

Parameter Type Description
prompt impl Into<String> The input prompt
options Option<ClaudeAgentOptions> Configuration options (defaults to ClaudeAgentOptions::default() if None)

Returns

Returns a Stream<Item = Result<Message>> that yields messages from the conversation.

Example

use anthropic_agent_sdk::query;
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let stream = query("What is the capital of France?", None).await?;
    let mut stream = Box::pin(stream);

    while let Some(message) = stream.next().await {
        println!("{:?}", message?);
    }
    Ok(())
}

With options:

use anthropic_agent_sdk::{query, ClaudeAgentOptions};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::builder()
        .system_prompt("You are a helpful coding assistant")
        .max_turns(1)
        .build();

    let stream = query("Write a hello world in Python", Some(options)).await?;
    let mut stream = Box::pin(stream);

    while let Some(message) = stream.next().await {
        println!("{:?}", message?);
    }
    Ok(())
}

ClaudeSDKClient

Client for bidirectional communication with Claude Code. Supports interactive conversations, interrupts, hooks, and permission callbacks.

pub struct ClaudeSDKClient { /* fields omitted */ }

Constructor

impl ClaudeSDKClient {
    pub async fn new(
        options: ClaudeAgentOptions,
        cli_path: Option<PathBuf>,
    ) -> Result<Self>
}
Parameter Type Description
options ClaudeAgentOptions Configuration options
cli_path Option<PathBuf> Path to Claude Code CLI (auto-detected if None)

Methods

Method Description
send_message(content) Send a message to Claude
next_message() Get the next message from the stream
receive_response() Stream messages until a Result message
interrupt() Send an interrupt signal
close() Close the client and clean up resources
is_connected() Check if the client is connected
get_session_id() Get the current session ID
queue_message(content) Queue a message to send after current turn
next_buffered() Get next message, auto-send queued after Result
queued_count() Number of messages in queue
has_queued() Check if queue has messages
send_queued() Manually send next queued message
clear_queue() Clear all queued messages
bind_session(session_id) Bind client to session, enable validation
bound_session() Get bound session ID
unbind_session() Clear session binding
validate_session() Validate current session matches bound
session_info() Get session information (model, tools, MCP servers)
current_model() Get the current model being used
available_tools() Get available tools in this session
mcp_server_status() Get status of MCP servers
supported_models() Get list of known Claude models (static)
supported_commands() Get available slash commands
account_info() Get account information
set_model(model) Store model preference locally
get_runtime_model() Get runtime model override
set_permission_mode(mode) Store permission mode locally
get_runtime_permission_mode() Get runtime permission mode override
set_max_thinking_tokens(tokens) Store thinking tokens preference locally
get_runtime_max_thinking_tokens() Get runtime thinking tokens override
clear_runtime_overrides() Clear all runtime overrides
cancellation_token() Get a child cancellation token
cancel() Cancel all ongoing operations
is_cancelled() Check if cancellation was requested
take_hook_receiver() Take the hook event receiver for manual handling
take_permission_receiver() Take the permission request receiver
respond_to_hook(hook_id, response) Respond to a hook event
respond_to_permission(request_id, result) Respond to a permission request

Basic Usage

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::default();
    let mut client = ClaudeSDKClient::new(options, None).await?;

    client.send_message("Hello, Claude!").await?;

    while let Some(message) = client.next_message().await {
        match message? {
            Message::Assistant { message, .. } => {
                println!("Response: {:?}", message.content);
            }
            Message::Result { .. } => break,
            _ => {}
        }
    }

    client.close().await?;
    Ok(())
}

Using receive_response()

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::default();
    let mut client = ClaudeSDKClient::new(options, None).await?;

    client.send_message("Hello").await?;

    let mut messages = Box::pin(client.receive_response());
    while let Some(msg) = messages.next().await {
        match msg? {
            Message::Assistant { message, .. } => println!("{:?}", message),
            Message::Result { .. } => println!("Done!"),
            _ => {}
        }
    }

    Ok(())
}

Interrupt

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions};
use tokio::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::default();
    let mut client = ClaudeSDKClient::new(options, None).await?;

    client.send_message("Write a long essay").await?;

    // After some time, interrupt
    tokio::time::sleep(Duration::from_millis(500)).await;
    client.interrupt().await?;

    Ok(())
}

Cancellation

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::default();
    let mut client = ClaudeSDKClient::new(options, None).await?;

    let cancel_token = client.cancellation_token();

    // Use in a spawned task
    let token = cancel_token.clone();
    tokio::spawn(async move {
        tokio::select! {
            _ = token.cancelled() => {
                println!("Operation cancelled");
            }
            _ = async { /* long operation */ } => {
                println!("Operation completed");
            }
        }
    });

    // Later, cancel all operations
    client.cancel();
    client.close().await?;

    Ok(())
}

Message Buffering

The CLI only reads stdin between turns (after Result, before next user message). Messages sent during streaming are ignored. The SDK provides built-in buffering to handle multi-turn conversations:

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::builder()
        .max_turns(10)
        .build();
    let mut client = ClaudeSDKClient::new(options, None).await?;

    // Send first message
    client.send_message("What is Python?").await?;

    // Queue follow-up messages (sent automatically after each Result)
    client.queue_message("What is TypeScript?");
    client.queue_message("Compare Rust to both.");

    let total = 1 + client.queued_count(); // Track expected turns
    let mut turn = 0;

    // next_buffered() auto-sends queued messages after each Result
    while let Some(msg) = client.next_buffered().await {
        match msg? {
            Message::Assistant { message, .. } => {
                println!("Claude: {:?}", message.content);
            }
            Message::Result { .. } => {
                turn += 1;
                if turn >= total {
                    break;
                }
            }
            _ => {}
        }
    }

    client.close().await?;
    Ok(())
}

Security: Each queued message is associated with the session_id at queue time. When sending, the SDK verifies the session hasn't changed - if it has, the message is discarded and the queue is cleared to prevent messages from being sent to an unintended conversation context.

Method Returns Description
queue_message(content) () Add message to buffer with current session_id
next_buffered() Option<Result<Message>> Get next message, auto-send queued after Result
queued_count() usize Number of messages waiting in queue
has_queued() bool Check if queue has pending messages
send_queued() Result<bool> Manually send next queued message
clear_queue() () Clear all queued messages

Session Binding (Secure by Default)

Sessions are automatically bound on first Result message for security:

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::default();
    let mut client = ClaudeSDKClient::new(options, None).await?;

    client.send_message("Hello").await?;

    // First Result auto-binds the session
    while let Some(msg) = client.next_message().await {
        if let Message::Result { .. } = msg? {
            // Session is now auto-bound!
            break;
        }
    }

    // All subsequent sends are validated automatically
    // Returns ClaudeError::SessionMismatch if session changed
    client.send_message("Follow-up").await?;

    client.close().await?;
    Ok(())
}
Method Returns Description
bind_session(session_id) () Override auto-bound session
bound_session() Option<SessionId> Get bound session ID
unbind_session() () Clear binding (for multi-session scenarios)
validate_session() Result<()> Validate current matches bound (auto-called by send_message)

Behavior:

  • Auto-binds on first Result (secure by default)
  • Returns Ok(()) if sessions match or either is None (early in conversation)
  • Returns Err(ClaudeError::SessionMismatch) if bound ≠ current

Introspection

use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ClaudeAgentOptions::default();
    let client = ClaudeSDKClient::new(options, None).await?;

    // Session information (available after init message)
    if let Some(info) = client.session_info() {
        println!("Model: {:?}", info.model);
        println!("Tools: {:?}", info.tool_names());

        for server in &info.mcp_servers {
            println!("MCP: {} ({})", server.name, server.status);
        }
    }

    // Static model list
    for model in ClaudeSDKClient::supported_models() {
        println!("{}: {:?}", model.id, model.name);
    }

    // Slash commands
    for cmd in client.supported_commands() {
        println!("/{} - {}", cmd.name, cmd.description);
    }

    Ok(())
}

Types

ClaudeAgentOptions

Configuration options for the SDK. Uses the builder pattern via typed-builder.

let options = ClaudeAgentOptions::builder()
    .system_prompt("You are a helpful assistant")
    .model("sonnet")
    .max_turns(10)
    .build();
Property Type Default Description
allowed_tools Vec<ToolName> [] Tools Claude is allowed to use
system_prompt Option<SystemPrompt> None System prompt configuration
mcp_servers McpServers McpServers::None MCP server configurations
permission_mode Option<PermissionMode> None Permission mode for tool execution
continue_conversation bool false Continue from previous conversation
resume Option<SessionId> None Session ID to resume
max_turns Option<u32> None Maximum conversation turns
disallowed_tools Vec<ToolName> [] Tools Claude cannot use
model Option<String> None Claude model to use
permission_prompt_tool_name Option<String> None MCP tool name for permission prompts
cwd Option<PathBuf> None Working directory
settings Option<PathBuf> None Path to settings file
add_dirs Vec<PathBuf> [] Additional directories Claude can access
env HashMap<String, String> {} Environment variables (dangerous vars blocked, see Security)
extra_args HashMap<String, Option<String>> {} Additional CLI arguments (allowlist enforced, see Security)
max_buffer_size Option<usize> None Maximum buffer size (default: 1MB)
read_timeout_secs Option<u64> None Read timeout in seconds (default: 120)
can_use_tool Option<CanUseToolCallback> None Custom permission callback
hooks Option<HashMap<HookEvent, Vec<HookMatcher>>> None Hook configurations
user Option<String> None User identifier
include_partial_messages bool false Include partial message events
fork_session bool false Fork session when resuming
session_id Option<String> None Custom session ID (must be valid UUID)
agents Option<HashMap<String, AgentDefinition>> None Custom agent definitions
setting_sources Option<Vec<SettingSource>> None Settings sources to load
max_budget_usd Option<f64> None Maximum budget in USD
max_thinking_tokens Option<u32> None Maximum tokens for thinking
fallback_model Option<String> None Model to use if primary fails
output_format Option<OutputFormat> None Structured output format
sandbox Option<SandboxSettings> None Sandbox configuration
plugins Option<Vec<SdkPluginConfig>> None Plugins to load
betas Option<Vec<SdkBeta>> None Beta features to enable
strict_mcp_config bool false Enforce strict MCP validation
resume_session_at Option<String> None Resume at specific message UUID
allow_dangerously_skip_permissions bool false Allow bypassing permissions
path_to_claude_code_executable Option<PathBuf> None Custom CLI path
stderr Option<StderrCallback> None Callback for stderr output
tools Option<ToolsConfig> None Tools configuration

SystemPrompt

System prompt configuration.

pub enum SystemPrompt {
    String(String),
    Preset(SystemPromptPreset),
}

SystemPromptPreset

pub struct SystemPromptPreset {
    pub prompt_type: String,  // Always "preset"
    pub preset: String,       // e.g., "claude_code"
    pub append: Option<String>,
}

ToolsConfig

Tools configuration - list or preset.

pub enum ToolsConfig {
    List(Vec<ToolName>),
    Preset(ToolsPreset),
}
// Use Claude Code's default tools
let config = ToolsConfig::claude_code_preset();

// Or specify explicit tools
let config = ToolsConfig::from_list(vec![
    ToolName::from("Read"),
    ToolName::from("Write"),
]);

OutputFormat

Structured output format configuration.

pub struct OutputFormat {
    pub format_type: String,        // Always "json_schema"
    pub schema: serde_json::Value,
}
let format = OutputFormat::json_schema(serde_json::json!({
    "type": "object",
    "properties": {
        "answer": { "type": "string" }
    }
}));

SandboxSettings

Configuration for command sandboxing.

pub struct SandboxSettings {
    pub enabled: Option<bool>,
    pub auto_allow_bash_if_sandboxed: Option<bool>,
    pub excluded_commands: Option<Vec<String>>,
    pub allow_unsandboxed_commands: Option<bool>,
    pub network: Option<NetworkSandboxSettings>,
    pub ignore_violations: Option<SandboxIgnoreViolations>,
    pub enable_weaker_nested_sandbox: Option<bool>,
}
Property Type Default Description
enabled Option<bool> None Enable sandbox mode
auto_allow_bash_if_sandboxed Option<bool> None Auto-approve bash when sandboxed
excluded_commands Option<Vec<String>> None Commands that bypass sandbox
allow_unsandboxed_commands Option<bool> None Allow model to request unsandboxed execution
network Option<NetworkSandboxSettings> None Network restrictions
ignore_violations Option<SandboxIgnoreViolations> None Violations to ignore
enable_weaker_nested_sandbox Option<bool> None Weaker nested sandbox for compatibility

NetworkSandboxSettings

Network-specific sandbox configuration.

pub struct NetworkSandboxSettings {
    pub allow_local_binding: Option<bool>,
    pub allow_unix_sockets: Option<Vec<String>>,
    pub allow_all_unix_sockets: Option<bool>,
    pub http_proxy_port: Option<u16>,
    pub socks_proxy_port: Option<u16>,
}

SandboxIgnoreViolations

Violations to ignore in sandbox mode.

pub struct SandboxIgnoreViolations {
    pub file: Option<Vec<String>>,
    pub network: Option<Vec<String>>,
}

SdkPluginConfig

Plugin configuration.

pub enum SdkPluginConfig {
    Local { path: String },
}

SdkBeta

Available beta features.

pub enum SdkBeta {
    Context1M,  // 1 million token context window
}
Value Description Compatible Models
Context1M 1 million token context window Claude Sonnet 4, Claude Sonnet 4.5

AgentDefinition

Configuration for custom subagents.

pub struct AgentDefinition {
    pub description: String,
    pub prompt: String,
    pub tools: Option<Vec<String>>,
    pub model: Option<String>,
}
Field Required Description
description Yes When to use this agent
prompt Yes Agent's system prompt
tools No Allowed tools (inherits all if omitted)
model No Model override ("sonnet", "opus", "haiku")

SettingSource

Configuration sources to load.

pub enum SettingSource {
    User,     // ~/.claude/settings.json
    Project,  // .claude/settings.json
    Local,    // .claude/settings.local.json
}

When setting_sources is omitted, no filesystem settings are loaded.

// Load project settings only
let options = ClaudeAgentOptions::builder()
    .setting_sources(vec![SettingSource::Project])
    .build();

// Load all settings
let options = ClaudeAgentOptions::builder()
    .setting_sources(vec![
        SettingSource::User,
        SettingSource::Project,
        SettingSource::Local,
    ])
    .build();

PermissionMode

Permission modes for tool execution.

pub enum PermissionMode {
    Default,           // Prompt for dangerous tools
    AcceptEdits,       // Auto-accept file edits
    Plan,              // Plan mode - no execution
    BypassPermissions, // Allow all (requires allow_dangerously_skip_permissions)
}

CanUseToolCallback

Type alias for permission callback.

pub type CanUseToolCallback = Arc<dyn PermissionCallback>;

PermissionResult

Result of a permission check.

pub enum PermissionResult {
    Allow(PermissionResultAllow),
    Deny(PermissionResultDeny),
}

PermissionResultAllow

pub struct PermissionResultAllow {
    pub updated_input: Option<serde_json::Value>,
    pub updated_permissions: Option<Vec<PermissionUpdate>>,
}

PermissionResultDeny

pub struct PermissionResultDeny {
    pub message: String,
    pub interrupt: bool,
}

McpServerConfig

MCP server configuration.

pub enum McpServerConfig {
    Stdio(McpStdioServerConfig),
    Sse(McpSseServerConfig),
    Http(McpHttpServerConfig),
    Sdk(SdkMcpServerConfig),
}

McpStdioServerConfig

pub struct McpStdioServerConfig {
    pub server_type: Option<String>,  // "stdio"
    pub command: String,
    pub args: Option<Vec<String>>,
    pub env: Option<HashMap<String, String>>,
}

McpSseServerConfig

pub struct McpSseServerConfig {
    pub server_type: String,  // "sse"
    pub url: String,
    pub headers: Option<HashMap<String, String>>,
}

McpHttpServerConfig

pub struct McpHttpServerConfig {
    pub server_type: String,  // "http"
    pub url: String,
    pub headers: Option<HashMap<String, String>>,
}

SdkMcpServerConfig

pub struct SdkMcpServerConfig {
    pub name: String,
    pub version: Option<String>,
}

McpServers

MCP servers container.

pub enum McpServers {
    None,
    Dict(HashMap<String, McpServerConfig>),
    Path(PathBuf),
}
use std::collections::HashMap;

let mut servers = HashMap::new();
servers.insert("my-server".to_string(), McpServerConfig::Stdio(
    McpStdioServerConfig {
        server_type: Some("stdio".to_string()),
        command: "npx".to_string(),
        args: Some(vec!["-y".to_string(), "@my/mcp-server".to_string()]),
        env: None,
    }
));

let options = ClaudeAgentOptions::builder()
    .mcp_servers(McpServers::Dict(servers))
    .build();

Message Types

Message

Message types returned from Claude Code.

pub enum Message {
    User { ... },
    Assistant { ... },
    System { ... },
    Result { ... },
    StreamEvent { ... },
}

Message::User

User message sent to Claude.

Field Type Description
parent_tool_use_id Option<String> Parent tool use ID for nested conversations
message UserMessageContent Message content
session_id Option<SessionId> Session identifier

Message::Assistant

Response from Claude.

Field Type Description
parent_tool_use_id Option<String> Parent tool use ID for nested conversations
message AssistantMessageContent Message content
session_id Option<SessionId> Session identifier

Message::System

System messages (init, status updates).

Field Type Description
subtype String Message subtype (e.g., "init")
data serde_json::Value System message data

Message::Result

Conversation result with metrics.

Field Type Description
subtype String Result subtype ("success", "error_max_turns", "error_during_execution")
duration_ms u64 Total duration in milliseconds
duration_api_ms u64 API call duration in milliseconds
is_error bool Whether this is an error result
num_turns u32 Number of conversation turns
session_id SessionId Session identifier
total_cost_usd Option<f64> Total cost in USD
usage Option<serde_json::Value> Aggregate token usage
result Option<String> Result message (for success)
model_usage HashMap<String, ModelUsage> Per-model usage statistics
permission_denials Vec<SDKPermissionDenial> Denied tool uses
structured_output Option<serde_json::Value> Structured output (when output_format specified)
errors Vec<String> Error messages

Message::StreamEvent

Partial message events (requires include_partial_messages: true).

Field Type Description
uuid String Event UUID
session_id SessionId Session identifier
event serde_json::Value Raw stream event data
parent_tool_use_id Option<String> Parent tool use ID

ContentBlock

Content blocks in assistant messages.

pub enum ContentBlock {
    Text { text: String },
    Thinking { thinking: String, signature: String },
    ToolUse { id: String, name: String, input: serde_json::Value },
    ToolResult { tool_use_id: String, content: Option<ContentValue>, is_error: Option<bool> },
}
Variant Description
Text Text content from Claude
Thinking Extended thinking block (when max_thinking_tokens set)
ToolUse Tool invocation request
ToolResult Result from tool execution

ContentValue

Content value for tool results.

pub enum ContentValue {
    String(String),
    Blocks(Vec<serde_json::Value>),
}

AskUserQuestion Tool Types

Types for the AskUserQuestion tool which allows agents to ask users multiple-choice questions.

QuestionOption

pub struct QuestionOption {
    pub label: String,
    pub description: String,
}
Field Type Description
label String Display text for this option
description String Explanation of what this option means

QuestionSpec

pub struct QuestionSpec {
    pub question: String,
    pub header: String,
    pub options: Vec<QuestionOption>,
    pub multi_select: bool,
}
Field Type Description
question String The complete question to ask the user
header String Short label displayed as a chip/tag (max 12 chars)
options Vec<QuestionOption> Available choices (2-4 options)
multi_select bool Whether multiple options can be selected

AskUserQuestionInput

pub struct AskUserQuestionInput {
    pub questions: Vec<QuestionSpec>,
    pub answers: Option<HashMap<String, String>>,
}
Field Type Description
questions Vec<QuestionSpec> Questions to ask (1-4 questions)
answers Option<HashMap<String, String>> User answers (populated in tool result)

AskUserQuestionOutput

pub struct AskUserQuestionOutput {
    pub answers: HashMap<String, String>,
}
Field Type Description
answers HashMap<String, String> User's answers keyed by question header

Example: Parsing Tool Input

use anthropic_agent_sdk::types::{AskUserQuestionInput, ContentBlock};

// When you receive a ToolUse content block
if let ContentBlock::ToolUse { name, input, .. } = block {
    if name == "AskUserQuestion" {
        let question_input: AskUserQuestionInput = serde_json::from_value(input)?;
        for q in &question_input.questions {
            println!("Q: {} (header: {})", q.question, q.header);
            for opt in &q.options {
                println!("  - {}: {}", opt.label, opt.description);
            }
        }
    }
}

UserMessageContent

User message content wrapper.

pub struct UserMessageContent {
    pub role: String,  // Always "user"
    pub content: Option<UserContent>,
}

UserContent

User content variants.

pub enum UserContent {
    String(String),
    Blocks(Vec<ContentBlock>),
}

AssistantMessageContent

Assistant message content.

pub struct AssistantMessageContent {
    pub model: String,
    pub content: Vec<ContentBlock>,
}

SDKPermissionDenial

Information about denied tool use.

pub struct SDKPermissionDenial {
    pub tool_name: String,
    pub tool_use_id: String,
    pub tool_input: serde_json::Value,
}

ModelUsage

Per-model usage statistics from Result messages.

pub struct ModelUsage {
    pub input_tokens: u64,
    pub output_tokens: u64,
    pub cache_read_input_tokens: u64,
    pub cache_creation_input_tokens: u64,
    pub web_search_requests: u64,
    pub cost_usd: f64,
    pub context_window: u64,
}
Method Returns Description
total_tokens() u64 Input + output tokens
effective_input_tokens() u64 Input + cache read + cache creation tokens

Working with Tools

Claude uses tools to interact with the filesystem, run commands, and perform actions. The SDK provides options, hooks, and callbacks to control tool usage.

Tool Flow

  1. Claude decides to use a tool → ContentBlock::ToolUse appears in message stream
  2. (Optional) PreToolUse hook fires → can block or modify input
  3. (Optional) PermissionCallback fires → can allow/deny
  4. CLI executes the tool
  5. (Optional) PostToolUse hook fires → can observe result
  6. ContentBlock::ToolResult appears in message stream

Restricting Tools via Options

Use allowed_tools and disallowed_tools to control which tools Claude can use:

// Allow only read operations
let options = ClaudeAgentOptions::builder()
    .allowed_tools(vec!["Read".into(), "Glob".into(), "Grep".into()])
    .build();

// Allow all except dangerous tools
let options = ClaudeAgentOptions::builder()
    .disallowed_tools(vec!["Bash".into(), "Write".into()])
    .build();
Option Type Description
allowed_tools Vec<ToolName> Whitelist - only these tools can be used
disallowed_tools Vec<ToolName> Blacklist - these tools cannot be used

When both are specified, allowed_tools takes precedence.

Detecting Tool Use

while let Some(msg) = client.next_message().await? {
    if let Message::Assistant { content, .. } = &msg {
        for block in content {
            match block {
                ContentBlock::ToolUse { id, name, input } => {
                    println!("Tool: {} ({})", name, id);
                    println!("Input: {}", input);
                }
                ContentBlock::ToolResult { tool_use_id, content, is_error } => {
                    println!("Result for {}: {:?}", tool_use_id, content);
                    if is_error.unwrap_or(false) {
                        println!("Tool execution failed");
                    }
                }
                _ => {}
            }
        }
    }
}

Intercepting Tool Use

Use hooks to intercept, modify, or block tool execution:

// Block all Bash commands containing "rm"
struct BlockDangerousBash;

#[async_trait]
impl HookCallback for BlockDangerousBash {
    async fn call(
        &self,
        input: serde_json::Value,
        _tool_use_id: Option<String>,
        _ctx: HookContext,
    ) -> Result<HookOutput> {
        if let Some(cmd) = input.get("command").and_then(|c| c.as_str()) {
            if cmd.contains("rm ") {
                return Ok(HookOutput {
                    decision: Some(HookDecision::Block),
                    reason: Some("Dangerous command blocked".into()),
                    ..Default::default()
                });
            }
        }
        Ok(HookOutput::default())
    }
}

// Register for Bash tool only
let hook = HookMatcher::builder()
    .event(HookEvent::PreToolUse)
    .tool_name("Bash")
    .build();

hook_manager.register(hook, Arc::new(BlockDangerousBash));

Dynamic Permission Control

Use PermissionCallback for runtime decisions:

struct AuditingPermissionHandler;

#[async_trait]
impl PermissionCallback for AuditingPermissionHandler {
    async fn call(
        &self,
        tool_name: String,
        input: serde_json::Value,
        _ctx: ToolPermissionContext,
    ) -> Result<PermissionResult> {
        // Log all tool usage
        println!("[AUDIT] Tool: {}, Input: {}", tool_name, input);

        // Allow read tools, deny write tools
        if matches!(tool_name.as_str(), "Read" | "Glob" | "Grep") {
            Ok(PermissionResult::Allow(PermissionResultAllow {
                updated_input: None,
                updated_permissions: None,
            }))
        } else {
            Ok(PermissionResult::Deny(PermissionResultDeny {
                message: format!("Tool '{}' not allowed", tool_name),
                interrupt: false,
            }))
        }
    }
}

let options = ClaudeAgentOptions::builder()
    .can_use_tool(Arc::new(AuditingPermissionHandler))
    .build();

See Hook Types for PreToolUse/PostToolUse details. See Permission Types for PermissionCallback trait. See Claude Code CLI Reference for tool schemas.

Hook Types

HookEvent

Hook event types for intercepting agent actions.

pub enum HookEvent {
    PreToolUse,        // Before a tool is used
    PostToolUse,       // After a tool is used
    PostToolUseFailure,// After a tool use fails
    Notification,      // When a notification is received
    UserPromptSubmit,  // When user submits a prompt
    SessionStart,      // When a session starts
    SessionEnd,        // When a session ends
    Stop,              // When conversation stops
    SubagentStart,     // When a subagent starts
    SubagentStop,      // When a subagent stops
    PreCompact,        // Before compacting the conversation
    PermissionRequest, // When a permission is requested
}

HookCallback

Trait for hook callbacks.

#[async_trait]
pub trait HookCallback: Send + Sync {
    async fn call(
        &self,
        input: serde_json::Value,
        tool_use_id: Option<String>,
        context: HookContext,
    ) -> Result<HookOutput>;
}

Implementing with a struct

use anthropic_agent_sdk::callbacks::HookCallback;
use anthropic_agent_sdk::types::{HookOutput, HookContext};
use async_trait::async_trait;

struct LoggingHook;

#[async_trait]
impl HookCallback for LoggingHook {
    async fn call(
        &self,
        input: serde_json::Value,
        tool_use_id: Option<String>,
        _context: HookContext,
    ) -> Result<HookOutput> {
        println!("Tool called: {:?}", tool_use_id);
        Ok(HookOutput::default())
    }
}

Implementing with a closure

use anthropic_agent_sdk::hooks::HookManager;

let hook = HookManager::callback(|input, tool_name, ctx| async move {
    println!("Tool: {:?}, Session: {:?}", tool_name, ctx.session_id);
    Ok(HookOutput::default())
});

HookMatcher

Hook configuration with pattern matching.

pub struct HookMatcher {
    pub matcher: Option<String>,
    pub hooks: Vec<Arc<dyn HookCallback>>,
    pub timeout: Option<Duration>,  // Default: 60 seconds
}
Field Type Description
matcher Option<String> Pattern (None for all, "Bash" for specific, "Write|Edit" for multiple)
hooks Vec<Arc<dyn HookCallback>> Hook callbacks to invoke
timeout Option<Duration> Timeout per hook (default: 60 seconds)

HookMatcherBuilder

Builder for creating HookMatcher instances.

use anthropic_agent_sdk::hooks::HookMatcherBuilder;
use std::time::Duration;

let matcher = HookMatcherBuilder::new(Some("Bash"))
    .add_hook(my_hook)
    .timeout(Duration::from_secs(30))
    .build();
Method Description
new(pattern) Create builder with optional pattern
add_hook(hook) Add a hook callback
timeout(duration) Set timeout for hooks
build() Build the HookMatcher

HookContext

Context passed to hook callbacks.

pub struct HookContext {
    pub session_id: Option<String>,
    pub cwd: Option<String>,
    pub cancellation_token: Option<CancellationToken>,
}
Field Type Description
session_id Option<String> Session identifier
cwd Option<String> Current working directory
cancellation_token Option<CancellationToken> Token for cancellation support
Method Returns Description
is_cancelled() bool Check if cancellation was requested

HookOutput

Output from a hook callback.

pub struct HookOutput {
    pub decision: Option<HookDecision>,
    pub system_message: Option<String>,
    pub hook_specific_output: Option<serde_json::Value>,
}
Field Type Description
decision Option<HookDecision> Block or allow the action
system_message Option<String> Message to add to system context
hook_specific_output Option<serde_json::Value> Hook-specific data

HookDecision

Decision for hook callbacks.

pub enum HookDecision {
    Block,  // Block the action
}

Omit decision or set to None to allow the action.

Hook Input Types

All hook inputs include BaseHookInput:

pub struct BaseHookInput {
    pub session_id: String,
    pub transcript_path: String,
    pub cwd: String,
    pub permission_mode: Option<String>,
}
Hook Event Input Type Additional Fields
PreToolUse PreToolUseHookInput tool_name, tool_input
PostToolUse PostToolUseHookInput tool_name, tool_input, tool_response, tool_use_id?
PostToolUseFailure PostToolUseFailureHookInput tool_name, tool_input, tool_use_id?, error, is_interrupt?
SessionStart SessionStartHookInput source: SessionStartSource
SessionEnd SessionEndHookInput reason: SessionEndReason
SubagentStart SubagentStartHookInput agent_id, agent_type
SubagentStop SubagentStopHookInput agent_id?, agent_transcript_path?, stop_hook_active
UserPromptSubmit UserPromptSubmitHookInput prompt
Notification NotificationHookInput message, title?
PreCompact PreCompactHookInput trigger: CompactTrigger, custom_instructions?
PermissionRequest PermissionRequestHookInput tool_name, tool_input, permission_suggestions?
Stop StopHookInput stop_hook_active

Note: Fields marked with ? are optional (may be absent in CLI 2.0.75+).

SessionStartSource

pub enum SessionStartSource {
    Startup,  // Fresh startup
    Resume,   // Resumed session
    Clear,    // After clear
    Compact,  // After compact
}

SessionEndReason

pub enum SessionEndReason {
    Clear,           // Session cleared
    Logout,          // User logged out
    PromptInputExit, // User exited prompt input
    Other,           // Other reason
}

CompactTrigger

pub enum CompactTrigger {
    Manual,  // Manually triggered
    Auto,    // Automatically triggered
}

HookManager

Manager for registering and invoking hooks.

use anthropic_agent_sdk::hooks::{HookManager, HookMatcherBuilder};
use anthropic_agent_sdk::types::HookEvent;

let mut manager = HookManager::new();

// Register a hook for PreToolUse events on Bash
let hook = HookManager::callback(|input, tool_name, ctx| async move {
    println!("Bash called: {:?}", input);
    Ok(HookOutput::default())
});

manager.register_for_event(
    HookEvent::PreToolUse,
    HookMatcherBuilder::new(Some("Bash")).add_hook(hook).build(),
);
Method Description
new() Create a new hook manager
from_hooks_config(config) Create from a hooks configuration HashMap
register_for_event(event, matcher) Register a matcher for an event
has_hooks_for(event) Check if hooks are registered for an event
invoke(event, data, tool_name, ctx) Invoke hooks for an event
set_session_context(session_id, cwd) Set session context
set_cancellation_token(token) Set cancellation token
build_context() Build a HookContext with current session info
callback(f) Create a hook callback from a closure

Permission Types

PermissionCallback

Trait for permission callbacks.

#[async_trait]
pub trait PermissionCallback: Send + Sync {
    async fn call(
        &self,
        tool_name: String,
        input: serde_json::Value,
        context: ToolPermissionContext,
    ) -> Result<PermissionResult>;
}

Implementing with a struct

use anthropic_agent_sdk::callbacks::PermissionCallback;
use anthropic_agent_sdk::types::{PermissionResult, PermissionResultAllow, PermissionResultDeny, ToolPermissionContext};
use async_trait::async_trait;

struct AllowReadOnly;

#[async_trait]
impl PermissionCallback for AllowReadOnly {
    async fn call(
        &self,
        tool_name: String,
        _input: serde_json::Value,
        _context: ToolPermissionContext,
    ) -> Result<PermissionResult> {
        if tool_name == "Read" || tool_name == "Glob" {
            Ok(PermissionResult::Allow(PermissionResultAllow {
                updated_input: None,
                updated_permissions: None,
            }))
        } else {
            Ok(PermissionResult::Deny(PermissionResultDeny {
                message: "Only read operations allowed".to_string(),
                interrupt: false,
            }))
        }
    }
}

Implementing with a closure

use anthropic_agent_sdk::permissions::PermissionManager;

let callback = PermissionManager::callback(|tool_name, input, ctx| async move {
    // ctx.suggestions contains permission suggestions from CLI
    // ctx.is_cancelled() checks if operation was cancelled
    Ok(PermissionResult::Allow(PermissionResultAllow::default()))
});

ToolPermissionContext

Context passed to permission callbacks.

pub struct ToolPermissionContext {
    pub suggestions: Vec<PermissionUpdate>,
    pub cancellation_token: Option<CancellationToken>,
}
Field Type Description
suggestions Vec<PermissionUpdate> Permission suggestions from CLI
cancellation_token Option<CancellationToken> Token for cancellation support
Method Returns Description
new(suggestions) Self Create with suggestions
with_cancellation(suggestions, token) Self Create with cancellation token
is_cancelled() bool Check if cancellation was requested

PermissionUpdate

Permission update operations.

pub enum PermissionUpdate {
    AddRules { rules: Option<Vec<PermissionRuleValue>>, destination: Option<PermissionUpdateDestination> },
    ReplaceRules { rules: Option<Vec<PermissionRuleValue>>, destination: Option<PermissionUpdateDestination> },
    RemoveRules { rules: Option<Vec<PermissionRuleValue>>, destination: Option<PermissionUpdateDestination> },
    SetMode { mode: PermissionMode, destination: Option<PermissionUpdateDestination> },
    AddDirectories { directories: Option<Vec<String>>, destination: Option<PermissionUpdateDestination> },
    RemoveDirectories { directories: Option<Vec<String>>, destination: Option<PermissionUpdateDestination> },
}

PermissionUpdateDestination

Where to save permission updates.

pub enum PermissionUpdateDestination {
    UserSettings,    // ~/.claude/settings.json
    ProjectSettings, // .claude/settings.json
    LocalSettings,   // .claude/settings.local.json
    Session,         // Session only (temporary)
}

PermissionBehavior

Permission behavior options.

pub enum PermissionBehavior {
    Allow,
    Deny,
    Ask,
}

PermissionRuleValue

Permission rule definition.

pub struct PermissionRuleValue {
    pub tool_name: String,
    pub rule_content: Option<String>,
}

PermissionRequest

Permission request from CLI.

pub struct PermissionRequest {
    pub tool_name: ToolName,
    pub tool_input: serde_json::Value,
    pub context: ToolPermissionContext,
}

PermissionManager

Manager for permission callbacks.

use anthropic_agent_sdk::permissions::PermissionManager;
use anthropic_agent_sdk::types::{PermissionResult, PermissionResultAllow, ToolName};

let mut manager = PermissionManager::new();

// Set allowed tools
manager.set_allowed_tools(Some(vec![
    ToolName::from("Read"),
    ToolName::from("Glob"),
]));

// Set disallowed tools
manager.set_disallowed_tools(vec![ToolName::from("Bash")]);

// Set custom callback
let callback = PermissionManager::callback(|tool_name, input, ctx| async move {
    Ok(PermissionResult::Allow(PermissionResultAllow::default()))
});
manager.set_callback(callback);
Method Description
new() Create a new permission manager
set_callback(callback) Set permission callback
set_allowed_tools(tools) Set allowed tools (None = all allowed)
set_disallowed_tools(tools) Set disallowed tools
can_use_tool(name, input, ctx) Check if tool can be used
callback(f) Create a callback from a closure

Introspection Types

SessionInfo

Session information from init message.

pub struct SessionInfo {
    pub model: Option<String>,
    pub tools: Vec<ToolInfo>,
    pub cwd: Option<String>,
    pub mcp_servers: Vec<McpServerStatus>,
    pub extra: HashMap<String, serde_json::Value>,
}
Method Returns Description
from_init_data(data) Self Create from init message data
tool_names() Vec<&str> Get tool names
has_tool(name) bool Check if tool is available
mcp_server(name) Option<&McpServerStatus> Get MCP server by name
has_mcp_errors() bool Check if any MCP server has errors
permission_mode() Option<&str> Get current permission mode
is_plan_mode() bool Check if session is in plan mode

ToolInfo

Tool information.

pub struct ToolInfo {
    pub name: String,
    pub description: Option<String>,
    pub input_schema: Option<serde_json::Value>,
}

McpServerStatus

MCP server connection status.

pub struct McpServerStatus {
    pub name: String,
    pub status: String,  // "connected", "failed", "needs-auth", "pending"
    pub error: Option<String>,
    pub tools: Vec<String>,
}
Method Returns Description
is_connected() bool Check if server is connected

ModelInfo

Claude model information.

pub struct ModelInfo {
    pub id: String,
    pub name: Option<String>,
    pub max_tokens: Option<u32>,
    pub supports_thinking: bool,
}
// Get known models
for model in ModelInfo::known_models() {
    println!("{}: {:?}", model.id, model.name);
}

SlashCommand

Custom slash command information.

pub struct SlashCommand {
    pub name: String,
    pub description: String,
    pub argument_hint: String,
}

AccountInfo

OAuth account information.

pub struct AccountInfo {
    pub email: Option<String>,
    pub account_id: Option<String>,
    pub is_oauth: bool,
    pub organization_id: Option<String>,
}

Error Types

ClaudeError

Main error type for the SDK.

pub enum ClaudeError {
    CliNotFound(String),
    Connection(String),
    Process { message: String, exit_code: i32, stderr: Option<String> },
    JsonDecode(serde_json::Error),
    JsonEncode(String),
    MessageParse { message: String, data: Option<serde_json::Value> },
    Transport(String),
    ControlProtocol(String),
    Hook(String),
    Mcp(String),
    Io(std::io::Error),
    Timeout(String),
    InvalidConfig(String),
    CliVersionTooOld { found: String, minimum: String },
    ControlTimeout { timeout_secs: u64, request_type: String },
    NotConnected,
    AlreadyConnected,
    AuthenticationError(String),
    NetworkError(String),
}
Variant Description
CliNotFound Claude Code CLI not found
Connection Connection error
Process Process execution error with exit code
JsonDecode JSON parsing error
JsonEncode JSON encoding error
MessageParse Message parsing error
Transport Transport layer error
ControlProtocol Control protocol error
Hook Hook execution error
Mcp MCP error
Io I/O error
Timeout Timeout error
InvalidConfig Invalid configuration
CliVersionTooOld CLI version below minimum
ControlTimeout Control request timed out
NotConnected Client not connected
AlreadyConnected Client already connected
AuthenticationError Authentication error
NetworkError Network error

Helper Constructors

ClaudeError::cli_not_found()
ClaudeError::connection("Failed to connect")
ClaudeError::process("Failed", 1, Some("stderr output".to_string()))
ClaudeError::message_parse("Invalid message", Some(data))
ClaudeError::transport("Connection lost")
ClaudeError::hook("Hook failed")
ClaudeError::mcp("MCP server error")
ClaudeError::timeout("Request timed out")
ClaudeError::invalid_config("Missing required field")
ClaudeError::cli_version_too_old("1.0.0", "2.0.0")
ClaudeError::not_connected()
ClaudeError::authentication("Invalid API key")
ClaudeError::network("DNS resolution failed")

Result<T>

Type alias for SDK results.

pub type Result<T> = std::result::Result<T, ClaudeError>;

MCP Configuration

MCP (Model Context Protocol) configuration for external and in-process servers.

External Server Configuration

Already documented in Types > McpServerConfig.

In-Process MCP Servers (feature: rmcp)

The rmcp feature enables creating in-process MCP servers with custom tools.

Enable in Cargo.toml:

[dependencies]
anthropic-agent-sdk = { version = "0.2", features = ["rmcp"] }

Re-exported Macros

Macro Description
#[tool_router] Mark an impl block as containing tool definitions
#[tool] Mark a method as an MCP tool
#[tool_handler] Implement ServerHandler for a type

Re-exported Types

Type Description
ServerHandler Trait for MCP server implementations
ToolRouter<T> Router for tool invocations
Parameters<T> Typed parameter wrapper
Json JSON response wrapper
CallToolResult Tool result type
Tool Tool definition
Content Content type for responses
ServerCapabilities Server capability declaration
ServerInfo Server information
McpError Error type (alias for ErrorData)
schemars Re-exported for JsonSchema derivation

SdkMcpServer

Marker trait automatically implemented for any ServerHandler.

pub trait SdkMcpServer: ServerHandler {}

Example: Creating an MCP Server

use anthropic_agent_sdk::mcp::{
    tool, tool_router, tool_handler,
    Parameters, CallToolResult, Content, ToolRouter, ServerHandler,
};
use schemars::JsonSchema;
use serde::Deserialize;

#[derive(Deserialize, JsonSchema)]
struct GreetParams {
    name: String,
}

#[derive(Clone)]
struct Greeter {
    tool_router: ToolRouter<Self>,
}

#[tool_router]
impl Greeter {
    fn new() -> Self {
        Self { tool_router: Self::tool_router() }
    }

    #[tool(description = "Greet someone by name")]
    async fn greet(&self, params: Parameters<GreetParams>) -> Result<CallToolResult, String> {
        Ok(CallToolResult::success(vec![Content::text(
            format!("Hello, {}!", params.name)
        )]))
    }
}

#[tool_handler]
impl ServerHandler for Greeter {
    fn get_info(&self) -> rmcp::model::ServerInfo {
        rmcp::model::ServerInfo::new("greeter", "1.0.0")
    }
}

Type-Safe Identifiers

Newtype wrappers for type safety. All implement Display, AsRef<str>, Deref<Target=str>, and Borrow<str>.

SessionId

Session identifier.

pub struct SessionId(String);
Method Returns Description
new(id) Self Create from string
as_str() &str Get as string slice

ToolName

Tool name identifier.

pub struct ToolName(String);
Method Returns Description
new(name) Self Create from string
as_str() &str Get as string slice

RequestId

Control protocol request identifier.

pub struct RequestId(String);
Method Returns Description
new(id) Self Create from string
as_str() &str Get as string slice

Utility Functions

Safe String Handling (utils module)

Safe UTF-8 string truncation utilities that prevent panics when handling multi-byte characters.

use anthropic_agent_sdk::utils::{safe_truncate, truncate_for_display, safe_window};

safe_truncate()

Safely truncate a string at a UTF-8 character boundary.

pub fn safe_truncate(s: &str, max_bytes: usize) -> &str
Parameter Type Description
s &str The string to truncate
max_bytes usize Maximum number of bytes in the result

Returns a string slice that is at most max_bytes long and valid UTF-8.

// Emoji 🔍 is 4 bytes - truncating at byte 10 would cut it in half
let text = "Status: 🔍 Active";
let result = safe_truncate(text, 10);
assert_eq!(result, "Status: "); // Stops before the emoji

truncate_for_display()

Truncate a string with ellipsis for display.

pub fn truncate_for_display(s: &str, max_bytes: usize) -> String
Parameter Type Description
s &str The string to truncate
max_bytes usize Maximum number of bytes before adding ellipsis

Returns a String truncated with "..." appended if truncation occurred.

let text = "This is a long message";
let result = truncate_for_display(text, 10);
assert_eq!(result, "This is a ...");

safe_window()

Extract a safe substring window ending at a byte position.

pub fn safe_window(s: &str, end_byte: usize, window_size: usize) -> &str
Parameter Type Description
s &str The source string
end_byte usize The byte position to end at (clamped to string length)
window_size usize Maximum window size in bytes

Returns a string slice of at most window_size bytes ending at end_byte, respecting UTF-8 boundaries on both ends.

let code = "export class 🔍 Scanner";
let window = safe_window(code, 20, 10);
// Returns a safe slice without cutting multi-byte characters

check_claude_version()

Validate Claude Code CLI version.

pub async fn check_claude_version(cli_path: &Path) -> Result<String>

Returns the version string if the CLI is at least MIN_CLI_VERSION, otherwise returns ClaudeError::CliVersionTooOld.

MIN_CLI_VERSION

Minimum required CLI version.

pub const MIN_CLI_VERSION: &str = "1.0.0";

Security

The SDK implements strict security measures to prevent injection attacks:

Environment Variables

Dangerous environment variables are blocked (not silently filtered):

// These will cause an error
options.env.insert("LD_PRELOAD".to_string(), "/tmp/evil.so".to_string());
options.env.insert("NODE_OPTIONS".to_string(), "--require=/tmp/bad.js".to_string());

let client = ClaudeSDKClient::new(options, None).await;
// Error: "Dangerous environment variables detected: [LD_PRELOAD, NODE_OPTIONS]..."

Blocked variables: LD_PRELOAD, LD_LIBRARY_PATH, DYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH, PATH, NODE_OPTIONS, PYTHONPATH, PERL5LIB, RUBYLIB

CLI Arguments

Only allowlisted flags are permitted in extra_args:

// This will cause an error
options.extra_args.insert("dangerous-flag".to_string(), None);

let client = ClaudeSDKClient::new(options, None).await;
// Error: "Disallowed CLI flags in extra_args: [dangerous-flag]..."

Allowed flags: timeout, retries, log-level, cache-dir

Session Binding

Sessions are automatically bound on first Result message. See Session Binding.

For full security documentation, see SECURITY.md.


OAuth Authentication

The SDK provides OAuth 2.0 authentication with PKCE for Claude Max/Pro subscribers. This allows users to authenticate without API keys.

OAuthClient

OAuth client for Claude authentication.

use anthropic_agent_sdk::auth::{OAuthClient, TokenStorage};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create OAuth client with default configuration
    let client = OAuthClient::new()?;

    // Authenticate - tries cached token first, then OAuth flow
    let token = client.authenticate().await?;

    println!("Access token: {}...", &token.access_token[..20]);
    Ok(())
}

Constructor

impl OAuthClient {
    pub fn new() -> AuthResult<Self>
    pub fn builder() -> OAuthClientBuilder
}

Methods

Method Returns Description
authenticate() AuthResult<TokenInfo> Try cached token, refresh, or start OAuth flow
start_oauth_flow() AuthResult<TokenInfo> Start full OAuth authorization flow
config() &OAuthConfig Get OAuth configuration
storage() &TokenStorage Get token storage
logout() AuthResult<()> Delete cached token
is_authenticated() bool Check if valid token exists
current_token() Option<TokenInfo> Get current token without refresh

OAuthClientBuilder

Builder for custom OAuth configuration.

let client = OAuthClient::builder()
    .auto_open_browser(false)  // Disable auto browser open
    .storage(TokenStorage::with_path("custom/path/token.json".into()))
    .build();
Method Description
config(config) Set custom OAuth configuration
storage(storage) Set custom token storage
auto_open_browser(bool) Enable/disable automatic browser opening (default: true)
build() Build the OAuth client

OAuthConfig

OAuth endpoint configuration.

pub struct OAuthConfig {
    pub client_id: String,
    pub auth_url: String,
    pub token_url: String,
    pub redirect_uri: String,
    pub scopes: String,
}

Default configuration uses Claude Code's official OAuth endpoints.

TokenInfo

OAuth token information.

pub struct TokenInfo {
    pub access_token: String,
    pub refresh_token: Option<String>,
    pub token_type: String,
    pub scope: Option<String>,
    pub expires_at: Option<u64>,
}
Method Returns Description
is_expired() bool Check if token is expired (with 60s buffer)
authorization_header() String Get "Bearer {token}" header value
remaining_validity() Option<Duration> Get remaining validity duration

TokenStorage

Persistent token storage.

use anthropic_agent_sdk::auth::TokenStorage;

// Default path: platform-specific (macOS: ~/Library/Application Support/claude-sdk/)
let storage = TokenStorage::new();

// Custom path
let storage = TokenStorage::with_path("/custom/path/token.json".into());

// Operations
let token = storage.load()?;        // Load token
let token = storage.load_valid()?;  // Load only if not expired
storage.save(&token)?;              // Save token
storage.delete()?;                  // Delete token
Method Returns Description
new() Self Create with default path
with_path(path) Self Create with custom path
path() &PathBuf Get storage path
load() Result<TokenInfo> Load token from storage
load_valid() Result<TokenInfo> Load token only if not expired
save(token) Result<()> Save token to storage
delete() Result<()> Delete stored token
has_valid_token() bool Check if valid token exists

OAuthError

Errors that can occur during OAuth operations.

pub enum OAuthError {
    Http(String),
    TokenExchange(String),
    InvalidResponse(String),
    Cancelled,
    Storage(TokenError),
    Io(std::io::Error),
    Json(serde_json::Error),
    BrowserOpen(String),
    Reqwest(reqwest::Error),
}

TokenError

Errors that can occur during token operations.

pub enum TokenError {
    Expired,
    NotFound,
    Io(std::io::Error),
    Json(serde_json::Error),
}

OAuth Flow

  1. Check for cached valid token
  2. If expired, attempt refresh using refresh token
  3. If no valid token, start browser-based OAuth flow with PKCE
  4. User authorizes in browser, copies authorization code
  5. Exchange code for access token
  6. Cache token for future use

Example: Full OAuth Flow

use anthropic_agent_sdk::auth::OAuthClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = OAuthClient::new()?;

    // Check if already authenticated
    if client.is_authenticated() {
        let token = client.current_token().unwrap();
        if let Some(remaining) = token.remaining_validity() {
            println!("Token valid for: {:?}", remaining);
        }
    } else {
        // Start OAuth flow
        let token = client.authenticate().await?;
        println!("Authenticated! Scopes: {:?}", token.scope);
    }

    // Log out when done
    // client.logout()?;

    Ok(())
}

See also