Skip to main content

Module client

Module client 

Source
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§

ClaudeClient
Client for interactive sessions with Claude CLI
ResponseStream
Stream of response messages from Claude CLI for a single conversation turn

Functions§

with_client
Run a closure with a freshly connected ClaudeClient, ensuring close() is called on exit.

Type Aliases§

ClaudeSDKClient
Alias for ClaudeClient matching the Python SDK’s ClaudeSDKClient class name.