Embed Meerkat as a Rust library: the full engine with no subprocess overhead.
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.
Production surfaces (CLI, REST, RPC, MCP) use the runtime-backed path where SessionService is substrate and RuntimeSessionAdapter owns keep-alive, Queue/Steer routing, and comms drain. See meerkat-rpc or meerkat-rest for those entry points.For the main production embedding path, use the runtime-backed persistent flow:
use meerkat::{ AgentFactory, Config, CreateSessionRequest, SessionService, build_persistent_service, open_realm_persistence_in,};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-5".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, 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.
use meerkat::{CreateSessionRequest, StartTurnRequest, SessionService};use meerkat_core::service::{HandlingMode, InitialTurnPolicy};let created = service.create_session(CreateSessionRequest { model: "claude-sonnet-4-5".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, 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()), render_metadata: None, handling_mode: HandlingMode::Queue, event_tx: None, skill_references: None, flow_tool_overlay: None, additional_instructions: None,}).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.
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.
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 imagelet 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: base64_data, source_path: None, }, ],});