# Agent SDK reference - Rust API reference for the Rust Agent SDK, including all functions, types, and traits. --- ## Installation Add to your `Cargo.toml`: ```toml [dependencies] anthropic-agent-sdk = "0.2" ``` With MCP SDK server support: ```toml [dependencies] anthropic-agent-sdk = { version = "0.2", features = ["rmcp"] } ``` ## Functions ### `query()` One-shot query function for stateless interactions. For multi-turn conversations, use [`ClaudeSDKClient`](#claudesdkclient) instead. ```rust pub async fn query( prompt: impl Into, options: Option, ) -> Result>> ``` #### Parameters | Parameter | Type | Description | | :-------- | :--------------------------- | :---------------------------------------------------------------------------- | | `prompt` | `impl Into` | The input prompt | | `options` | `Option` | Configuration options (defaults to `ClaudeAgentOptions::default()` if `None`) | #### Returns Returns a `Stream>` that yields messages from the conversation. #### Example ```rust use anthropic_agent_sdk::query; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { 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: ```rust use anthropic_agent_sdk::{query, ClaudeAgentOptions}; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { 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. ```rust pub struct ClaudeSDKClient { /* fields omitted */ } ``` #### Constructor ```rust impl ClaudeSDKClient { pub async fn new( options: ClaudeAgentOptions, cli_path: Option, ) -> Result } ``` | Parameter | Type | Description | | :--------- | :------------------- | :------------------------------------------------ | | `options` | `ClaudeAgentOptions` | Configuration options | | `cli_path` | `Option` | 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 ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message}; #[tokio::main] async fn main() -> Result<(), Box> { 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()` ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message}; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { 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 ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions}; use tokio::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { 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 ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions}; #[tokio::main] async fn main() -> Result<(), Box> { 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: ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message}; #[tokio::main] async fn main() -> Result<(), Box> { 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>` | 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` | 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: ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions, Message}; #[tokio::main] async fn main() -> Result<(), Box> { 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` | 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 ```rust use anthropic_agent_sdk::{ClaudeSDKClient, ClaudeAgentOptions}; #[tokio::main] async fn main() -> Result<(), Box> { 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`. ```rust let options = ClaudeAgentOptions::builder() .system_prompt("You are a helpful assistant") .model("sonnet") .max_turns(10) .build(); ``` | Property | Type | Default | Description | | :----------------------------------- | :--------------------------------------------- | :----------------- | :----------------------------------------------------------- | | `allowed_tools` | `Vec` | `[]` | Tools Claude is allowed to use | | `system_prompt` | `Option` | `None` | System prompt configuration | | `mcp_servers` | `McpServers` | `McpServers::None` | MCP server configurations | | `permission_mode` | `Option` | `None` | Permission mode for tool execution | | `continue_conversation` | `bool` | `false` | Continue from previous conversation | | `resume` | `Option` | `None` | Session ID to resume | | `max_turns` | `Option` | `None` | Maximum conversation turns | | `disallowed_tools` | `Vec` | `[]` | Tools Claude cannot use | | `model` | `Option` | `None` | Claude model to use | | `permission_prompt_tool_name` | `Option` | `None` | MCP tool name for permission prompts | | `cwd` | `Option` | `None` | Working directory | | `settings` | `Option` | `None` | Path to settings file | | `add_dirs` | `Vec` | `[]` | Additional directories Claude can access | | `env` | `HashMap` | `{}` | Environment variables (dangerous vars blocked, see Security) | | `extra_args` | `HashMap>` | `{}` | Additional CLI arguments (allowlist enforced, see Security) | | `max_buffer_size` | `Option` | `None` | Maximum buffer size (default: 1MB) | | `read_timeout_secs` | `Option` | `None` | Read timeout in seconds (default: 120) | | `can_use_tool` | `Option` | `None` | Custom permission callback | | `hooks` | `Option>>` | `None` | Hook configurations | | `user` | `Option` | `None` | User identifier | | `include_partial_messages` | `bool` | `false` | Include partial message events | | `fork_session` | `bool` | `false` | Fork session when resuming | | `session_id` | `Option` | `None` | Custom session ID (must be valid UUID) | | `agents` | `Option>` | `None` | Custom agent definitions | | `setting_sources` | `Option>` | `None` | Settings sources to load | | `max_budget_usd` | `Option` | `None` | Maximum budget in USD | | `max_thinking_tokens` | `Option` | `None` | Maximum tokens for thinking | | `fallback_model` | `Option` | `None` | Model to use if primary fails | | `output_format` | `Option` | `None` | Structured output format | | `sandbox` | `Option` | `None` | Sandbox configuration | | `plugins` | `Option>` | `None` | Plugins to load | | `betas` | `Option>` | `None` | Beta features to enable | | `strict_mcp_config` | `bool` | `false` | Enforce strict MCP validation | | `resume_session_at` | `Option` | `None` | Resume at specific message UUID | | `allow_dangerously_skip_permissions` | `bool` | `false` | Allow bypassing permissions | | `path_to_claude_code_executable` | `Option` | `None` | Custom CLI path | | `stderr` | `Option` | `None` | Callback for stderr output | | `tools` | `Option` | `None` | Tools configuration | ### `SystemPrompt` System prompt configuration. ```rust pub enum SystemPrompt { String(String), Preset(SystemPromptPreset), } ``` #### `SystemPromptPreset` ```rust pub struct SystemPromptPreset { pub prompt_type: String, // Always "preset" pub preset: String, // e.g., "claude_code" pub append: Option, } ``` ### `ToolsConfig` Tools configuration - list or preset. ```rust pub enum ToolsConfig { List(Vec), Preset(ToolsPreset), } ``` ```rust // 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. ```rust pub struct OutputFormat { pub format_type: String, // Always "json_schema" pub schema: serde_json::Value, } ``` ```rust let format = OutputFormat::json_schema(serde_json::json!({ "type": "object", "properties": { "answer": { "type": "string" } } })); ``` ### `SandboxSettings` Configuration for command sandboxing. ```rust pub struct SandboxSettings { pub enabled: Option, pub auto_allow_bash_if_sandboxed: Option, pub excluded_commands: Option>, pub allow_unsandboxed_commands: Option, pub network: Option, pub ignore_violations: Option, pub enable_weaker_nested_sandbox: Option, } ``` | Property | Type | Default | Description | | :----------------------------- | :-------------------------------- | :------ | :------------------------------------------- | | `enabled` | `Option` | `None` | Enable sandbox mode | | `auto_allow_bash_if_sandboxed` | `Option` | `None` | Auto-approve bash when sandboxed | | `excluded_commands` | `Option>` | `None` | Commands that bypass sandbox | | `allow_unsandboxed_commands` | `Option` | `None` | Allow model to request unsandboxed execution | | `network` | `Option` | `None` | Network restrictions | | `ignore_violations` | `Option` | `None` | Violations to ignore | | `enable_weaker_nested_sandbox` | `Option` | `None` | Weaker nested sandbox for compatibility | ### `NetworkSandboxSettings` Network-specific sandbox configuration. ```rust pub struct NetworkSandboxSettings { pub allow_local_binding: Option, pub allow_unix_sockets: Option>, pub allow_all_unix_sockets: Option, pub http_proxy_port: Option, pub socks_proxy_port: Option, } ``` ### `SandboxIgnoreViolations` Violations to ignore in sandbox mode. ```rust pub struct SandboxIgnoreViolations { pub file: Option>, pub network: Option>, } ``` ### `SdkPluginConfig` Plugin configuration. ```rust pub enum SdkPluginConfig { Local { path: String }, } ``` ### `SdkBeta` Available beta features. ```rust 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. ```rust pub struct AgentDefinition { pub description: String, pub prompt: String, pub tools: Option>, pub model: Option, } ``` | 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. ```rust 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. ```rust // 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. ```rust 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. ```rust pub type CanUseToolCallback = Arc; ``` ### `PermissionResult` Result of a permission check. ```rust pub enum PermissionResult { Allow(PermissionResultAllow), Deny(PermissionResultDeny), } ``` #### `PermissionResultAllow` ```rust pub struct PermissionResultAllow { pub updated_input: Option, pub updated_permissions: Option>, } ``` #### `PermissionResultDeny` ```rust pub struct PermissionResultDeny { pub message: String, pub interrupt: bool, } ``` ### `McpServerConfig` MCP server configuration. ```rust pub enum McpServerConfig { Stdio(McpStdioServerConfig), Sse(McpSseServerConfig), Http(McpHttpServerConfig), Sdk(SdkMcpServerConfig), } ``` #### `McpStdioServerConfig` ```rust pub struct McpStdioServerConfig { pub server_type: Option, // "stdio" pub command: String, pub args: Option>, pub env: Option>, } ``` #### `McpSseServerConfig` ```rust pub struct McpSseServerConfig { pub server_type: String, // "sse" pub url: String, pub headers: Option>, } ``` #### `McpHttpServerConfig` ```rust pub struct McpHttpServerConfig { pub server_type: String, // "http" pub url: String, pub headers: Option>, } ``` #### `SdkMcpServerConfig` ```rust pub struct SdkMcpServerConfig { pub name: String, pub version: Option, } ``` ### `McpServers` MCP servers container. ```rust pub enum McpServers { None, Dict(HashMap), Path(PathBuf), } ``` ```rust 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. ```rust pub enum Message { User { ... }, Assistant { ... }, System { ... }, Result { ... }, StreamEvent { ... }, } ``` #### `Message::User` User message sent to Claude. | Field | Type | Description | | :------------------- | :------------------- | :------------------------------------------ | | `parent_tool_use_id` | `Option` | Parent tool use ID for nested conversations | | `message` | `UserMessageContent` | Message content | | `session_id` | `Option` | Session identifier | #### `Message::Assistant` Response from Claude. | Field | Type | Description | | :------------------- | :------------------------ | :------------------------------------------ | | `parent_tool_use_id` | `Option` | Parent tool use ID for nested conversations | | `message` | `AssistantMessageContent` | Message content | | `session_id` | `Option` | 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` | Total cost in USD | | `usage` | `Option` | Aggregate token usage | | `result` | `Option` | Result message (for success) | | `model_usage` | `HashMap` | Per-model usage statistics | | `permission_denials` | `Vec` | Denied tool uses | | `structured_output` | `Option` | Structured output (when `output_format` specified) | | `errors` | `Vec` | 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` | Parent tool use ID | ### `ContentBlock` Content blocks in assistant messages. ```rust 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, is_error: Option }, } ``` | 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. ```rust pub enum ContentValue { String(String), Blocks(Vec), } ``` ### AskUserQuestion Tool Types Types for the `AskUserQuestion` tool which allows agents to ask users multiple-choice questions. #### `QuestionOption` ```rust 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` ```rust pub struct QuestionSpec { pub question: String, pub header: String, pub options: Vec, 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` | Available choices (2-4 options) | | `multi_select` | `bool` | Whether multiple options can be selected | #### `AskUserQuestionInput` ```rust pub struct AskUserQuestionInput { pub questions: Vec, pub answers: Option>, } ``` | Field | Type | Description | | :---------- | :-------------------------------- | :-------------------------------------- | | `questions` | `Vec` | Questions to ask (1-4 questions) | | `answers` | `Option>` | User answers (populated in tool result) | #### `AskUserQuestionOutput` ```rust pub struct AskUserQuestionOutput { pub answers: HashMap, } ``` | Field | Type | Description | | :-------- | :------------------------ | :-------------------------------------- | | `answers` | `HashMap` | User's answers keyed by question header | #### Example: Parsing Tool Input ```rust 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. ```rust pub struct UserMessageContent { pub role: String, // Always "user" pub content: Option, } ``` ### `UserContent` User content variants. ```rust pub enum UserContent { String(String), Blocks(Vec), } ``` ### `AssistantMessageContent` Assistant message content. ```rust pub struct AssistantMessageContent { pub model: String, pub content: Vec, } ``` ### `SDKPermissionDenial` Information about denied tool use. ```rust 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. ```rust 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: ```rust // 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` | Whitelist - only these tools can be used | | `disallowed_tools` | `Vec` | Blacklist - these tools cannot be used | When both are specified, `allowed_tools` takes precedence. ### Detecting Tool Use ```rust 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: ```rust // 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, _ctx: HookContext, ) -> Result { 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: ```rust struct AuditingPermissionHandler; #[async_trait] impl PermissionCallback for AuditingPermissionHandler { async fn call( &self, tool_name: String, input: serde_json::Value, _ctx: ToolPermissionContext, ) -> Result { // 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](#hook-types) for `PreToolUse`/`PostToolUse` details. See [Permission Types](#permission-types) for `PermissionCallback` trait. See [Claude Code CLI Reference](https://docs.anthropic.com/en/docs/claude-code/cli) for tool schemas. ## Hook Types ### `HookEvent` Hook event types for intercepting agent actions. ```rust 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. ```rust #[async_trait] pub trait HookCallback: Send + Sync { async fn call( &self, input: serde_json::Value, tool_use_id: Option, context: HookContext, ) -> Result; } ``` #### Implementing with a struct ```rust 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, _context: HookContext, ) -> Result { println!("Tool called: {:?}", tool_use_id); Ok(HookOutput::default()) } } ``` #### Implementing with a closure ```rust 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. ```rust pub struct HookMatcher { pub matcher: Option, pub hooks: Vec>, pub timeout: Option, // Default: 60 seconds } ``` | Field | Type | Description | | :-------- | :--------------------------- | :---------------------------------------------------------------------------- | | `matcher` | `Option` | Pattern (`None` for all, `"Bash"` for specific, `"Write\|Edit"` for multiple) | | `hooks` | `Vec>` | Hook callbacks to invoke | | `timeout` | `Option` | Timeout per hook (default: 60 seconds) | ### `HookMatcherBuilder` Builder for creating `HookMatcher` instances. ```rust 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. ```rust pub struct HookContext { pub session_id: Option, pub cwd: Option, pub cancellation_token: Option, } ``` | Field | Type | Description | | :------------------- | :-------------------------- | :----------------------------- | | `session_id` | `Option` | Session identifier | | `cwd` | `Option` | Current working directory | | `cancellation_token` | `Option` | Token for cancellation support | | Method | Returns | Description | | :--------------- | :------ | :---------------------------------- | | `is_cancelled()` | `bool` | Check if cancellation was requested | ### `HookOutput` Output from a hook callback. ```rust pub struct HookOutput { pub decision: Option, pub system_message: Option, pub hook_specific_output: Option, } ``` | Field | Type | Description | | :--------------------- | :-------------------------- | :------------------------------- | | `decision` | `Option` | Block or allow the action | | `system_message` | `Option` | Message to add to system context | | `hook_specific_output` | `Option` | Hook-specific data | ### `HookDecision` Decision for hook callbacks. ```rust 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`: ```rust pub struct BaseHookInput { pub session_id: String, pub transcript_path: String, pub cwd: String, pub permission_mode: Option, } ``` | 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` ```rust pub enum SessionStartSource { Startup, // Fresh startup Resume, // Resumed session Clear, // After clear Compact, // After compact } ``` ### `SessionEndReason` ```rust pub enum SessionEndReason { Clear, // Session cleared Logout, // User logged out PromptInputExit, // User exited prompt input Other, // Other reason } ``` ### `CompactTrigger` ```rust pub enum CompactTrigger { Manual, // Manually triggered Auto, // Automatically triggered } ``` ### `HookManager` Manager for registering and invoking hooks. ```rust 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. ```rust #[async_trait] pub trait PermissionCallback: Send + Sync { async fn call( &self, tool_name: String, input: serde_json::Value, context: ToolPermissionContext, ) -> Result; } ``` #### Implementing with a struct ```rust 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 { 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 ```rust 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. ```rust pub struct ToolPermissionContext { pub suggestions: Vec, pub cancellation_token: Option, } ``` | Field | Type | Description | | :------------------- | :-------------------------- | :------------------------------ | | `suggestions` | `Vec` | Permission suggestions from CLI | | `cancellation_token` | `Option` | 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. ```rust pub enum PermissionUpdate { AddRules { rules: Option>, destination: Option }, ReplaceRules { rules: Option>, destination: Option }, RemoveRules { rules: Option>, destination: Option }, SetMode { mode: PermissionMode, destination: Option }, AddDirectories { directories: Option>, destination: Option }, RemoveDirectories { directories: Option>, destination: Option }, } ``` ### `PermissionUpdateDestination` Where to save permission updates. ```rust pub enum PermissionUpdateDestination { UserSettings, // ~/.claude/settings.json ProjectSettings, // .claude/settings.json LocalSettings, // .claude/settings.local.json Session, // Session only (temporary) } ``` ### `PermissionBehavior` Permission behavior options. ```rust pub enum PermissionBehavior { Allow, Deny, Ask, } ``` ### `PermissionRuleValue` Permission rule definition. ```rust pub struct PermissionRuleValue { pub tool_name: String, pub rule_content: Option, } ``` ### `PermissionRequest` Permission request from CLI. ```rust pub struct PermissionRequest { pub tool_name: ToolName, pub tool_input: serde_json::Value, pub context: ToolPermissionContext, } ``` ### `PermissionManager` Manager for permission callbacks. ```rust 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. ```rust pub struct SessionInfo { pub model: Option, pub tools: Vec, pub cwd: Option, pub mcp_servers: Vec, pub extra: HashMap, } ``` | 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. ```rust pub struct ToolInfo { pub name: String, pub description: Option, pub input_schema: Option, } ``` ### `McpServerStatus` MCP server connection status. ```rust pub struct McpServerStatus { pub name: String, pub status: String, // "connected", "failed", "needs-auth", "pending" pub error: Option, pub tools: Vec, } ``` | Method | Returns | Description | | :--------------- | :------ | :--------------------------- | | `is_connected()` | `bool` | Check if server is connected | ### `ModelInfo` Claude model information. ```rust pub struct ModelInfo { pub id: String, pub name: Option, pub max_tokens: Option, pub supports_thinking: bool, } ``` ```rust // Get known models for model in ModelInfo::known_models() { println!("{}: {:?}", model.id, model.name); } ``` ### `SlashCommand` Custom slash command information. ```rust pub struct SlashCommand { pub name: String, pub description: String, pub argument_hint: String, } ``` ### `AccountInfo` OAuth account information. ```rust pub struct AccountInfo { pub email: Option, pub account_id: Option, pub is_oauth: bool, pub organization_id: Option, } ``` ## Error Types ### `ClaudeError` Main error type for the SDK. ```rust pub enum ClaudeError { CliNotFound(String), Connection(String), Process { message: String, exit_code: i32, stderr: Option }, JsonDecode(serde_json::Error), JsonEncode(String), MessageParse { message: String, data: Option }, 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 ```rust 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` Type alias for SDK results. ```rust pub type Result = std::result::Result; ``` ## MCP Configuration MCP (Model Context Protocol) configuration for external and in-process servers. ### External Server Configuration Already documented in [Types > McpServerConfig](#mcpserverconfig). ### In-Process MCP Servers (feature: `rmcp`) The `rmcp` feature enables creating in-process MCP servers with custom tools. Enable in `Cargo.toml`: ```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` | Router for tool invocations | | `Parameters` | 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`. ```rust pub trait SdkMcpServer: ServerHandler {} ``` #### Example: Creating an MCP Server ```rust 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, } #[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) -> Result { 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`, `Deref`, and `Borrow`. ### `SessionId` Session identifier. ```rust pub struct SessionId(String); ``` | Method | Returns | Description | | :--------- | :------ | :------------------ | | `new(id)` | `Self` | Create from string | | `as_str()` | `&str` | Get as string slice | ### `ToolName` Tool name identifier. ```rust 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. ```rust 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. ```rust use anthropic_agent_sdk::utils::{safe_truncate, truncate_for_display, safe_window}; ``` #### `safe_truncate()` Safely truncate a string at a UTF-8 character boundary. ```rust 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. ```rust // 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. ```rust 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. ```rust 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. ```rust 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. ```rust 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. ```rust pub async fn check_claude_version(cli_path: &Path) -> Result ``` Returns the version string if the CLI is at least `MIN_CLI_VERSION`, otherwise returns `ClaudeError::CliVersionTooOld`. ### `MIN_CLI_VERSION` Minimum required CLI version. ```rust 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): ```rust // 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`: ```rust // 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](#session-binding-secure-by-default). For full security documentation, see [SECURITY.md](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. ```rust use anthropic_agent_sdk::auth::{OAuthClient, TokenStorage}; #[tokio::main] async fn main() -> Result<(), Box> { // 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 ```rust impl OAuthClient { pub fn new() -> AuthResult pub fn builder() -> OAuthClientBuilder } ``` #### Methods | Method | Returns | Description | | :------------------- | :---------------------- | :--------------------------------------------- | | `authenticate()` | `AuthResult` | Try cached token, refresh, or start OAuth flow | | `start_oauth_flow()` | `AuthResult` | 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` | Get current token without refresh | ### `OAuthClientBuilder` Builder for custom OAuth configuration. ```rust 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. ```rust 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. ```rust pub struct TokenInfo { pub access_token: String, pub refresh_token: Option, pub token_type: String, pub scope: Option, pub expires_at: Option, } ``` | 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` | Get remaining validity duration | ### `TokenStorage` Persistent token storage. ```rust 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` | Load token from storage | | `load_valid()` | `Result` | 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. ```rust 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. ```rust 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 ```rust use anthropic_agent_sdk::auth::OAuthClient; #[tokio::main] async fn main() -> Result<(), Box> { 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 - [SECURITY.md](SECURITY.md) - Full security documentation and threat model - [Claude Code CLI reference](https://docs.anthropic.com/en/docs/claude-code/cli-reference) - Command-line interface and tool input/output schemas - [TypeScript SDK reference](https://docs.anthropic.com/en/docs/agent-sdk/typescript) - TypeScript SDK documentation - [Python SDK reference](https://docs.anthropic.com/en/docs/agent-sdk/python) - Python SDK documentation - [Common workflows](https://docs.anthropic.com/en/docs/claude-code/common-workflows) - Step-by-step guides - [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) - Protocol specification