A Rust SDK for building AI agents with first-class streaming, tool execution, and session management.
Arky treats CLI-backed AI providers (Claude Code, Codex) as first-class streaming runtimes -- not thin text generators. It normalizes provider-specific protocols into a unified event stream, manages session persistence and replay, and gives you composable hooks, tools, and MCP integration out of the box.
- Streaming-first architecture -- real-time
AgentEventstreams with text deltas, tool lifecycle, and extended thinking - Multiple providers -- Claude Code CLI, Codex app server, plus Claude-compatible wrappers (Bedrock, Vertex, OpenRouter, Ollama, and more)
- Tool system with
#[tool]macro -- turn any async function into an agent tool with auto-generated JSON schemas - MCP integration -- client/server bridge for importing and exposing tools via the Model Context Protocol
- Session persistence & replay -- in-memory or SQLite-backed session stores with event-sourced replay and resume
- Lifecycle hooks -- intercept tool calls, session events, stop decisions, and prompt submissions
- Error classification -- structured error codes with retryability hints, HTTP status mapping, and correction context
- Cooperative cancellation --
CancellationTokenthroughout the entire stack for graceful abort - HTTP/SSE server -- expose agents over HTTP with Server-Sent Events streaming
Add Arky to your project:
[dependencies]
arky = { version = "0.1", features = ["claude-code"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }Build and run an agent:
use arky::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider = ClaudeCodeProvider::new();
let agent = Agent::builder()
.provider(provider)
.model("sonnet")
.build()?;
let response = agent.prompt("Summarize the repository layout.").await?;
println!("{:?}", response.message);
Ok(())
}use arky::prelude::*;
use futures::StreamExt;
let mut stream = agent.stream("Plan a release checklist.").await?;
while let Some(event) = stream.next().await {
match event? {
AgentEvent::MessageUpdate { delta, .. } => println!("{delta:?}"),
AgentEvent::ToolExecutionStart { tool_name, .. } => {
println!("tool: {tool_name}");
}
_ => {}
}
}The #[tool] proc macro turns async functions into agent tools with zero boilerplate:
use arky::prelude::*;
#[tool]
async fn search(query: String) -> String {
/// Search the web for information
format!("Results for: {query}")
}
let agent = Agent::builder()
.provider(provider)
.tool(SearchTool)
.build()?;Tools support cancellation tokens, structured ToolResult returns, and JSON input schemas generated automatically via schemars.
Wraps the Claude CLI with streaming JSON parsing, nested tool tracking, and native session resume.
let provider = ClaudeCodeProvider::with_config(ClaudeCodeProviderConfig {
binary: "claude".to_owned(),
..Default::default()
});Claude-compatible wrappers are also available: BedrockProvider, VertexProvider, OpenRouterProvider, VercelProvider, OllamaProvider, ZaiProvider, MoonshotProvider, and MinimaxProvider.
Speaks newline-delimited JSON-RPC with the Codex app server, with thread-aware routing for concurrent sessions.
let provider = CodexProvider::with_config(CodexProviderConfig {
binary: "codex".to_owned(),
..Default::default()
});Arky persists transcript messages, replay events, and turn checkpoints for safe resume:
// In-memory for development
let store = InMemorySessionStore::new();
// SQLite for production (requires "sqlite" feature)
let store = SqliteSessionStore::new("sessions.db").await?;
let agent = Agent::builder()
.provider(provider)
.session_store(store)
.build()?;
// Resume a previous session
agent.resume(session_id).await?;Intercept and control agent behavior at every stage:
use arky::prelude::*;
struct SafetyHooks;
#[async_trait]
impl Hooks for SafetyHooks {
async fn before_tool_call(
&self,
ctx: &BeforeToolCallContext,
_cancel: CancellationToken,
) -> Result<Verdict, HookError> {
if ctx.tool_call.name.contains("dangerous") {
return Ok(Verdict::block("Tool not allowed"));
}
Ok(Verdict::Allow)
}
}
let agent = Agent::builder()
.provider(provider)
.hooks(SafetyHooks)
.build()?;Hooks support before_tool_call, after_tool_call, session_start, session_end, on_stop, and user_prompt_submit. Compose multiple hooks with HookChain and configure per-hook failure modes (FailOpen / FailClosed).
Import tools from MCP servers or expose your tools as an MCP server:
// Import tools from an MCP server
let client = McpClient::connect(McpStdioClientConfig {
command: "my-mcp-server".to_owned(),
args: vec![],
env: Default::default(),
}).await?;
// Expose tools as an MCP server
let server = McpServer::builder()
.tools(tool_registry)
.transport(McpServerTransport::Http { port: 3000 })
.build()?;Tools follow canonical naming: mcp/<server>/<tool_name>.
Arky is a Cargo workspace with narrowly-scoped crates that evolve independently:
Layer Crates Responsibility
───── ────── ──────────────
Leaf arky-error, arky-protocol, Shared contracts, IDs, protocol
arky-config, arky-tools-macros shapes, configuration, proc macros
Foundation arky-tools, arky-hooks, Tool registry, lifecycle hooks,
arky-session, arky-provider persistence, provider abstractions
Integration arky-mcp MCP client/server bridge
Providers arky-claude-code, arky-codex CLI-backed provider implementations
Orchestration arky-core Agent loop, turn runtime, replay,
event fanout
Exposure arky-server HTTP and SSE runtime
Facade arky Curated public API with
feature-gated re-exports
| Feature | Description | Default |
|---|---|---|
claude-code |
Claude Code CLI provider | Yes |
codex |
Codex app server provider | Yes |
sqlite |
SQLite session persistence | No |
server |
HTTP/SSE server runtime | No |
full |
All optional features | No |
# Format code (uses nightly for unstable rustfmt options)
make fmt
# Run lints (clippy with -D warnings)
make lint
# Run all tests
make test
# Full verification (fmt + lint + test)
make verify
# Build in release mode
make build
# Run live provider examples
cargo run -p arky --example 01_claude_basic
cargo run -p arky --example 09_live_matrix -- allRequires Rust 1.94.0+. The workspace uses nightly cargo fmt for unstable formatting options.
- Getting Started -- from
cargo addto a running agent - Architecture -- crate layout, design goals, and runtime flows
- Live Examples -- self-checking provider validation scenarios
Apache-2.0