#claude #sdk #claude-code #llm

bin+lib cc-sdk

Rust SDK for Claude Code CLI with full interactive capabilities

10 releases (4 breaking)

Uses new Rust 2024

new 0.5.0 Feb 8, 2026
0.4.0 Dec 17, 2025
0.3.0 Oct 20, 2025
0.2.0 Oct 7, 2025
0.1.5 Jul 22, 2025

#268 in Asynchronous

Download history 111/week @ 2025-10-19 14/week @ 2025-10-26 91/week @ 2025-12-28 32/week @ 2026-01-04 13/week @ 2026-01-18 11/week @ 2026-01-25 29/week @ 2026-02-01

53 downloads per month
Used in url-preview

MIT license

380KB
7K SLoC

Claude Code SDK for Rust

Crates.io Documentation License

A Rust SDK for interacting with Claude Code CLI, providing both simple query interfaces and full interactive client capabilities.

v0.4.0: 🎉 100% Feature Parity with Python SDK v0.1.14 - Including automatic CLI download!

Features

  • 🚀 Simple Query Interface - One-shot queries with the query() function
  • 💬 Interactive Client - Stateful conversations with context retention
  • 🔄 Streaming Support - Real-time message streaming
  • 🛑 Interrupt Capability - Cancel ongoing operations
  • 🔧 Full Configuration - Comprehensive options for Claude Code
  • 📦 Type Safety - Strongly typed with serde support
  • Async/Await - Built on Tokio for async operations
  • 🔒 Control Protocol - Full support for permissions, hooks, and MCP servers
  • 💰 Token Optimization - Built-in tools to minimize costs and track usage
  • 📥 Auto CLI Download - Automatically downloads Claude Code CLI if not found (v0.4.0+)
  • 📁 File Checkpointing - Rewind file changes to any point in conversation (v0.4.0+)
  • 📊 Structured Output - JSON schema validation for responses (v0.4.0+)

Python SDK Parity (v0.4.0)

This Rust SDK achieves 100% feature parity with the official Python claude-agent-sdk v0.1.14:

Feature Python SDK Rust SDK Status
Simple query API ✅ Parity
Interactive client ✅ Parity
Streaming messages ✅ Parity
tools (base tool set) ✅ Parity
permission_mode ✅ Parity
max_budget_usd ✅ Parity
fallback_model ✅ Parity
output_format (structured) ✅ Parity
enable_file_checkpointing ✅ Parity
rewind_files() ✅ Parity
sandbox ✅ Parity
plugins ✅ Parity
betas (SDK beta features) ✅ Parity
Permission callbacks ✅ Parity
Hook callbacks ✅ Parity
MCP servers (all types) ✅ Parity
Bundled/Auto CLI ✅ (bundled) ✅ (auto-download) ✅ Equivalent

Note: Only user (OS setuid) is not implemented due to platform/privilege requirements.

Token Optimization (New in v0.1.12)

Minimize token consumption and control costs with built-in optimization tools:

use cc_sdk::{ClaudeCodeOptions, ClaudeSDKClient, PermissionMode};
use cc_sdk::token_tracker::BudgetLimit;
use cc_sdk::model_recommendation::ModelRecommendation;

// 1. Choose cost-effective model
let recommender = ModelRecommendation::default();
let model = recommender.suggest("simple").unwrap(); // → Haiku (cheapest)
// Or use latest Sonnet 4.5 for balanced tasks
let latest = recommender.suggest("latest").unwrap(); // → Sonnet 4.5

// 2. Configure for minimal token usage
let options = ClaudeCodeOptions::builder()
    .model(model)
    .max_turns(Some(3))              // Limit conversation length
    .max_output_tokens(2000)          // Cap response size (NEW)
    .allowed_tools(vec!["Read".to_string()])  // Restrict tools
    .permission_mode(PermissionMode::BypassPermissions)
    .build();

let mut client = ClaudeSDKClient::new(options);

// 3. Set budget with alerts
client.set_budget_limit(
    BudgetLimit::with_cost(5.0),      // $5 max
    Some(|msg| eprintln!("⚠️  {}", msg))  // Alert at 80%
).await;

// ... run your queries ...

// 4. Monitor usage
let usage = client.get_usage_stats().await;
println!("Tokens: {}, Cost: ${:.2}", usage.total_tokens(), usage.total_cost_usd);

Key Features:

  • max_output_tokens - Precise output control (1-32000, overrides env var)
  • TokenUsageTracker - Real-time token and cost monitoring
  • BudgetLimit - Set cost/token caps with 80% warning threshold
  • ModelRecommendation - Smart model selection (Haiku/Sonnet/Opus)
  • ✅ Automatic usage tracking from ResultMessage

