Skip to main content

Crate agent_sdk

Crate agent_sdk 

Source
Expand description

§Agent SDK

A Rust SDK for building AI agents powered by large language models (LLMs).

This crate provides the infrastructure to build agents that can:

  • Converse with users via multiple LLM providers
  • Execute tools to interact with external systems
  • Persist turn events for downstream consumers and UIs
  • Persist conversation history and state

For task-oriented recipes — tools, typed tools, structured output, streaming, MCP (local + remote HTTP), durable serving, and human-in-the-loop — see the cookbook and the runnable examples/.

§Quick Start

Ask a question and print the answer — the whole 30-second path:

use agent_sdk::{builder, ThreadId, providers::AnthropicProvider};

let agent = builder::<()>()
    .provider(AnthropicProvider::from_env()) // reads ANTHROPIC_API_KEY
    .build();

let answer = agent.ask(ThreadId::new(), "What is the capital of France?").await?;
println!("{answer}");

ask builds the ToolContext and CancellationToken internally and returns the assembled assistant text. When you need application context, a confirmation flow, explicit cancellation, or the raw AgentRunState, drop down to run. The runnable example below uses a tiny stub provider so it compiles and runs under cargo test --doc with no network and no API key:

use std::sync::Arc;
use agent_sdk::{
    builder, AgentEvent, AgentInput, CancellationToken, EventStore, InMemoryEventStore,
    ThreadId, ToolContext,
};
use agent_sdk::llm::{
    ChatOutcome, ChatRequest, ChatResponse, ContentBlock, LlmProvider, StopReason, Usage,
};
use async_trait::async_trait;

// A stub provider that always replies with a fixed line. Keeps the
// quickstart key-free and offline. Swap for `AnthropicProvider` (below)
// for real conversations.
struct StubProvider;

#[async_trait]
impl LlmProvider for StubProvider {
    async fn chat(&self, _request: ChatRequest) -> anyhow::Result<ChatOutcome> {
        Ok(ChatOutcome::Success(ChatResponse {
            id: "stub".to_string(),
            content: vec![ContentBlock::Text { text: "Paris.".to_string() }],
            model: self.model().to_string(),
            stop_reason: Some(StopReason::EndTurn),
            usage: Usage {
                input_tokens: 0,
                output_tokens: 0,
                cached_input_tokens: 0,
                cache_creation_input_tokens: 0,
            },
        }))
    }
    fn model(&self) -> &str { "stub-model" }
    fn provider(&self) -> &'static str { "stub" }
}

// 1. Build the agent.
let event_store = Arc::new(InMemoryEventStore::new());
let agent = builder::<()>()
    .provider(StubProvider)
    .event_store(event_store.clone())
    .build();

// 2. Run a conversation.
let thread_id = ThreadId::new();
let final_state = agent.run(
    thread_id.clone(),
    AgentInput::Text("What is the capital of France?".to_string()),
    ToolContext::new(()),
    CancellationToken::new(),
);
let _ = final_state.await?;

// 3. Read persisted events.
let mut reply = String::new();
for envelope in event_store.get_events(&thread_id).await? {
    match envelope.event {
        AgentEvent::Text { text, .. } => reply.push_str(&text),
        AgentEvent::Done { .. } => break,
        _ => {}
    }
}
assert_eq!(reply, "Paris.");

§Run with a real key

For a live conversation, depend on the anthropic provider and read your key from the environment — the only change is the provider line:

use agent_sdk::{builder, InMemoryEventStore, providers::AnthropicProvider};
use std::sync::Arc;

let api_key = std::env::var("ANTHROPIC_API_KEY")?;
let event_store = Arc::new(InMemoryEventStore::new());
let agent = builder::<()>()
    .provider(AnthropicProvider::sonnet(api_key))
    .event_store(event_store)
    .build();

§Core Concepts

§Agent Loop

The AgentLoop orchestrates the conversation cycle:

  1. User sends a message
  2. Agent sends message to LLM
  3. LLM responds with text and/or tool calls
  4. Agent executes tools and feeds results back to LLM
  5. Repeat until LLM responds with only text

Use builder() to construct an agent:

use agent_sdk::{builder, AgentConfig, providers::AnthropicProvider};

