---
title: "Rust SDK overview"
description: "Embed Meerkat as a Rust library: the full engine with no subprocess overhead."
icon: "code"
---

The Rust SDK is the primary interface. The Python/TypeScript SDKs and all API servers are thin wrappers over this same engine. The default embedding path is runtime-backed `SessionService`, which keeps Rust aligned with the same session lifecycle semantics used by every other Meerkat surface while still avoiding subprocess or JSON-RPC overhead.

## Method overview

| Area | Method / Type | Purpose |
|------|---------------|---------|
| **Setup** | `Config::load()` | Load configuration from disk |
| | `AgentFactory::new(store_root)` | Create a factory for building agents |
| | `open_realm_persistence_in(...)` | Open a realm-backed persistence bundle |
| | `build_persistent_service(factory, config, cap, persistence)` | Build the runtime-backed persistent session substrate |
| | `build_ephemeral_service(factory, config, cap)` | Build an in-memory Queue-only substrate |
| **Sessions** | `service.create_session(req)` | Create a session and run the first turn |
| | `service.start_turn(id, req)` | Continue an existing session |
| | `service.read(id)` | Read session state |
| | `service.list()` | List active sessions |
| | `service.archive(id)` | Remove a session |
| **Agent** | `agent.run(prompt)` | Run agent with a prompt |
| | `agent.run_with_events(prompt, tx)` | Run with event streaming |
| | `agent.cancel()` | Cancel the current run |

## Installation

<Steps>
  <Step title="Add the dependency">
    ```toml
    [dependencies]
    meerkat = "0.6.19"
    tokio = { version = "1", features = ["full"] }
    ```
  </Step>
  <Step title="Choose feature flags">
    The default includes all three LLM providers and nothing else — add subsystems as needed:
    <CodeGroup>
    ```toml Default (all providers, no storage)
    [dependencies]
    meerkat = "0.6.19"
    ```

    ```toml Add persistence
    [dependencies]
    meerkat = { version = "0.6.19", features = ["sqlite-store", "session-store"] }
    ```

    ```toml Full harness
    [dependencies]
    meerkat = { version = "0.6.19", features = [
        "sqlite-store", "session-store", "session-compaction",
        "memory-store-session", "comms", "mcp", "skills", "live"
    ] }
    ```

    ```toml Minimal (single provider)
    [dependencies]
    meerkat = { version = "0.6.19", default-features = false, features = ["anthropic"] }
    ```
    </CodeGroup>
  </Step>
</Steps>

<Accordion title="Feature flag reference">
| Feature | Description | Default |
|---------|-------------|---------|
| `anthropic` | Anthropic Claude API client | Yes |
| `openai` | OpenAI API client | Yes |
| `gemini` | Google Gemini API client | Yes |
| `all-providers` | Shorthand for all three providers | No |
| `sqlite-store` | SQLite-backed persistent realms | No |
| `jsonl-store` | File-based session persistence | No |
| `memory-store` | In-memory session storage (testing) | No |
| `session-store` | Persistent session lifecycle support | No |
| `session-compaction` | Auto-compact long conversations | No |
| `memory-store-session` | Semantic memory indexing | No |
| `comms` | Ed25519 inter-agent messaging | No |
| `mcp` | MCP protocol client and tool routing | No |
| `live` | Live-channel orchestration and realtime adapter helpers | No |
| `skills` | Composable knowledge packs | No |
</Accordion>

---

## Quick start

Production surfaces (CLI, REST, RPC, MCP) use the **runtime-backed** path where `SessionService` is substrate and `MeerkatMachine` owns keep-alive, Queue/Steer routing, and comms drain. See the [JSON-RPC API](/api/rpc) or [REST API](/api/rest) for those entry points.

For the main production embedding path, use the runtime-backed persistent flow:

```rust
use meerkat::{
    AgentFactory, Config, CreateSessionRequest, SessionService,
    build_persistent_service, open_realm_persistence_in,
    DeferredPromptPolicy,
};
use meerkat_core::service::InitialTurnPolicy;
use meerkat_store::RealmBackend;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::load().await?;
    let realms_root = std::env::current_dir()?.join(".rkat").join("realms");
    let (_manifest, persistence) = open_realm_persistence_in(
        &realms_root,
        "team-alpha",
        Some(RealmBackend::Sqlite),
        None,
    ).await?;
    let factory = AgentFactory::new(realms_root.clone()).runtime_root(realms_root);
    let service = build_persistent_service(factory, config, 64, persistence);

    let result = service.create_session(CreateSessionRequest {
        model: "claude-sonnet-4-6".into(),
        prompt: "What is the capital of France?".into(),
        render_metadata: None,
        system_prompt: Some("You are a helpful assistant.".into()),
        max_tokens: Some(1024),
        event_tx: None,
        skill_references: None,
        initial_turn: InitialTurnPolicy::RunImmediately,
        deferred_prompt_policy: DeferredPromptPolicy::Discard,
        build: None,
        labels: None,
    }).await?;

    println!("Response: {}", result.text);
    println!("Session ID: {}", result.session_id);
    Ok(())
}
```

