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]asyncfnmain() -> 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()?;letmut driver = agent
.start(SessionConfig::new("chat").with_cache(PromptCacheRequest::automatic().with_retention(PromptCacheRetention::Short),)).await?;ifletLoopStep::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.
Provider configs use agentkit_http::Authentication as their first-class
credential type. For Baseten, Cerebras, Groq, Mistral, OpenAI, OpenRouter, and
authenticated Ollama/vLLM endpoints, passing a bare string to an authentication
argument is shorthand for bearer authentication. Custom refresh-capable
credentials can be installed with .with_authentication_provider(...).
Anthropic is the exception: AnthropicConfig::new(...) sends x-api-key, while
AnthropicConfig::with_auth_token(...) explicitly selects a bearer auth token.
Ollama and vLLM authentication is optional.
Provider resilience is also opt-in. Configs store
Option<agentkit_http::ResilienceConfig> and default to None; call
.with_resilience(...) to enable retries and timeouts. Leaving it as None
preserves the existing single-attempt behavior.
- Set your OpenRouter API key and model — either through environment variables or directly in code via
OpenRouterConfig::new(authentication, 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()?;letmut 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.