let agent = builder::<()>()
    .provider(AnthropicProvider::from_env())
    .config(AgentConfig {
        max_turns: Some(20),
        system_prompt: "You are a helpful assistant.".into(),
        ..Default::default()
    })
    .build();

§Tools

Tools let the LLM interact with external systems. The lowest-ceremony way is the SimpleTool trait — a &'static str name, no ToolName type:

use agent_sdk::{SimpleTool, ToolContext, ToolResult};
use serde_json::{json, Value};
use std::future::Future;

struct WeatherTool;

impl SimpleTool<()> for WeatherTool {
    fn name(&self) -> &'static str { "get_weather" }
    fn description(&self) -> &'static str { "Get current weather for a city" }
    fn input_schema(&self) -> Value {
        json!({ "type": "object", "properties": { "city": { "type": "string" } } })
    }

    fn execute(
        &self,
        _ctx: &ToolContext<()>,
        input: Value,
    ) -> impl Future<Output = anyhow::Result<ToolResult>> + Send {
        async move {
            let city = input["city"].as_str().unwrap_or("Unknown");
            Ok(ToolResult::success(format!("Weather in {city}: Sunny, 72°F")))
        }
    }
}

Register it with ToolRegistry::register_simple:

use agent_sdk::{builder, ToolRegistry, providers::AnthropicProvider};

let mut tools = ToolRegistry::new();
tools.register_simple(WeatherTool);

let agent = builder::<()>()
    .provider(AnthropicProvider::from_env())
    .tools(tools)
    .build();

