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. |
||
|---|---|---|
| src | ||
| .gitignore | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
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.ClaudeSDKClient→claude_agent_sdk::Clientfor 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 protocol —
interrupt(),set_permission_mode(),set_model(),get_mcp_status(), etc. The Python SDK sends a JSON-RPCinitializerequest before the first user message and handles control requests/responses over the same stdio pair. The RustClientcurrently speaks only the bare user / assistant / result frames. Adding the control protocol unlocks the rest of the API. can_use_toolpermission callback — requires the control protocol.- In-process MCP servers (
@tool/create_sdk_mcp_server) — the Python decorator wraps amcp.server.Serverinstance. The Rust shape for this (likely a derive macro on aTooltrait + 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.SessionStoremirroring adapter.Sandboxsettings, 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.