- Re-export TransportHandle / TransportReader / TransportWriter alongside SubprocessTransport. Without these in the public API, rustdoc refused to resolve [`TransportHandle::close`] from SubprocessTransport's docs. - Inline the close()/end_input() link prose where the broken-link warning fired. cargo doc --no-deps now builds clean.
132 lines
5 KiB
Rust
132 lines
5 KiB
Rust
//! Async Rust SDK for the Claude Agent CLI.
|
|
//!
|
|
//! This crate is a Rust port of the official Python
|
|
//! [`claude-agent-sdk`](https://github.com/anthropics/claude-agent-sdk-python).
|
|
//! It wraps the `claude` CLI as a subprocess and exposes its
|
|
//! newline-delimited JSON stream as ergonomic Rust types.
|
|
//!
|
|
//! # Quick start
|
|
//!
|
|
//! ```no_run
|
|
//! 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(())
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! # Two entry points
|
|
//!
|
|
//! - [`query`] — one-shot prompts. Returns a stream that ends when the CLI
|
|
//! emits its terminal `result` message, automatically tearing down the
|
|
//! subprocess.
|
|
//! - [`Client`] — bidirectional, multi-turn sessions. Lets you send
|
|
//! follow-up prompts in response to assistant messages, mirroring the
|
|
//! Python SDK's `ClaudeSDKClient`.
|
|
//!
|
|
//! # Configuration
|
|
//!
|
|
//! Configure both entry points via [`ClaudeAgentOptions`]. It uses a builder
|
|
//! pattern — chain `.with_*()` methods on a default value:
|
|
//!
|
|
//! ```
|
|
//! use claude_agent_sdk::{ClaudeAgentOptions, PermissionMode};
|
|
//!
|
|
//! let opts = ClaudeAgentOptions::new()
|
|
//! .with_model("claude-sonnet-4-5")
|
|
//! .with_permission_mode(PermissionMode::AcceptEdits)
|
|
//! .with_allowed_tool("Read")
|
|
//! .with_allowed_tool("Bash")
|
|
//! .with_cwd("/tmp/my-project")
|
|
//! .with_max_turns(5);
|
|
//! ```
|
|
//!
|
|
//! See [`ClaudeAgentOptions`] for the full list of supported fields.
|
|
//!
|
|
//! # Field naming
|
|
//!
|
|
//! The CLI wire protocol is `snake_case`, so deserialized field names match
|
|
//! the Python SDK directly (`session_id`, `total_cost_usd`, `tool_use_id`,
|
|
//! etc.). Where the wire used `camelCase` historically — `modelUsage` — we
|
|
//! preserve it via `#[serde(rename = "...")]` rather than blanket-renaming
|
|
//! the container.
|
|
//!
|
|
//! # v0.1 scope and known limitations
|
|
//!
|
|
//! The v0.1 port covers the core path of the Python SDK:
|
|
//!
|
|
//! - Subprocess transport with newline-delimited JSON framing.
|
|
//! - All message and content-block types parsed faithfully.
|
|
//! - `query()` + `Client` for one-shot and bidirectional use.
|
|
//! - The full `ClaudeAgentOptions` flag surface relevant to subprocess args.
|
|
//!
|
|
//! Deferred to v0.2 — see the upstream README for the current shape of these
|
|
//! features in Python:
|
|
//!
|
|
//! - **Control protocol over JSON-RPC**: `interrupt()`, `set_permission_mode()`,
|
|
//! `set_model()`, `get_mcp_status()`, etc. The Rust [`Client`] today only
|
|
//! speaks the bare user / assistant / result frames.
|
|
//! - **`can_use_tool` permission callback**: requires the control protocol.
|
|
//! - **`@tool` decorator / `create_sdk_mcp_server()`**: in-process MCP
|
|
//! servers — needs a derive macro or trait shape, deferred until
|
|
//! downstream demand is clearer.
|
|
//! - **`HookMatcher`** and hook callbacks: the wire format is supported
|
|
//! (initialize-payload + hook_callback responses) but the Rust callback
|
|
//! surface is not designed yet.
|
|
//! - **`SessionStore`** mirroring adapter trait.
|
|
//! - **`Sandbox` settings**, **plugins**, **agents** dataclass.
|
|
//!
|
|
//! These all degrade gracefully — the CLI ignores absent stdin frames, so
|
|
//! a v0.1 caller using a `claude` build that supports control requests will
|
|
//! simply see fewer features than the Python SDK exposes.
|
|
|
|
#![deny(missing_docs)]
|
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
|
|
mod client;
|
|
mod errors;
|
|
mod messages;
|
|
mod options;
|
|
mod query;
|
|
mod transport;
|
|
|
|
pub use client::Client;
|
|
pub use errors::{Error, Result};
|
|
pub use messages::{
|
|
AssistantMessage, AssistantMessageInner, ContentBlock, Message, ResultMessage,
|
|
ServerToolResultBlock, ServerToolUseBlock, StreamEventMessage, SystemMessage, TextBlock,
|
|
ThinkingBlock, ToolResultBlock, ToolUseBlock, UserContent, UserMessage, UserMessageInner,
|
|
};
|
|
pub use options::{ClaudeAgentOptions, Effort, McpServersConfig, PermissionMode, SystemPrompt};
|
|
pub use query::query;
|
|
pub use transport::{SubprocessTransport, TransportHandle, TransportReader, TransportWriter};
|
|
|
|
/// Crate version, as set in `Cargo.toml`. Sent to the CLI as
|
|
/// `CLAUDE_AGENT_SDK_VERSION` in the subprocess env.
|
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|