Rust port of anthropics/claude-agent-sdk-python.
Find a file
Kayos ef1d8fdd31 port query() and Client
Two entry points mirroring the Python SDK's surface:

- query(prompt, options) returns an impl Stream<Item = Result<Message>>
  that terminates after the CLI emits its terminal result message. The
  stream owns the underlying Client and tears down the subprocess via a
  spawned disconnect task either on Result observation or on Drop.

- Client (mirror of ClaudeSDKClient) supports bidirectional multi-turn
  sessions: connect, send (or send_raw for tool-result frames), drain
  the messages stream, repeat. Drop is intentionally a no-op for the
  subprocess — callers should call disconnect() for a clean shutdown
  that surfaces non-zero exit codes as Error::Process.

lib.rs re-exports the public API and carries the crate-level docs +
quick-start example. The v0.1 / v0.2 split is documented inline: control
protocol (interrupt, set_permission_mode, etc.), can_use_tool, in-process
MCP servers, HookMatcher, SessionStore, sandbox, plugins, and the agents
dataclass are all deferred.
2026-05-14 08:03:52 -07:00
src port query() and Client 2026-05-14 08:03:52 -07:00
.gitignore scaffold the crate 2026-05-14 08:03:11 -07:00
Cargo.toml scaffold the crate 2026-05-14 08:03:11 -07:00
LICENSE scaffold the crate 2026-05-14 08:03:11 -07:00
README.md scaffold the crate 2026-05-14 08:03:11 -07:00

claude-agent-sdk (Rust)

Async Rust SDK for the Claude Agent CLI — a faithful port of the official Python claude-agent-sdk.

This crate wraps the claude CLI as a subprocess and exposes its newline-delimited JSON stream as ergonomic Rust types. The Python SDK's two entry points map directly:

  • query() for fire-and-forget one-shot prompts.
  • ClaudeSDKClientclaude_agent_sdk::Client for bidirectional multi-turn sessions.

Status

v0.0.1 — initial port. Core feature parity for the subprocess wire protocol; advanced features (control protocol, hooks, in-process MCP servers, session_store, sandbox, plugins, agents) are deferred to v0.2. See v0.1 scope below.

Install

[dependencies]
claude-agent-sdk = { git = "https://gitea.sulkta.com/Sulkta-Coop/claude-agent-sdk-rust" }
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1"

Prerequisites: the claude CLI on PATH (or supply ClaudeAgentOptions::with_cli_path()), and an authenticated Claude session. The SDK does not bundle the CLI binary.

Quick start

use claude_agent_sdk::{query, ClaudeAgentOptions, Message, ContentBlock};
use tokio_stream::StreamExt;

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let opts = ClaudeAgentOptions::new()
        .with_system_prompt("You are a helpful assistant.")
        .with_max_turns(1);

    let mut stream = query("What is 2 + 2?", opts).await?;

    while let Some(msg) = stream.next().await {
        match msg? {
            Message::Assistant(a) => {
                for block in a.message.content {
                    if let ContentBlock::Text(t) = block {
                        println!("Claude: {}", t.text);
                    }
                }
            }
            Message::Result(r) => {
                if let Some(usd) = r.total_cost_usd {
                    println!("Cost: ${:.4}", usd);
                }
                break;
            }
            _ => {}
        }
    }
    Ok(())
}

Multi-turn sessions

use claude_agent_sdk::{Client, ClaudeAgentOptions, Message, ContentBlock};
use tokio_stream::StreamExt;

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let mut client = Client::new(ClaudeAgentOptions::new()).await?;
    client.connect().await?;
    let mut messages = client.messages();

    client.send("What's the capital of France?").await?;
    // ...consume messages until a Result frame, then ask a follow-up...
    client.send("And of Germany?").await?;

    // Drain until next Result, then disconnect.
    while let Some(msg) = messages.next().await {
        if let Message::Result(_) = msg? {
            break;
        }
    }
    client.disconnect().await?;
    Ok(())
}

See the examples/ directory for runnable variants.

Mapping the Python SDK

Python Rust
query(prompt=...) query(prompt, opts).await?
ClaudeSDKClient Client
ClaudeAgentOptions ClaudeAgentOptions
AssistantMessage Message::Assistant(_)
UserMessage Message::User(_)
SystemMessage Message::System(_)
ResultMessage Message::Result(_)
TextBlock / ToolUseBlock ContentBlock::Text/ToolUse(_)
CLINotFoundError Error::CliNotFound
CLIConnectionError Error::CliConnection
ProcessError Error::Process { .. }
CLIJSONDecodeError Error::JsonDecode { .. }
MessageParseError Error::MessageParse { .. }

v0.1 scope and known limitations

The v0.1 port covers the core path. The following are deferred:

  • Control protocolinterrupt(), set_permission_mode(), set_model(), get_mcp_status(), etc. The Python SDK sends a JSON-RPC initialize request before the first user message and handles control requests/responses over the same stdio pair. The Rust Client currently speaks only the bare user / assistant / result frames. Adding the control protocol unlocks the rest of the API.
  • can_use_tool permission callback — requires the control protocol.
  • In-process MCP servers (@tool / create_sdk_mcp_server) — the Python decorator wraps a mcp.server.Server instance. The Rust shape for this (likely a derive macro on a Tool trait + a runtime that multiplexes over the control protocol) is not yet drafted.
  • HookMatcher — wire format is supported on the CLI side but the Rust callback surface is not designed.
  • SessionStore mirroring adapter.
  • Sandbox settings, plugins, agents dataclass.

Each of these degrades gracefully — a v0.1 caller using a newer CLI sees fewer features, not breakage.

License

MIT. See LICENSE.

This is an independent port; the upstream Python SDK is © Anthropic, PBC.