claude-agent-sdk-rust/README.md
Cobb Hayes 2aa3cd07b7 Public-flip audit: URL refresh + minor scrubs
Repository URLs, version strings, and example creds normalized for the
public git.sulkta.com endpoint. No code-behavior change.

Audit-applied by the public-flip rolling-audit pass — see
kayos/openclaw-workspace memory/2026-05-27 logs for the campaign
context.
2026-05-27 10:59:57 -07:00

144 lines
5.1 KiB
Markdown

# claude-agent-sdk (Rust)
Async Rust SDK for the Claude Agent CLI — a faithful port of the official
Python [`claude-agent-sdk`](https://github.com/anthropics/claude-agent-sdk-python).
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::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](#v01-scope-and-known-limitations) below.
## Install
```toml
[dependencies]
claude-agent-sdk = { git = "https://git.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
```rust
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
```rust
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-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`](LICENSE).
This is an independent port; the upstream Python SDK is © Anthropic, PBC.