#claude #protocols #json

claude-codes

A tightly typed Rust interface for the Claude Code JSON protocol

16 releases (8 stable)

new 2.1.19 Feb 17, 2026
2.1.18 Jan 26, 2026
0.3.0 Aug 30, 2025
0.2.1 Aug 28, 2025
0.0.5 Aug 24, 2025

#276 in Encoding

Download history 8/week @ 2025-10-21 1/week @ 2025-10-28 45/week @ 2026-01-06 38/week @ 2026-01-13 26/week @ 2026-01-20

98 downloads per month

Apache-2.0

245KB
4.5K SLoC

claude-codes

Crates.io Documentation CI License Downloads

A typed Rust interface for the Claude Code JSON protocol.

Overview

This library provides type-safe bindings for communicating with the Claude CLI via its JSON Lines protocol. It handles message serialization, streaming responses, and session management.

Note: The Claude CLI protocol is unstable and may change between versions. This crate tracks protocol changes and will warn if you're using an untested CLI version.

Features

  • Type-safe message encoding/decoding
  • JSON Lines protocol support with streaming
  • Async and sync client implementations
  • Image support (JPEG, PNG, GIF, WebP) with base64 encoding
  • Tool use blocks for Claude's tool capabilities
  • OAuth and API key authentication via environment variables
  • UUID-based session management

Installation

Default Installation (All Features)

cargo add claude-codes

Requires the Claude CLI (claude binary) to be installed and available in PATH.

Feature Flags

  • types - Core message types only (WASM-compatible, minimal dependencies)
  • sync-client - Synchronous client with blocking I/O
  • async-client - Asynchronous client with tokio runtime
  • Default - All features enabled

Types Only

[dependencies]
claude-codes = { version = "2", default-features = false, features = ["types"] }

Sync Client Only

[dependencies]
claude-codes = { version = "2", default-features = false, features = ["sync-client"] }

Async Client Only

[dependencies]
claude-codes = { version = "2", default-features = false, features = ["async-client"] }

WASM Support

The types feature is fully compatible with wasm32-unknown-unknown, making it suitable for sharing Claude message types between native and browser/WASM code:

[dependencies]
claude-codes = { version = "2", default-features = false, features = ["types"] }

This gives you access to all the typed message structures (ClaudeInput, ClaudeOutput, ContentBlock, etc.) without pulling in tokio or other native-only dependencies. Useful for:

  • Frontend applications that communicate with a Claude proxy
  • Shared type definitions across native backend and WASM frontend
  • Any WASM context needing Claude protocol types

Usage

Async Client

use claude_codes::AsyncClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = AsyncClient::with_defaults().await?;

    let mut stream = client.query_stream("What is 2 + 2?").await?;

    while let Some(response) = stream.next().await {
        match response {
            Ok(output) => println!("Got: {}", output.message_type()),
            Err(e) => eprintln!("Error: {}", e),
        }
    }

    Ok(())
}

Sync Client

use claude_codes::{SyncClient, ClaudeInput};
use uuid::Uuid;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = SyncClient::with_defaults()?;

    let input = ClaudeInput::user_message("What is 2 + 2?", Uuid::new_v4());
    let responses = client.query(input)?;

    for response in responses {
        println!("Got: {}", response.message_type());
    }

    Ok(())
}

Sending Images

use claude_codes::{AsyncClient, ClaudeInput};
use base64::{engine::general_purpose::STANDARD, Engine};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = AsyncClient::with_defaults().await?;

    let image_data = std::fs::read("diagram.png")?;
    let base64_image = STANDARD.encode(&image_data);

    let input = ClaudeInput::user_message_with_image(
        base64_image,
        "image/png".to_string(),
        Some("What's in this image?".to_string()),
        uuid::Uuid::new_v4(),
    )?;

    client.send(&input).await?;

    Ok(())
}

Raw Protocol Access

use claude_codes::{Protocol, ClaudeOutput};

let json_line = r#"{"type":"assistant","message":{...}}"#;
let output: ClaudeOutput = Protocol::deserialize(json_line)?;

let serialized = Protocol::serialize(&output)?;

Compatibility

Tested version: Claude CLI 2.1.3

If you're using a different CLI version, please report whether it works at: https://github.com/meawoppl/rust-claude-codes/issues

Include:

  • Your Claude CLI version (claude --version)
  • Whether messages serialized/deserialized correctly
  • Any errors encountered

License

Apache-2.0. See LICENSE.

Contributing

Contributions welcome. Any contribution submitted for inclusion will be licensed under Apache-2.0.

Dependencies

~1.6–3.5MB
~64K SLoC