Model Cost Comparison:

  • Haiku 3.5: 1x (baseline, cheapest)
  • Sonnet 4.5 (Latest): ~5x more expensive, best balance ⭐
  • Opus 4.1: ~15x more expensive, most capable

See Token Optimization Guide for complete strategies and examples.

Complete Feature Set

This Rust SDK provides comprehensive functionality for Claude Code interactions:

  • Client methods: query(), send_message(), receive_response(), interrupt()
  • Interactive sessions: Full stateful conversation support
  • Message streaming: Real-time async message handling
  • Configuration options: System prompts, models, permissions, tools, etc.
  • Message types: User, Assistant, System, Result messages
  • Error handling: Comprehensive error types with detailed diagnostics
  • Session management: Multi-session support with context isolation
  • Type safety: Leveraging Rust's type system for reliable code
  • Control Protocol: Permission callbacks, hook system, MCP servers (SDK type)
  • CLI Compatibility: Configurable protocol format for maximum compatibility
  • Account Information: Retrieve current account details programmatically

Installation

Add this to your Cargo.toml:

[dependencies]
cc-sdk = "0.4.0"
tokio = { version = "1.0", features = ["full"] }
futures = "0.3"

Automatic CLI Download (Default)

The SDK will automatically download Claude Code CLI if it's not found on your system:

let options = ClaudeCodeOptions::builder()
    .auto_download_cli(true)  // Enabled by default
    .build();

CLI is cached in platform-specific locations:

  • macOS: ~/Library/Caches/cc-sdk/cli/
  • Linux: ~/.cache/cc-sdk/cli/
  • Windows: %LOCALAPPDATA%\cc-sdk\cli\

To disable auto-download, use:

[dependencies]
cc-sdk = { version = "0.4.0", default-features = false }

Prerequisites

Claude Code CLI is automatically downloaded by the SDK if not found (v0.4.0+).

For manual installation:

npm install -g @anthropic-ai/claude-code

Environment Setup

For reliable SDK operation, set the ANTHROPIC_USER_EMAIL environment variable:

export ANTHROPIC_USER_EMAIL="your-email@example.com"

Or create a .env file in your project:

# .env
ANTHROPIC_USER_EMAIL=your-email@example.com
CLAUDE_MODEL=claude-sonnet-4-5-20250929

See Environment Variables Guide for complete details.

Supported Models (2025)

The SDK supports the latest Claude models available in 2025:

Latest Models

  • Opus 4.1 - Most capable model

    • Full name: "claude-opus-4-1-20250805"
    • Alias: "opus" (recommended - uses latest Opus)
  • Sonnet 4 - Balanced performance

    • Full name: "claude-sonnet-4-20250514"
    • Alias: "sonnet" (recommended - uses latest Sonnet)

Previous Generation

  • Claude 3.5 Sonnet - "claude-3-5-sonnet-20241022"
  • Claude 3.5 Haiku - "claude-3-5-haiku-20241022" (fastest)

Using Models in Code

use cc_sdk::{query, ClaudeCodeOptions, Result};

// Using Opus 4.1 (recommended: use alias)
let options = ClaudeCodeOptions::builder()
    .model("opus")  // or "claude-opus-4-1-20250805" for specific version
    .build();

// Using Sonnet 4 (recommended: use alias)
let options = ClaudeCodeOptions::builder()
    .model("sonnet")  // or "claude-sonnet-4-20250514" for specific version
    .build();

let mut messages = query("Your prompt", Some(options)).await?;

Quick Start

Simple Query (One-shot)

use cc_sdk::{query, Result};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<()> {
    let mut messages = query("What is 2 + 2?", None).await?;
    
    while let Some(msg) = messages.next().await {
        println!("{:?}", msg?);
    }
    
    Ok(())
}

Interactive Client