For **testing or embedded use** where runtime semantics are intentionally not needed, `build_ephemeral_service` remains available as a direct Queue-only substrate.

---

## Sessions

`SessionService` is the canonical lifecycle API. All surfaces (CLI, REST, MCP, RPC) route through it.

### Multi-turn conversations

```rust
use meerkat::{
    CreateSessionRequest, DeferredPromptPolicy, SessionService,
    StartTurnRequest, StartTurnRuntimeSemantics,
};
use meerkat_core::service::InitialTurnPolicy;

// Turn 1: create session
let result = service.create_session(CreateSessionRequest {
    model: "claude-sonnet-4-6".into(),
    prompt: "My name is Alice.".into(),
    render_metadata: None,
    system_prompt: Some("You are a helpful assistant with memory.".into()),
    max_tokens: None,
    event_tx: None,
    skill_references: None,
    initial_turn: InitialTurnPolicy::RunImmediately,
    deferred_prompt_policy: DeferredPromptPolicy::Discard,
    build: None,
    labels: None,
}).await?;

let session_id = result.session_id;

// Turn 2: agent remembers "Alice"
let result = service.start_turn(&session_id, StartTurnRequest {
    prompt: "What's my name?".into(),
    system_prompt: None,
    event_tx: None,
    runtime: StartTurnRuntimeSemantics::default(),
}).await?;

// Read session state
let view = service.read(&session_id).await?;
println!("Messages: {}", view.state.message_count);

// Archive when done
service.archive(&session_id).await?;
```

### Deferred first-turn system prompt override

```rust
use meerkat::{
    CreateSessionRequest, DeferredPromptPolicy, SessionService,
    StartTurnRequest, StartTurnRuntimeSemantics,
};
use meerkat_core::service::InitialTurnPolicy;

let created = service.create_session(CreateSessionRequest {
    model: "claude-sonnet-4-6".into(),
    prompt: "Let's wait before we run.".into(),
    render_metadata: None,
    system_prompt: Some("You are the original prompt.".into()),
    max_tokens: None,
    event_tx: None,
    skill_references: None,
    initial_turn: InitialTurnPolicy::Defer,
    deferred_prompt_policy: DeferredPromptPolicy::Stage,
    build: None,
    labels: None,
}).await?;

let result = service.start_turn(&created.session_id, StartTurnRequest {
    prompt: "Now run with the first-turn override.".into(),
    system_prompt: Some("You are the deferred first-turn override.".into()),
    event_tx: None,
    runtime: StartTurnRuntimeSemantics::default(),
}).await?;
```

`system_prompt` on `StartTurnRequest` is only supported for a deferred session's
first turn. Once the session has existing history/messages, turn-time
`system_prompt` overrides are rejected.

### Error handling

```rust
use meerkat::SessionError;

match service.start_turn(&id, req).await {
    Ok(result) => println!("Response: {}", result.text),
    Err(SessionError::NotFound { id }) => println!("Session {} not found", id),
    Err(SessionError::Busy { id }) => println!("Session {} is busy, retry later", id),
    Err(e) => println!("Error: {}", e),
}
```

---

## Direct agent APIs

<Warning>
`Agent::run(...)` and `AgentBuilder` are expert-level escape hatches. Prefer `SessionService` for normal embedding so your Rust code follows the same runtime-backed session semantics as CLI, REST, RPC, MCP, Python, and TypeScript.
</Warning>

## Running agents directly

### Basic run

```rust
let result = agent.run("What is 2 + 2?".into()).await?;
println!("Answer: {}", result.text);
```

### Run with event streaming

```rust
use tokio::sync::mpsc;
use meerkat::AgentEvent;

let (tx, mut rx) = mpsc::channel::<AgentEvent>(100);

tokio::spawn(async move {
    while let Some(event) = rx.recv().await {
        match event {
            AgentEvent::TextDelta { delta } => print!("{}", delta),
            AgentEvent::ToolExecutionStarted { name, .. } => {
                println!("[Calling {}...]", name);
            }
            AgentEvent::TurnCompleted { usage, .. } => {
                println!("\n[Tokens: {}]", usage.total_tokens());
            }
            _ => {}
        }
    }
});

let result = agent.run_with_events("Tell me a story".into(), tx).await?;
```

### Agent methods

| Method | Description |
|--------|-------------|
| `run(prompt)` | Run agent with a `ContentInput` prompt (text or multimodal) |
| `run_with_events(prompt, tx)` | Run with event streaming; prompt is `ContentInput` |
| `session()` | Get current session (read-only) |
| `budget()` | Get current budget tracker |
| `state()` | Get current loop state |
| `cancel()` | Cancel the current run |

### Error handling

```rust
use meerkat::AgentError;

match agent.run("prompt".into()).await {
    Ok(result) => println!("Success: {}", result.text),
    Err(AgentError::LlmError(msg)) => println!("LLM error: {}", msg),
    Err(AgentError::TokenBudgetExceeded { used, limit }) => {
        println!("Token budget exceeded: {} / {}", used, limit);
    }
    Err(e) => println!("Other error: {}", e),
}
```

