claude-code-sdk-rust 0.1.1

Async Rust SDK for the Claude Code CLI: streaming agent turns, tool use, and sessions.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# claude-code-sdk-rust

[![crates.io](https://img.shields.io/crates/v/claude-code-sdk-rust.svg)](https://crates.io/crates/claude-code-sdk-rust)
[![docs.rs](https://docs.rs/claude-code-sdk-rust/badge.svg)](https://docs.rs/claude-code-sdk-rust)
[![license](https://img.shields.io/crates/l/claude-code-sdk-rust.svg)](./LICENSE)

An async Rust SDK for the Claude Code CLI.

> **Crate name vs. import path:** this crate is published on crates.io as
> **`claude-code-sdk-rust`**, but its library import path is **`claude_agent_sdk`**.
> Add `claude-code-sdk-rust` to your `Cargo.toml` and write
> `use claude_agent_sdk::...;` in your code.

This crate provides a Tokio-native SDK for driving an authenticated local `claude` CLI process with typed options, message parsing, interactive sessions, control requests, SDK MCP servers, and local/session-store helpers. It is modeled after the Python `claude-agent-sdk` API while using Rust-native async, traits, and type-safe data structures.

## Status

This repository is an SDK implementation, not an official Anthropic package. It targets parity with the Python Claude Agent SDK public behavior and is verified by the local test suite.

Verification:

```bash
cargo test
cargo check --features otel
```

The normal test suite avoids paid/authenticated CLI calls. The ignored `e2e_cli` smoke test can be run separately when the Claude CLI is authenticated.

## Requirements

- Rust 1.70+
- Tokio runtime
- Claude Code CLI installed and authenticated
- `claude` available on `PATH`, or set via `ClaudeAgentOptions::builder().cli_path(...)`

## Installation

Add the crate from crates.io:

```toml
[dependencies]
claude-code-sdk-rust = "0.1"
tokio = { version = "1", features = ["full"] }
```

Then import it through its library path, `claude_agent_sdk`:

```rust
use claude_agent_sdk::query;
```

Optional OpenTelemetry propagation support:

```toml
[dependencies]
claude-code-sdk-rust = { version = "0.1", features = ["otel"] }
```

Or track the development branch directly from Git:

```toml
[dependencies]
claude-code-sdk-rust = { git = "https://github.com/PandelisZ/claude-agent-sdk-rust" }
```

## Quick start

### One-shot query

```rust
use claude_agent_sdk::query;

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let result = query("Explain ownership in Rust in two sentences.", None).await?;
    println!("{}", result.content);
    Ok(())
}
```

### Full message sequence

```rust
use claude_agent_sdk::query_messages;

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let messages = query_messages("List the files you would inspect first.", None).await?;

    for message in messages {
        println!("{message:?}");
    }

    Ok(())
}
```

### Streamed one-shot input

```rust
use claude_agent_sdk::query_stream_messages;

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let stream = futures::stream::iter(vec![
        serde_json::json!({
            "type": "user",
            "message": {"role": "user", "content": "First prompt frame"}
        }),
        serde_json::json!({
            "type": "user",
            "message": {"role": "user", "content": "Second prompt frame"}
        }),
    ]);

    let messages = query_stream_messages(stream, None).await?;
    println!("received {} messages", messages.len());
    Ok(())
}
```

## Interactive client

The interactive client mirrors the Python SDK's explicit connection model: create the client, call `connect()`, send prompts or control requests, receive messages, then `disconnect()` or `close()`.

```rust
use claude_agent_sdk::{ClaudeAgentClient, ClaudeAgentOptions, PermissionMode};

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let options = ClaudeAgentOptions::builder()
        .model("claude-sonnet-4-20250514")
        .permission_mode(PermissionMode::AcceptEdits)
        .build();

    let mut client = ClaudeAgentClient::new(options)?;
    client.connect().await?;

    client.query("Inspect this repository and summarize the test strategy.").await?;
    let messages = client.receive_response().await?;

    for message in messages {
        println!("{message:?}");
    }

    client.disconnect().await?;
    Ok(())
}
```

Convenience connection helpers are also available:

```rust
client.connect_with_prompt("Start by reading Cargo.toml").await?;
client.connect_with_stream(prompt_stream).await?;
```

## Streaming responses

```rust
use claude_agent_sdk::{ClaudeAgentClient, ClaudeAgentOptions, StreamEvent};

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let mut client = ClaudeAgentClient::new(ClaudeAgentOptions::default())?;
    client.connect().await?;

    let mut events = client.stream_message("Write a short haiku about compilers.").await?;
    while let Some(event) = events.recv().await {
        match event {
            StreamEvent::ContentChunk(text) => print!("{text}"),
            StreamEvent::Complete(response) => println!("\nfinished: {:?}", response.stop_reason),
            StreamEvent::Error(error) => eprintln!("error: {error}"),
            _ => {}
        }
    }

    client.disconnect().await?;
    Ok(())
}
```

## Configuration

Use `ClaudeAgentOptions::builder()` for ergonomic configuration.

```rust
use claude_agent_sdk::{
    ClaudeAgentOptions, MCPServerConfig, PermissionMode, SettingSource, SkillsConfig,
};
use std::collections::HashMap;

let mut env = HashMap::new();
env.insert("RUST_LOG".to_string(), "info".to_string());

let options = ClaudeAgentOptions::builder()
    .cwd("/path/to/project")
    .model("claude-sonnet-4-20250514")
    .system_prompt("You are a careful Rust coding agent.")
    .allowed_tools(vec!["Read".into(), "Edit".into(), "Bash".into()])
    .permission_mode(PermissionMode::AskOnFirstUse)
    .setting_sources(vec![SettingSource::User, SettingSource::Project])
    .skills(SkillsConfig::All)
    .env(env)
    .mcp_server(
        "docs",
        MCPServerConfig::Stdio {
            command: "docs-mcp".into(),
            args: Some(vec!["--stdio".into()]),
            env: None,
        },
    )
    .build();
```

Supported option areas include:

- CLI path, working directory, environment, extra CLI args
- model, fallback model, betas, thinking config, task budgets
- system prompt presets/files, dynamic prompt controls, skills config
- allowed/disallowed tools and permission mode
- `can_use_tool` callback support for streaming/client mode
- hooks and hook event inclusion
- stdio, SSE, HTTP, raw/path MCP server config
- in-process SDK MCP servers
- sandbox settings
- session resume, continue, fork, and session-store mirroring
- stderr callback
- optional OpenTelemetry context propagation with the `otel` feature

## SDK MCP servers

The crate can host in-process SDK MCP tools and bridge them through Claude Code control requests.

```rust
use claude_agent_sdk::{create_sdk_mcp_server, tool, ClaudeAgentOptions, MCPContent};

let server = create_sdk_mcp_server(
    "local-tools",
    vec![tool(
        "greet",
        "Greet a user",
        serde_json::json!({
            "type": "object",
            "properties": {"name": {"type": "string"}},
            "required": ["name"]
        }),
        |input| {
            let name = input.get("name").and_then(|v| v.as_str()).unwrap_or("there");
            Ok(vec![MCPContent::Text { text: format!("Hello, {name}!") }])
        },
    )],
);

let options = ClaudeAgentOptions::builder()
    .sdk_mcp_server("local-tools", server)
    .build();
```

Use `create_sdk_mcp_server_with_version` and `tool_with_annotations` when the CLI needs server version metadata or MCP tool annotations.

## Permission callbacks

```rust
use claude_agent_sdk::{ClaudeAgentOptions, PermissionResult};

let options = ClaudeAgentOptions::builder()
    .can_use_tool(|tool_name, input, _context| async move {
        if tool_name == "Bash" {
            return Ok(PermissionResult::deny("Bash is disabled in this run"));
        }
        println!("allowing {tool_name} with input {input:?}");
        Ok(PermissionResult::allow())
    })
    .build();
```

`can_use_tool` requires streaming/client mode. String-based one-shot `query_messages(...)` rejects this callback because there is no live bidirectional control channel after spawning the subprocess.

## Hooks

Hooks are represented as callback matchers grouped by event name.

```rust
use claude_agent_sdk::{ClaudeAgentOptions, HookCallback, HookMatcher};

let callback = HookCallback::new(|input, _tool_use_id, _context| async move {
    println!("hook input: {input:?}");
    Ok(serde_json::json!({"continue": true}))
});

let options = ClaudeAgentOptions::builder()
    .hook("PreToolUse", HookMatcher::new(callback).matcher("Edit"))
    .include_hook_events(true)
    .build();
```

## Sessions

Local Claude transcript helpers are available from the crate root and from `claude_agent_sdk::sessions`.

```rust
use claude_agent_sdk::{get_session_info, get_session_messages, list_sessions};

#[tokio::main]
async fn main() -> claude_agent_sdk::Result<()> {
    let sessions = list_sessions(None).await?;

    for session in sessions {
        println!("{} {:?}", session.id, session.title);
    }

    let info = get_session_info("session-uuid", None).await?;
    let messages = get_session_messages("session-uuid", None).await?;

    println!("{info:?}");
    println!("{} messages", messages.len());
    Ok(())
}
```

Session helpers include:

- list, inspect, page, and delete local sessions
- list and read subagent transcripts
- rename and tag sessions by appending metadata events
- fork sessions with remapped IDs
- import local transcripts into a custom session store
- store-backed list/read/mutate/fork helpers

## Custom session stores

Implement `SessionStore` for external persistence. Stores can opt into session listing by overriding `supports_list_sessions()` and `list_sessions(...)`.

```rust
use async_trait::async_trait;
use claude_agent_sdk::{
    Result, SessionKey, SessionListSubkeysKey, SessionStore, SessionStoreEntry,
    SessionStoreListEntry,
};

struct MyStore;

#[async_trait]
impl SessionStore for MyStore {
    async fn append(&self, key: SessionKey, entries: Vec<SessionStoreEntry>) -> Result<()> {
        println!("append {} entries to {key:?}", entries.len());
        Ok(())
    }

    async fn load(&self, key: SessionKey) -> Result<Option<Vec<SessionStoreEntry>>> {
        println!("load {key:?}");
        Ok(None)
    }

    async fn delete(&self, key: SessionKey) -> Result<()> {
        println!("delete {key:?}");
        Ok(())
    }

    async fn list_subkeys(&self, key: SessionListSubkeysKey) -> Result<Vec<String>> {
        println!("list subkeys for {key:?}");
        Ok(Vec::new())
    }

    fn supports_list_sessions(&self) -> bool {
        true
    }

    async fn list_sessions(&self, _project_key: &str) -> Result<Vec<SessionStoreListEntry>> {
        Ok(Vec::new())
    }
}
```

## Error handling

Most functions return `claude_agent_sdk::Result<T>`, an alias for `Result<T, ClaudeSDKError>`.

Main error variants include:

- `CLIConnectionError`
- `CLINotFoundError`
- `ProcessError`
- `CLIJSONDecodeError`
- `MessageParseError`
- generic SDK/configuration errors

## Testing

Run the standard suite:

```bash
cargo test
```

Run feature verification:

```bash
cargo check --features otel
```

Run the ignored real CLI smoke test only when authenticated and prepared for possible API usage:

```bash
cargo test --test e2e_cli -- --ignored
```

## Crate layout

- `client` - interactive client
- `query` - one-shot query helpers
- `types` - public SDK data types
- `options` - builder API
- `mcp` - SDK MCP server/tool helpers
- `session_store` - custom store trait and in-memory store
- `sessions` - local and store-backed session helpers
- `internal` - CLI args, transport, protocol, control, parsing, mirroring

## License

MIT