Skip to main content

claude_agent_sdk_rust/
lib.rs

1//! Claude Agent SDK for Rust
2//!
3//! Build production-ready AI agents with Claude Code.
4//!
5//! # Quick Start
6//!
7//! ```no_run
8//! use claude_agent_sdk_rust::{query, ClaudeAgentOptions, Message};
9//! use futures::StreamExt;
10//!
11//! #[tokio::main]
12//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
13//!     let stream = query("What is 2 + 2?", None).await?;
14//!     let mut messages = Box::pin(stream);
15//!
16//!     while let Some(msg) = messages.next().await {
17//!         match msg? {
18//!             Message::Assistant(assistant) => {
19//!                 println!("Claude: {:?}", assistant);
20//!             }
21//!             Message::Result(result) => {
22//!                 println!("Cost: ${:.4}", result.total_cost_usd.unwrap_or(0.0));
23//!             }
24//!             _ => {}
25//!         }
26//!     }
27//!
28//!     Ok(())
29//! }
30//! ```
31
32pub mod callbacks;
33pub mod client;
34pub mod error;
35pub mod parser;
36pub mod query;
37pub mod transport;
38pub mod types;
39
40// Re-export commonly used types
41pub use callbacks::{HookCallback, PermissionCallback};
42pub use client::ClaudeSDKClient;
43pub use error::{ClaudeSDKError, Result};
44pub use types::{
45    AgentDefinition, AssistantMessage, ClaudeAgentOptions, ContentBlock, Effort, HookContext,
46    HookEvent, HookInput, HookOutput, Message, MessageContent, PermissionMode, PermissionResult,
47    ResultMessage, SandboxSettings, SdkPluginConfig, SettingSource, SystemPrompt,
48    SystemPromptPreset, TextBlock, ThinkingConfig, ToolPermissionContext, ToolUseBlock,
49    ToolsOption, UserMessage,
50};
51
52use futures::Stream;
53use query::Query;
54use transport::{check_claude_version, find_claude_cli, subprocess::SubprocessTransport};
55
56/// Query Claude with a simple prompt.
57///
58/// This is the simplest way to interact with Claude. It creates a one-shot
59/// query and returns a stream of messages.
60///
61/// Internally uses streaming mode (matching Python/TypeScript SDK behavior)
62/// to send the prompt via stdin after initialization. This allows agents
63/// and large configs to be sent via the initialize request.
64///
65/// # Arguments
66///
67/// * `prompt` - The prompt to send to Claude
68/// * `options` - Optional configuration (uses defaults if None)
69///
70/// # Returns
71///
72/// A stream of `Message` objects representing the conversation.
73///
74/// # Example
75///
76/// ```no_run
77/// use claude_agent_sdk_rust::{query, Message};
78/// use futures::StreamExt;
79///
80/// #[tokio::main]
81/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
82///     let stream = query("Hello, Claude!", None).await?;
83///     let mut messages = Box::pin(stream);
84///
85///     while let Some(msg) = messages.next().await {
86///         println!("{:?}", msg?);
87///     }
88///
89///     Ok(())
90/// }
91/// ```
92pub async fn query(
93    prompt: impl Into<String>,
94    options: Option<ClaudeAgentOptions>,
95) -> Result<impl Stream<Item = Result<Message>>> {
96    let prompt_str = prompt.into();
97    let options = options.unwrap_or_default();
98
99    // Find CLI
100    let cli_path = find_claude_cli(options.cli_path.as_ref())?;
101
102    // Check version (optional, doesn't fail)
103    if std::env::var("CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK").is_err() {
104        let _ = check_claude_version(&cli_path).await;
105    }
106
107    // Always use streaming mode internally (matching Python/TypeScript SDK)
108    let mut transport = SubprocessTransport::new_streaming(cli_path, &options);
109
110    // Spawn process (empty prompt for streaming mode)
111    transport.spawn(&options, "").await?;
112
113    // Create Query, start it, initialize
114    let query_obj = Query::new(transport);
115    let query_handle = query_obj.start();
116
117    // Initialize streaming mode
118    query_handle.initialize(None).await?;
119
120    // Send user message via stdin after initialize, then close stdin
121    // to signal no more input (matching Python SDK's end_input() call)
122    query_handle.send_user_message(&prompt_str).await?;
123    query_handle.close_stdin().await;
124
125    // Return stream of parsed messages
126    let message_stream = async_stream::stream! {
127        use futures::stream::StreamExt as _;
128
129        let mut stream = Box::pin(query_handle.read_messages());
130
131        while let Some(result) = stream.next().await {
132            match result {
133                Ok(value) => {
134                    match parser::parse_message(value) {
135                        Ok(message) => yield Ok(message),
136                        Err(ClaudeSDKError::UnknownMessageType(_)) => {
137                            // Skip unknown message types (forward-compatible)
138                            continue;
139                        }
140                        Err(e) => yield Err(e),
141                    }
142                }
143                Err(e) => yield Err(e),
144            }
145        }
146    };
147
148    Ok(message_stream)
149}