---

## Events

<Accordion title="All event types">
```rust
use meerkat::AgentEvent;

match event {
    // Session lifecycle
    AgentEvent::RunStarted { session_id, prompt } => {}
    AgentEvent::RunCompleted { session_id, result, usage } => {}
    AgentEvent::RunFailed { session_id, error } => {}

    // Hook lifecycle
    AgentEvent::HookStarted { hook_id, point } => {}
    AgentEvent::HookCompleted { hook_id, point, duration_ms } => {}
    AgentEvent::HookFailed { hook_id, point, error } => {}
    AgentEvent::HookDenied { hook_id, point, reason_code, message, .. } => {}

    // LLM interaction
    AgentEvent::TurnStarted { turn_number } => {}
    AgentEvent::ReasoningDelta { delta } => {}
    AgentEvent::ReasoningComplete { content } => {}
    AgentEvent::TextDelta { delta } => {}
    AgentEvent::TextComplete { content } => {}
    AgentEvent::ToolCallRequested { id, name, args } => {}
    AgentEvent::ToolResultReceived { id, name, content, is_error } => {}
    AgentEvent::TurnCompleted { stop_reason, usage } => {}

    // Tool execution
    AgentEvent::ToolExecutionStarted { id, name } => {}
    AgentEvent::ToolExecutionCompleted { id, name, result, content, is_error, duration_ms } => {}
    AgentEvent::ToolExecutionTimedOut { id, name, timeout_ms } => {}

    // Compaction
    AgentEvent::CompactionStarted { input_tokens, estimated_history_tokens, message_count } => {}
    AgentEvent::CompactionCompleted { summary_tokens, messages_before, messages_after } => {}
    AgentEvent::CompactionFailed { error } => {}

    // Budget
    AgentEvent::BudgetWarning { budget_type, used, limit, percent } => {}

    // Retry
    AgentEvent::Retrying { attempt, max_attempts, error, delay_ms } => {}

    // Skills
    AgentEvent::SkillsResolved { skills, injection_bytes } => {}
    AgentEvent::SkillResolutionFailed { skill_key, reason, reference, error } => {}

    // Comms interaction lifecycle
    AgentEvent::InteractionComplete { interaction_id, result } => {}
    AgentEvent::InteractionFailed { interaction_id, error } => {}
    AgentEvent::StreamTruncated { reason } => {}

    // Tool config changes
    AgentEvent::ToolConfigChanged { payload } => {}

    _ => {} // non_exhaustive: forward compatibility
}
```
</Accordion>

---

## Core types

### Message and ContentBlock

```rust
use meerkat::{Message, UserMessage, AssistantMessage, SystemMessage, ToolResult};
use meerkat::ContentBlock;

let system = Message::System(SystemMessage { content: "You are helpful.".to_string() });

// Text-only user message (convenience)
let user = Message::User(UserMessage::text("Hello!"));

// Multimodal user message with text and image
let user = Message::User(UserMessage {
    content: vec![
        ContentBlock::Text { text: "What is in this image?".to_string() },
        ContentBlock::Image {
            media_type: "image/png".to_string(),
            data: ImageData::Inline { data: base64_data },
        },
    ],
});
```

### ContentInput

`ContentInput` is the prompt type accepted by `CreateSessionRequest` and `StartTurnRequest`. It supports both text-only and multimodal prompts:

```rust
use meerkat::ContentInput;

// Text-only (most common) — implements From<&str> and From<String>
let prompt: ContentInput = "What is Rust?".into();

// Multimodal — blocks with mixed content types
let prompt = ContentInput::Blocks(vec![
    ContentBlock::Text { text: "Describe this image.".to_string() },
    ContentBlock::Image {
        media_type: "image/jpeg".to_string(),
        data: ImageData::Inline { data: base64_data },
    },
]);
```

### ToolCall and ToolResult

```rust
use meerkat::{ToolCall, ToolResult};

let tool_call = ToolCall {
    id: "tc_123".to_string(),
    name: "get_weather".to_string(),
    args: json!({"city": "Tokyo"}),
};

let result = ToolResult::new("tc_123".to_string(), "Sunny, 25C".to_string(), false);
let error = ToolResult::new("tc_123".to_string(), "City not found".to_string(), true);
```

### RunResult

```rust
let result: RunResult = agent.run("Hello".into()).await?;

println!("Response: {}", result.text);
println!("Session: {}", result.session_id);
println!("Tokens: {}", result.usage.total_tokens());
println!("Turns: {}", result.turns);
println!("Tool calls: {}", result.tool_calls);
```

---

## See also

- [Tools and stores](/rust/tools-and-stores) - tool system, session stores, MCP integration
- [Advanced](/rust/advanced) - expert-only direct agent construction, providers, budgets, and hooks
- [API reference](/reference/api-reference) - quick-lookup type index
