agentkit is a Rust toolkit for building LLM agent applications such as coding agents, assistant CLIs, and multi-agent tools.
The project is split into small crates behind feature flags so hosts can pull in only the pieces they need.
use agentkit_core::{Item, ItemKind};
use agentkit_loop::{
Agent, LoopStep, PromptCacheRequest, PromptCacheRetention, SessionConfig,
};
use agentkit_provider_openrouter::{OpenRouterAdapter, OpenRouterConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = OpenRouterConfig::from_env()?;
let adapter = OpenRouterAdapter::new(config)?;
let agent = Agent::builder()
.model(adapter)
.input(vec![Item::text(ItemKind::User, "Hello!")])
.build()?;
let mut driver = agent
.start(SessionConfig::new("chat").with_cache(
PromptCacheRequest::automatic().with_retention(PromptCacheRetention::Short),
))
.await?;
if let LoopStep::Finished(result) = driver.next().await? {
println!("Finished: {:?}", result.finish_reason);
}
Ok(())
}agentkit-core- transcript, parts, deltas, IDs, usage, and cancellation primitives
agentkit-capabilities- lower-level invocable/resource/prompt abstraction
agentkit-tools-core- tools, registry, executor, permissions, approvals
agentkit-loop- model session abstraction, driver, interrupts, tool roundtrips
agentkit-contextAGENTS.mdand skills loading
agentkit-mcp- MCP integration built on
rmcp: stdio + Streamable HTTP transports, discovery, lifecycle, auth + replay, tool/resource/prompt adapters, sampling/elicitation/roots responders, and a server-event broadcast
- MCP integration built on
agentkit-plugins- information-first Agent Plugins 1.0 parsing, validation, and portable asset discovery
agentkit-acp- Agent Client Protocol integration built on the official
agent-client-protocolSDK: session binding, observer routing, prompt conversion, approval resolvers, and a headless stdio runtime for standalone ACP agents
- Agent Client Protocol integration built on the official
agentkit-reporting- loop observers and reporting adapters
agentkit-compaction- compaction triggers, strategies, pipelines, backend hooks
agentkit-task-manager- task scheduling for tool execution: foreground, background, and detach-after-timeout routing
agentkit-tool-fs- filesystem tools
agentkit-tool-shell- shell execution tool
agentkit-tool-skills- progressive skill discovery and activation
agentkit-http- HTTP transport abstraction (
HttpClient,Http,HttpRequestBuilder) with a default reqwest-backed implementation and an optionalreqwest-middlewareadapter
- HTTP transport abstraction (
agentkit-adapter-completions- generic chat completions adapter base with buffered and SSE streaming turns
agentkit-provider-openrouter- OpenRouter adapter with streaming, tool calls, multimodal content, and prompt caching
agentkit-provider-openai- OpenAI adapter with streaming, tool calls, multimodal content, and prompt caching
agentkit-provider-anthropic- Anthropic Messages API adapter with streaming, prompt caching, extended thinking, and server-side tools (web search, web fetch, code execution)
agentkit-provider-baseten- Baseten Model API adapter with streaming and tool calls, including custom endpoints for dedicated deployments
agentkit-provider-cerebras- Cerebras Inference API adapter with streaming, reasoning, strict JSON schema, compression (msgpack/gzip), predicted outputs, service tiers, and Files + Batch API
agentkit-provider-ollama- Ollama adapter with streaming
agentkit-provider-vllm- vLLM adapter with streaming
agentkit-provider-groq- Groq adapter with streaming
agentkit-provider-mistral- Mistral adapter with streaming
agentkit- umbrella crate with feature-gated re-exports
Filesystem:
fs_read_file- supports optional
from/toline ranges
- supports optional
fs_write_filefs_replace_in_filefs_movefs_deletefs_list_directoryfs_create_directory
Shell:
shell_exec
The filesystem crate also supports session-scoped read-before-write enforcement through FileSystemToolResources and FileSystemToolPolicy.
- Set your OpenRouter API key and model — either through environment variables or directly in code via
OpenRouterConfig::new(api_key, model). - Run one of the examples.
Example commands:
cargo run -p openrouter-chat -- "hello"cargo run -p openrouter-coding-agent -- \
"Use fs_read_file on ./Cargo.toml and return only the workspace member count as an integer."cargo run -p openrouter-agent-cli -- --mcp-mock \
"Return only the secret from the MCP tool."openrouter-chat- minimal chat loop
- now supports
Ctrl-Cturn cancellation
openrouter-coding-agent- interactive coding-agent host with streaming delta rendering and filesystem tools
openrouter-context-agent- context loading from
AGENTS.mdand skills
- context loading from
openrouter-mcp-tool- MCP tool discovery and invocation
openrouter-subagent-tool- custom tool that runs a nested agent
openrouter-acp-trio- three agents (orchestrator, worker, reviewer) exposed as in-memory ACP endpoints, delegating to each other over the Agent Client Protocol
openrouter-compaction-agent- structural, semantic, and hybrid compaction
- semantic compaction uses a nested agent as the backend
openrouter-parallel-agent- async task manager with foreground fs tools and detach-after-timeout shell tools
TaskManagerHandleevent stream printed to stderr
openrouter-agent-cli- combined example using context, tools, shell, MCP, compaction, and reporting
anthropic-chat- streaming REPL against Anthropic's Messages API, with server tools
(
--web-search,--web-fetch,--code-exec), extended thinking (--thinking), and a streaming / buffered toggle (--streaming/--no-streaming)
- streaming REPL against Anthropic's Messages API, with server tools
(
cerebras-chat- interactive REPL against Cerebras
/v1/chat/completions; CLI flags cover everyCerebrasConfigknob (sampling, reasoning, response format, compression, service tier, predicted outputs, local tools) and slash commands (/show,/usage,/ratelimit,/headers,/models,/reset) surface runtime state
- interactive REPL against Cerebras
cerebras-batch- one-shot CLI over the Cerebras Files + Batch APIs:
files upload|list|get|content|delete,batches create|submit|list|get|cancel|wait, andrunto submit → wait → dump outputs
- one-shot CLI over the Cerebras Files + Batch APIs:
Build an agent with a provider adapter and an opening user turn, then drive the loop:
use agentkit_core::{Item, ItemKind};
use agentkit_loop::{
Agent, LoopInterrupt, LoopStep, PromptCacheRequest, PromptCacheRetention, SessionConfig,
};
use agentkit_provider_openrouter::{OpenRouterAdapter, OpenRouterConfig};
let adapter = OpenRouterAdapter::new(
OpenRouterConfig::new("sk-or-v1-...", "openrouter/auto")
.with_temperature(0.0),
)?;
let agent = Agent::builder()
.model(adapter)
// Optional — preload a prior transcript (system prompt or resumed
// session) and the next user turn. Both default to empty.
.input(vec![Item::text(ItemKind::User, "Hello!")])
.build()?;
let mut driver = agent
.start(SessionConfig::new("chat").with_cache(
PromptCacheRequest::automatic().with_retention(PromptCacheRetention::Short),
))
.await?;
// First next() dispatches the model directly because we preloaded input.
match driver.next().await? {
LoopStep::Finished(result) => { /* render result.items */ }
LoopStep::Interrupt(LoopInterrupt::ApprovalRequest(pending)) => {
/* blocking: approve or deny via the PendingApproval handle */
}
LoopStep::Interrupt(LoopInterrupt::AwaitingInput(req)) => {
/* cooperative: req.submit(&mut driver, more_items)? then call next() */
}
LoopStep::Interrupt(LoopInterrupt::AfterToolResult(_)) => { /* call next() to resume */ }
}AgentBuilder::transcript preloads the prior transcript as passive starting state — typically [system_item] for a fresh session, or a transcript loaded from disk when resuming. AgentBuilder::input preloads the next user turn into the driver's pending-input queue: when non-empty, the first next() dispatches the model directly; when left empty (the default for turn-based loops), the first next() yields AwaitingInput and every user turn flows through the InputRequest / ToolRoundInfo handles surfaced on the cooperative interrupts. There is no out-of-turn submit_input entry point.
Register filesystem tools with a path-scoped permission policy. Tool sources federate — call add_tool_source once per source (registry, MCP catalog reader, skill watcher, …) and the agent walks them in registration order:
use agentkit_core::MetadataMap;
use agentkit_loop::Agent;
use agentkit_tools_core::{
CompositePermissionChecker, PathPolicy, PermissionCode, PermissionDecision, PermissionDenial,
};
let permissions = CompositePermissionChecker::new(PermissionDecision::Deny(PermissionDenial {
code: PermissionCode::UnknownRequest,
message: "not allowed by policy".into(),
metadata: MetadataMap::new(),
}))
.with_policy(
PathPolicy::new()
.allow_root(std::env::current_dir()?)
.require_approval_outside_allowed(false),
);
let agent = Agent::builder()
.model(adapter)
.add_tool_source(agentkit_tool_fs::registry())
.permissions(permissions)
.build()?;Compose multiple observers to log output, track usage, and record transcripts:
use agentkit_reporting::{CompositeReporter, JsonlReporter, StdoutReporter, UsageReporter};
let reporter = CompositeReporter::new()
.with_observer(StdoutReporter::new(std::io::stderr()).with_usage(false))
.with_observer(JsonlReporter::new(Vec::new()))
.with_observer(UsageReporter::new());
let agent = Agent::builder()
.model(adapter)
.observer(reporter)
.build()?;Configure structural compaction that drops reasoning and failed tool results, then keeps the most recent items:
use agentkit_compaction::{
AgentBuilderCompactorExt, CompactionPipeline, DropFailedToolResultsStrategy,
DropReasoningStrategy, KeepRecentStrategy, StrategyCompactor,
};
use agentkit_core::ItemKind;
let compactor = StrategyCompactor::builder()
.item_count_trigger(10)
.strategy(
CompactionPipeline::new()
.with_strategy(DropReasoningStrategy::new())
.with_strategy(DropFailedToolResultsStrategy::new())
.with_strategy(
KeepRecentStrategy::new(8)
.preserve_kind(ItemKind::System)
.preserve_kind(ItemKind::Context),
),
)
.build()?;
let agent = Agent::builder()
.model(adapter)
.compactor(compactor)
.build()?;Compactors plug into the loop's generic LoopMutator seam, so the same hook handles redaction, repair, or any other transcript edit. Use context_window_trigger(window, percent) for token-aware triggering driven by provider-reported input_tokens.
Route shell commands to background execution with automatic detach-after-timeout:
use agentkit_task_manager::{AsyncTaskManager, RoutingDecision};
use std::time::Duration;
let task_manager = AsyncTaskManager::new().routing(|req: &agentkit_tools_core::ToolRequest| {
if req.tool_name.0 == "shell_exec" {
RoutingDecision::ForegroundThenDetachAfter(Duration::from_secs(5))
} else {
RoutingDecision::Foreground
}
});
let agent = Agent::builder()
.model(adapter)
.add_tool_source(tools)
.task_manager(task_manager)
.build()?;The umbrella crate re-exports subcrates behind feature flags.
Default flags:
corecapabilitiestoolstask-managerloopreporting
Optional flags:
acpcompactioncontextmcppluginsadapter-completionsprovider-openrouterprovider-openaiprovider-anthropicprovider-basetenprovider-cerebrasprovider-ollamaprovider-vllmprovider-groqprovider-mistraltool-fstool-shelltool-skills
More detail is in docs/feature-flags.md.