use cc_sdk::{InteractiveClient, ClaudeCodeOptions, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let mut client = InteractiveClient::new(ClaudeCodeOptions::default())?;
    client.connect().await?;

    // Send a message and receive response
    let messages = client.send_and_receive(
        "Help me write a Python web server".to_string()
    ).await?;

    // Process responses
    for msg in &messages {
        match msg {
            cc_sdk::Message::Assistant { message } => {
                println!("Claude: {:?}", message);
            }
            _ => {}
        }
    }

    // Send follow-up
    let messages = client.send_and_receive(
        "Make it use async/await".to_string()
    ).await?;

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

Account Information (New)

use cc_sdk::{ClaudeSDKClient, ClaudeCodeOptions, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let mut client = ClaudeSDKClient::new(ClaudeCodeOptions::default());
    client.connect(None).await?;

    // Get current account information
    let account_info = client.get_account_info().await?;
    println!("Current account: {}", account_info);

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

Streaming Output (Since v0.1.8)

use cc_sdk::{InteractiveClient, ClaudeCodeOptions, Result};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<()> {
    let mut client = InteractiveClient::new(ClaudeCodeOptions::default())?;
    client.connect().await?;
    
    // Send a message
    client.send_message("Explain quantum computing".to_string()).await?;
    
    // Receive messages as a stream
    let mut stream = client.receive_messages_stream().await;
    while let Some(result) = stream.next().await {
        match result {
            Ok(message) => {
                println!("Received: {:?}", message);
                if matches!(message, cc_sdk::Message::Result { .. }) {
                    break;
                }
            }
            Err(e) => eprintln!("Error: {}", e),
        }
    }
    
    // Or use the convenience method that stops at Result message
    client.send_message("What's 2 + 2?".to_string()).await?;
    let mut stream = client.receive_response_stream().await;
    while let Some(result) = stream.next().await {
        match result {
            Ok(message) => println!("Message: {:?}", message),
            Err(e) => eprintln!("Error: {}", e),
        }
    }
    
    client.disconnect().await?;
    Ok(())
}

Advanced Usage

use cc_sdk::{InteractiveClient, ClaudeCodeOptions, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let mut client = InteractiveClient::new(ClaudeCodeOptions::default())?;
    client.connect().await?;
    
    // Send message without waiting for response
    client.send_message("Calculate pi to 100 digits".to_string()).await?;
    
    // Do other work...
    
    // Receive response when ready (stops at Result message)
    let messages = client.receive_response().await?;
    
    // Cancel long-running operations
    client.send_message("Write a 10,000 word essay".to_string()).await?;
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    client.interrupt().await?;
    
    client.disconnect().await?;
    Ok(())
}

Configuration Options

use cc_sdk::{ClaudeCodeOptions, PermissionMode, ControlProtocolFormat};

let options = ClaudeCodeOptions::builder()
    .system_prompt("You are a helpful coding assistant")
    .model("claude-3-5-sonnet-20241022")
    .permission_mode(PermissionMode::AcceptEdits)
    .max_turns(10)
    .max_thinking_tokens(10000)
    .allowed_tools(vec!["read_file".to_string(), "write_file".to_string()])
    .cwd("/path/to/project")
    // New in v0.1.6
    .settings("claude-settings.json")  // Use custom settings file
    .add_dir("/path/to/related/project")  // Add additional working directories
    .add_dirs(vec![PathBuf::from("/dir1"), PathBuf::from("/dir2")])  // Add multiple dirs
    // New in v0.1.11: Control protocol format configuration
    .control_protocol_format(ControlProtocolFormat::Legacy)  // Default: maximum compatibility
    .build();

Control Protocol (v0.1.12+)

New request helpers and options aligned with the Python SDK:

  • Query::set_permission_mode("acceptEdits" | "default" | "plan" | "bypassPermissions")
  • Query::set_model(Some("sonnet")) or set_model(None) to clear
  • ClaudeCodeOptions::builder().include_partial_messages(true) to include partial assistant chunks
  • Query::stream_input(stream) automatically calls end_input when finished

Example:

use cc_sdk::{Query, ClaudeCodeOptions};
use cc_sdk::transport::SubprocessTransport;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::Mutex;

# async fn demo() -> cc_sdk::Result<()> {
let options = ClaudeCodeOptions::builder()
    .model("sonnet")
    .include_partial_messages(true)
    .build();

let transport: Box<dyn cc_sdk::transport::Transport + Send> =
    Box::new(SubprocessTransport::new(options)?);
let transport = Arc::new(Mutex::new(transport));

let mut q = Query::new(transport, true, None, None, HashMap::new());
q.start().await?;                  // start routing
q.set_permission_mode("acceptEdits").await?;
q.set_model(Some("opus".into())).await?;

// Stream input; end_input is called automatically when the stream completes
let inputs = vec![serde_json::json!("Hello"), serde_json::json!({"content":"Ping"})];
q.stream_input(futures::stream::iter(inputs)).await?;
# Ok(()) }

Advanced flags mapped to CLI:

  • fork_session(true)--fork-session
  • setting_sources(vec![User, Project, Local])--setting-sources user,project,local
  • agents(map)--agents '<json>'

Agent Tools & MCP

  • Tools whitelist/blacklist: set allowed_tools / disallowed_tools in ClaudeCodeOptions.
  • Permission mode: PermissionMode::{Default, AcceptEdits, Plan, BypassPermissions}.
  • Runtime approvals: implement CanUseTool and return PermissionResult::{Allow,Deny}.
  • MCP servers: configure via options.mcp_servers (stdio/http/sse/sdk), SDK packs JSON for --mcp-config.
use cc_sdk::{ClaudeCodeOptions, PermissionMode, CanUseTool, ToolPermissionContext, PermissionResult,
             PermissionResultAllow, transport::{Transport, SubprocessTransport}, Query};
use std::{collections::HashMap, sync::Arc};
use tokio::sync::Mutex;

struct AllowRead;
#[async_trait::async_trait]
impl CanUseTool for AllowRead {
  async fn can_use_tool(&self, tool:&str, _input:&serde_json::Value, _ctx:&ToolPermissionContext) -> PermissionResult {
    if tool == "Read" { PermissionResult::Allow(PermissionResultAllow{updated_input: None, updated_permissions: None}) }
    else { cc_sdk::PermissionResult::Deny(cc_sdk::PermissionResultDeny{ message: "Not allowed".into(), interrupt: false }) }
  }
}

# async fn demo() -> cc_sdk::Result<()> {
let mut opts = ClaudeCodeOptions::builder()
  .permission_mode(PermissionMode::AcceptEdits)
  .include_partial_messages(true)
  .build();
opts.allowed_tools = vec!["Read".into()];

let mut mcp = HashMap::new();
mcp.insert("filesystem".into(), cc_sdk::McpServerConfig::Stdio{ command: "npx".into(), args: Some(vec!["-y".into(), "@modelcontextprotocol/server-filesystem".into(), "/allowed".into()]), env: None });
opts.mcp_servers = mcp;

let transport: Box<dyn Transport + Send> = Box::new(SubprocessTransport::new(opts)?);
let transport = Arc::new(Mutex::new(transport));
let mut q = Query::new(transport, true, Some(Arc::new(AllowRead)), None, HashMap::new());
q.start().await?;
# Ok(()) }

Control Protocol Compatibility (v0.1.11+)

The SDK supports configurable control protocol formats for CLI compatibility:

  • Legacy (default): Uses sdk_control_request/response format - works with all CLI versions
  • Control: Uses new type=control format - for newer CLI versions
  • Auto: Currently defaults to Legacy, will auto-negotiate in future
// Use environment variable to override (useful for testing)
// export CLAUDE_CODE_CONTROL_FORMAT=legacy  # or "control"

// Or configure programmatically
let options = ClaudeCodeOptions::builder()
    .control_protocol_format(ControlProtocolFormat::Legacy)
    .build();

See CONTROL_PROTOCOL_COMPATIBILITY.md for detailed information.

API Reference

query()

Simple, stateless query function for one-shot interactions.

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

InteractiveClient

Main client for stateful, interactive conversations.

Methods

  • new(options: ClaudeCodeOptions) -> Result<Self> - Create a new client
  • connect() -> Result<()> - Connect to Claude CLI
  • send_and_receive(prompt: String) -> Result<Vec<Message>> - Send message and wait for complete response
  • send_message(prompt: String) -> Result<()> - Send message without waiting
  • receive_response() -> Result<Vec<Message>> - Receive messages until Result message
  • interrupt() -> Result<()> - Cancel ongoing operation
  • disconnect() -> Result<()> - Disconnect from Claude CLI

Message Types

  • UserMessage - User input messages
  • AssistantMessage - Claude's responses
  • SystemMessage - System notifications
  • ResultMessage - Operation results with timing and cost info

Error Handling

The SDK provides comprehensive error types:

  • CLINotFoundError - Claude Code CLI not installed
  • CLIConnectionError - Connection failures
  • ProcessError - CLI process errors
  • InvalidState - Invalid operation state

Examples

Check the examples/ directory for more usage examples:

  • interactive_demo.rs - Interactive conversation demo
  • query_simple.rs - Simple query example
  • file_operations.rs - File manipulation example

New Features (v0.1.6)

Test the latest features with these examples:

  • test_settings.rs - Using custom settings files
  • test_settings_safe.rs - Safe settings file handling with path detection
  • test_add_dirs.rs - Adding multiple working directories
  • test_combined_features.rs - Combining settings and add_dirs
  • test_new_options.rs - Testing the new builder methods

Example settings files are provided:

  • examples/claude-settings.json - Basic settings configuration
  • examples/custom-claude-settings.json - Advanced settings with MCP servers

Note: When running examples from the project root, use:

cargo run --example test_settings

The settings files use relative paths from the project root (e.g., examples/claude-settings.json)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Dependencies

~12–33MB
~411K SLoC