For full control over the serialized tool name, implement the Tool trait directly with a strongly-typed ToolName (e.g. a #[derive(Serialize, Deserialize)] enum or the built-in DynamicToolName).

§Tool Tiers

Tools are classified by permission level via ToolTier:

TierDescriptionExample
ToolTier::ObserveRead-only, always allowedGet balance, read file
ToolTier::ConfirmRequires user confirmationSend email, transfer funds

§Lifecycle Hooks

Implement AgentHooks to intercept and control agent behavior:

use agent_sdk::{AgentHooks, ToolDecision, ToolInvocation, ToolResult, ToolTier};
use async_trait::async_trait;

struct MyHooks;

#[async_trait]
impl AgentHooks for MyHooks {
    async fn pre_tool_use(&self, invocation: &ToolInvocation) -> ToolDecision {
        println!("Tool called: {}", invocation.tool_name);
        match invocation.tier {
            ToolTier::Observe => ToolDecision::Allow,
            ToolTier::Confirm => ToolDecision::RequiresConfirmation(
                "Please confirm this action".into()
            ),
        }
    }

    async fn post_tool_use(&self, tool_name: &str, result: &ToolResult) {
        println!("{tool_name} completed: {}", result.success);
    }
}

Built-in hook implementations:

§Events

The agent emits AgentEvents during execution for real-time updates:

EventDescription
AgentEvent::StartAgent begins processing
AgentEvent::TextText response from LLM
AgentEvent::TextDeltaStreaming text chunk
AgentEvent::ToolCallStartTool execution starting
AgentEvent::ToolCallEndTool execution completed
AgentEvent::TurnCompleteOne LLM round-trip finished
AgentEvent::DoneAgent completed successfully
AgentEvent::ErrorAn error occurred

§Task Tracking

Use TodoWriteTool and TodoReadTool to track task progress:

use agent_sdk::todo::{TodoState, TodoWriteTool, TodoReadTool};
use std::sync::Arc;
use tokio::sync::RwLock;

let state = Arc::new(RwLock::new(TodoState::new()));
let write_tool = TodoWriteTool::new(Arc::clone(&state));
let read_tool = TodoReadTool::new(state);

Task states: Pending (○), InProgress (⚡), Completed (✓)

§Custom Context

Pass application-specific data to tools via the generic type parameter:

use agent_sdk::{DynamicToolName, Tool, ToolContext, ToolResult, ToolTier};
use serde_json::Value;
use std::future::Future;

// Your application context
struct AppContext {
    user_id: String,
    // database: Database,
}

struct UserInfoTool;

impl Tool<AppContext> for UserInfoTool {
    type Name = DynamicToolName;

    fn name(&self) -> DynamicToolName { DynamicToolName::new("get_user_info") }
    fn display_name(&self) -> &'static str { "User Info" }
    fn description(&self) -> &'static str { "Get info about current user" }
    fn input_schema(&self) -> Value { serde_json::json!({"type": "object"}) }

    fn execute(
        &self,
        ctx: &ToolContext<AppContext>,
        _input: Value,
    ) -> impl Future<Output = anyhow::Result<ToolResult>> + Send {
        let user_id = ctx.app.user_id.clone();
        async move {
            Ok(ToolResult::success(format!("User: {user_id}")))
        }
    }
}

§Workspace Architecture

The SDK is split into focused crates. This agent-sdk crate is the public façade — it re-exports everything you need so downstream users only depend on agent-sdk.

CratePurpose
agent_sdk_foundationData-only contract types (IDs, events, LLM messages)
agent_sdk_toolsTool traits, registry, hooks, stores, environment
agent_sdk_providersLLM provider trait and first-party implementations
agent-serverServer-side orchestration (internal, not published)
agent-sdkThis crate — façade with agent loop, examples, and convenience re-exports

§Modules

ModuleDescription
providersLLM provider implementations
primitive_toolsBuilt-in file operation tools (Read, Write, Edit, Glob, Grep, Bash)
llmLLM abstraction layer
subagentNested agent execution with SubagentFactory
mcpModel Context Protocol client (stdio + streamable-HTTP/SSE, resources/prompts)
todoTask tracking tools (TodoWriteTool, TodoReadTool)
user_interactionUser question/confirmation tools (AskUserQuestionTool)
webWeb search and fetch tools
skillsCustom skill/command loading
remindersSystem reminder infrastructure for agent guidance

§System Reminders

The SDK includes a reminder system that provides contextual guidance to the AI agent using the <system-reminder> XML tag pattern. Claude is trained to recognize these tags and follow the instructions without mentioning them to users.

use agent_sdk::reminders::{wrap_reminder, ReminderConfig, ReminderTracker};

// Wrap guidance in system-reminder tags
let reminder = wrap_reminder("Verify the output before proceeding.");

// Configure reminder behavior
let config = ReminderConfig::new()
    .with_todo_reminder_turns(5)
    .with_repeated_action_threshold(3);

§Feature Flags

Providers and the heavier tool families are gated behind cargo features so a minimal consumer only compiles (and only pulls the transitive dependencies of) what it uses. The common case — an Anthropic agent — works out of the box because anthropic is the only default feature.

FeatureDefaultPullsDescription
anthropicYesAnthropic Messages API provider
openaiNoOpenAI Chat Completions + Responses providers
openai-codexNotokio-tungsteniteOpenAI Codex / ChatGPT WebSocket provider
geminiNoGoogle Gemini provider
vertexNoGoogle Vertex AI provider (implies anthropic + gemini)
cloudflareNoCloudflare AI Gateway proxy (implies anthropic + openai + gemini)
webNohtml2textweb search + fetch tools
mcpNomcp Model Context Protocol client (stdio + streamable-HTTP/SSE)
skillsNoserde_yaml_ngskills markdown skill loader
otelNoopentelemetryOpenTelemetry tracing instrumentation

A minimal Anthropic-only build pulls no WebSocket, HTML, or YAML crates:

agent-sdk = { version = "0.8", default-features = false, features = ["anthropic"] }

When otel is enabled, the SDK emits OpenTelemetry spans for agent invocations, turns, LLM requests, tool execution, subagent runs, MCP operations, and context compaction. See the observability module for details.

Re-exports§

pub use reminders::ReminderConfig;
pub use reminders::ReminderTracker;
pub use reminders::ReminderTrigger;
pub use reminders::ToolReminder;
pub use reminders::append_reminder;
pub use reminders::wrap_reminder;
pub use subagent::METADATA_MAX_SUBAGENT_DEPTH;
pub use subagent::METADATA_SUBAGENT_DEPTH;
pub use subagent::SubagentConfig;
pub use subagent::SubagentFactory;
pub use subagent::SubagentTool;
pub use todo::TodoItem;
pub use todo::TodoReadTool;
pub use todo::TodoState;
pub use todo::TodoStatus;
pub use todo::TodoWriteTool;
pub use user_interaction::AskUserQuestionTool;
pub use user_interaction::ConfirmationRequest;
pub use user_interaction::ConfirmationResponse;
pub use user_interaction::QuestionOption;
pub use user_interaction::QuestionRequest;
pub use user_interaction::QuestionResponse;
pub use observability::CaptureDecision;otel
pub use observability::CaptureKind;otel
pub use observability::CaptureResult;otel
pub use observability::ObservabilityStore;otel
pub use observability::PayloadBundle;otel

Modules§

advanced
Server- and host-facing contract types.
builtin_tools
Bundled registration for the full set of built-in SDK tools.
context
Context compaction for long-running conversations.
llm
LLM provider trait, streaming types, and message data types.
mcpmcp
Model Context Protocol (MCP) client support.
model_capabilities
Model capability metadata and pricing.
observabilityotel
OpenTelemetry observability module.
prelude
The common imports for building an in-process agent.
primitive_tools
Primitive tools that work with the Environment abstraction.
providers
LLM Provider implementations.
reminders
System reminder infrastructure for agent guidance.
skillsskills
Skills system for loading agent behavior from markdown files.
subagent
Subagent support for spawning child agents.
todo
TODO task tracking for agents.
user_interaction
User interaction types and tools.
webweb
Web tools for search and fetching.

Macros§

tool
Define a tool inline, expanding to a fresh zero-sized struct plus a SimpleTool impl — the lowest-ceremony way to add a one-off tool in an example, test, or script.

Structs§

AgentCapabilities
Capabilities that control what the agent can do.
AgentConfig
Configuration for the agent loop
AgentError
Error from the agent loop.
AgentEventEnvelope
Envelope wrapping every AgentEvent with idempotency metadata.
AgentHandle
Handle to a persistent agent thread.
AgentLoop
The main agent loop that orchestrates LLM calls and tool execution.
AgentLoopBuilder
Builder for constructing an AgentLoop.
AgentLoopCompactionConfig
Configuration bundle for constructing an AgentLoop with compaction.
AgentState
Snapshot of agent state for checkpointing
AllowAllHooks
Hooks that allow all tools without confirmation
CancellationToken
A token which can be used to signal a cancellation request to one or more tasks.
ContentSource
Source data for image and document content blocks.
DefaultContextFactory
Default factory that delegates to crate::tools::ToolContext::from_seed.
DefaultHooks
Default hooks implementation that uses tier-based decisions
DynamicToolName
Dynamic tool name for runtime-created tools (MCP bridges, subagents).
ExecResult
Result from command execution
ExternalToolResult
A tool result provided by the external runtime for a specific tool call.
FileEntry
Entry in a directory listing
GrepMatch
Match result from grep operation
InMemoryEventStore
In-memory implementation of EventStore.
InMemoryExecutionStore
In-memory implementation of ToolExecutionStore.
InMemoryFileSystem
In-memory filesystem for testing
InMemoryStore
In-memory implementation of MessageStore and StateStore. Useful for testing and simple use cases.
LocalFileSystem
Local filesystem implementation using std::fs
LoggingHooks
Hooks that log all events (useful for debugging)
ModelCapabilities
NoopAuditSink
Default sink that discards every record.
NullEnvironment
A null environment that rejects all operations. Useful as a default when no environment is configured.
PendingToolCallInfo
Information about a pending tool call that was extracted from the LLM response.
PricePoint
Pricing
RedactionPolicy
Configurable redaction rules for tool audit records and observability payloads.
ResponseFormat
Requests that the model constrain its final answer to a JSON Schema.
RetryConfig
Configuration for retry behavior on transient errors.
RunOptions
Per-run trace metadata applied to every span emitted by the agent loop.
SequenceCounter
Monotonically increasing per-run counter for event ordering.
SimpleToolAdapter
Adapter that turns any SimpleTool into a full Tool with Name = DynamicToolName.
StoredTurnEvents
Stored event data for a single turn.
StructuredConfig
Bounds for the structured-output re-prompt loop.
StructuredOutput
A successfully validated structured output and the response that produced it.
ThinkingConfig
Configuration for extended thinking.
ThreadId
Unique identifier for a conversation thread
TokenUsage
Token usage statistics
ToolContext
Context passed to tool execution
ToolContextSeed
Durable inputs needed to reconstruct a crate::tools::ToolContext.
ToolExecution
Record of a tool execution for idempotency.
ToolInvocation
Structured input passed to the pre_tool_use hook for policy evaluation.
ToolRegistry
Registry of available tools.
ToolResult
Result of a tool execution
TurnOptions
Options that control how a single run_turn invocation behaves.
TypedToolAdapter
Adapter that turns any TypedTool into a full Tool.

Enums§

AgentEvent
Events emitted by the agent loop during execution. These are streamed to the client for real-time UI updates.
AgentInput
Input to start or resume an agent run.
AgentRunState
Outcome of running the agent loop.
ContentBlock
Effort
Effort level for adaptive thinking via output_config.
ExecutionStatus
Status of a tool execution for idempotency tracking.
PrimitiveToolName
Tool names for SDK’s built-in primitive tools.
RedactionLevel
How aggressively to redact a given field category.
SourceStatus
StructuredOutputError
Errors from the structured-output runner.
StructuredOutputSupport
How a provider satisfies a ResponseFormat structured-output request.
ThinkingMode
The mode of extended thinking.
ToolDecision
Decision returned by pre-tool hooks
ToolOutcome
Result of tool execution - may indicate async operation in progress.
ToolRuntime
How tool calls should be handled during a turn.
ToolStatus
Status update from an async tool operation.
ToolTier
Permission tier for tools

Constants§

REDACTED_MARKER
Redaction marker used for wholesale redaction (sensitive key match or full-string secret prefix). Entity-level masks use [REDACTED:<category>] — see crate::privacy.

Traits§

AgentHooks
Lifecycle hooks for the agent loop. Implement this trait to customize agent behavior.
AsyncTool
A tool that performs long-running async operations.
Environment
Environment abstraction for file and command operations.
EventStore
Trait for storing and retrieving turn-scoped event streams.
LlmProvider
MessageStore
Trait for storing and retrieving conversation messages. Implement this trait to persist messages to your storage backend.
ProgressStage
Marker trait for tool progress stages (type-safe, like ToolName).
SimpleTool
An ergonomic Tool whose name is a plain string.
StateStore
Trait for storing agent state checkpoints. Implement this to enable conversation recovery and resume.
Tool
Definition of a tool that can be called by the agent.
ToolAuditSink
Async sink that receives one ToolAuditRecord per tool-call lifecycle transition.
ToolExecutionStore
Store for tracking tool executions (idempotency).
ToolLogic
The execute-only half of a tool, used as the target of the #[derive(Tool)] / #[derive(TypedTool)] ergonomics macros.
ToolName
Marker trait for tool names.
TypedTool
A tool whose model-emitted arguments are validated against a typed, deserializable Input before execute runs.

Functions§

builder
Create a new builder for constructing an AgentLoop.
get_model_capabilities
invalid_tool_input_result
Synthesise the structured validation-error ToolResult returned to the model when its arguments fail to deserialize into a TypedTool::Input.
redact_error
Apply redaction rules to an error string based on the given policy’s error level. Same semantics as redact_string, but gated by RedactionPolicy::error_level.
redact_for_observability
Combined helper for the SDK observability boundary.
redact_string
Apply redaction rules to a string value based on the given policy’s output level.
redact_value
Apply redaction rules to a JSON value based on the given policy’s input level.
run_structured
Run a bounded, schema-validated structured-output exchange against provider.
stage_to_string
Helper to get string representation of a progress stage via serde.
supported_model_capabilities
tool_name_from_str
Parse a tool name from string via serde.
tool_name_to_string
Helper to get string representation of a tool name via serde.
validate_tool_input
Deserialize raw model args into a typed Input, or synthesise the structured validation-error result.

Derive Macros§

Tool
Derive an agent_sdk::SimpleTool impl from struct-level #[tool(...)] attributes.
ToolName
Derive agent_sdk::ToolName (plus the Serialize / Deserialize it requires) for an enum, removing the marker-impl boilerplate.
TypedTool
Derive an agent_sdk::TypedTool impl (typed Input + runtime validation) from struct-level #[tool(...)] attributes.