Expand description
Client for interactive sessions with Claude CLI
This module provides ClaudeClient, a persistent client for multi-turn
conversations with the Claude Code CLI. Unlike the one-shot query() API, ClaudeClient
maintains state across multiple messages and supports control operations like model switching
and session interruption.
ClaudeClient for interactive sessions with Claude CLI
The ClaudeClient provides a high-level API for maintaining long-running interactive sessions
with the Claude Code CLI. Unlike the one-shot query() API, ClaudeClient
maintains a persistent connection and allows:
- Multiple message exchanges - Send messages and receive streaming responses
- Session control - Interrupt execution, change models, modify permission modes
- Handler registration - Install callbacks for tool permission checks, hooks, and MCP
- Full control protocol access - All control operations supported by the CLI
§Architecture
┌──────────────────────────────────────────────────────────┐
│ ClaudeClient │
│ │
│ Session Management Control Operations │
│ • connect() • interrupt() │
│ • send_message() • set_permission_mode() │
│ • receive_response() • set_model() │
│ • close() • mcp_status() │
│ • get_server_info() • rewind_files() │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ControlProtocol (request/response) │ │
│ └────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Transport (SubprocessCLITransport) │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
↓ ResponseStream ↑
Assistant/Result/System send_message()§Example: Basic Session
use rusty_claw::prelude::*;
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create and connect client
let options = ClaudeAgentOptions::builder()
.max_turns(10)
.permission_mode(PermissionMode::AcceptEdits)
.build();
let mut client = ClaudeClient::new(options)?;
client.connect().await?;
// First turn
let mut stream = client.send_message("What files are in this directory?").await?;
while let Some(result) = stream.next().await {
match result {
Ok(Message::Assistant(msg)) => {
for block in msg.message.content {
if let ContentBlock::Text { text } = block {
println!("Claude: {}", text);
}
}
}
Ok(Message::Result(_)) => break,
Ok(_) => {}
Err(e) => eprintln!("Error: {}", e),
}
}
// Second turn (same client, same session!)
let mut stream2 = client.send_message("Now count them.").await?;
while let Some(result) = stream2.next().await {
match result {
Ok(Message::Assistant(msg)) => {
for block in msg.message.content {
if let ContentBlock::Text { text } = block {
println!("Claude: {}", text);
}
}
}
Ok(Message::Result(_)) => break,
Ok(_) => {}
Err(e) => eprintln!("Error: {}", e),
}
}
client.close().await?;
Ok(())
}§Example: Using receive_response()
use rusty_claw::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let options = ClaudeAgentOptions::default();
let mut client = ClaudeClient::new(options)?;
client.connect().await?;
// send_message returns a ResponseStream
let stream = client.send_message("Summarize this repo").await?;
// receive_response collects all messages until Result
let messages = stream.receive_response().await?;
for msg in messages {
println!("{:?}", msg);
}
client.close().await?;
Ok(())
}§Example: Control Operations
use rusty_claw::prelude::*;
// Start a task
let mut stream = client.send_message("Write a long essay about Rust").await?;
// Change your mind and interrupt
client.interrupt().await?;
// Switch to a faster model
client.set_model("claude-sonnet-4-5").await?;
// Change permission mode
client.set_permission_mode(PermissionMode::Ask).await?;§Example: Transport Injection (for testing)
use rusty_claw::prelude::*;
// Inject a custom transport (e.g., mock for tests)
// let transport = Box::new(MyMockTransport::new());
// let mut client = ClaudeClient::with_transport(options, transport)?;§Example: RAII with_client helper
use rusty_claw::client::with_client;
use rusty_claw::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let options = ClaudeAgentOptions::default();
with_client(options, |_client| async {
// Interact with _client here
Ok(())
}).await?;
Ok(())
}Structs§
- Claude
Client - Client for interactive sessions with Claude CLI
- Response
Stream - Stream of response messages from Claude CLI for a single conversation turn
Functions§
- with_
client - Run a closure with a freshly connected
ClaudeClient, ensuringclose()is called on exit.
Type Aliases§
- ClaudeSDK
Client - Alias for
ClaudeClientmatching the Python SDK’sClaudeSDKClientclass name.