Tools
Tools are capabilities that agents can invoke during their reasoning loop. When the LLM decides it needs to take an action, it generates a tool call with a name and parameters, Heartbit executes the tool, and the result is fed back into the conversation.
The Tool Trait
Section titled “The Tool Trait”Every tool implements the Tool trait:
use heartbit::{Tool, ToolDefinition, ToolOutput, Error};use serde_json::Value;use std::pin::Pin;use std::future::Future;
pub struct MyTool;
impl Tool for MyTool { fn definition(&self) -> ToolDefinition { ToolDefinition { name: "my_tool".into(), description: "Does something useful".into(), input_schema: serde_json::json!({ "type": "object", "properties": { "input": { "type": "string", "description": "The input value" } }, "required": ["input"] }), } }
fn execute( &self, input: Value, ) -> Pin<Box<dyn Future<Output = Result<ToolOutput, Error>> + Send + '_>> { Box::pin(async move { let input_str = input["input"].as_str().unwrap_or_default(); Ok(ToolOutput::success(format!("Processed: {input_str}"))) }) }}definition()returns aToolDefinitionwith the tool’s name, description, and JSON Schema parameters.execute()receives the LLM’s input as aserde_json::Valueand returns aToolOutput.
Tools are stored as Arc<dyn Tool> and registered via the builder:
let agent = AgentRunner::builder(provider) .tools(vec![Arc::new(MyTool)]) .build()?;Parallel Execution
Section titled “Parallel Execution”When the LLM generates multiple tool calls in a single response, Heartbit executes them concurrently using tokio::JoinSet. This is automatic — no configuration needed.
Built-in Tools
Section titled “Built-in Tools”Heartbit ships with 14 built-in tools available by default in env-based mode (no config file):
| Tool | Description |
|---|---|
bash | Execute bash commands. Working directory persists between calls. Default timeout: 120s, max: 600s. |
read | Read a file with line numbers. Detects binary files. Max size: 256 KB. |
write | Write content to a file. Creates parent directories. Read-before-write guard. |
edit | Replace an exact string in a file (must appear exactly once). Read-before-write guard. |
patch | Apply unified diff patches to one or more files. Single-pass hunk application. |
glob | Find files matching a glob pattern. Skips hidden files. |
grep | Search file contents with regex. Uses rg when available, falls back to built-in. |
list | List directory contents as an indented tree. Skips common build artifacts. |
webfetch | Fetch content from a URL via HTTP GET. Supports text, markdown, HTML. Max: 5 MB. |
websearch | Search the web via Exa AI. Requires EXA_API_KEY. |
todowrite | Write/replace the full todo list. Only 1 item in progress at a time. |
todoread | Read the current todo list. |
skill | Load skill definitions from SKILL.md files. |
question | Ask the user structured questions (only when on_question callback is set). |
Built-in tools share a FileTracker that enforces read-before-write guards, preventing accidental overwrites of files the agent hasn’t seen.
MCP Integration
Section titled “MCP Integration”Heartbit includes an MCP (Model Context Protocol) client supporting Streamable HTTP and stdio transports. This lets agents connect to any MCP-compatible tool server.
Configure MCP servers in your TOML config:
[[agents]]name = "researcher"description = "Research agent"system_prompt = "You research topics."
[[agents.mcp_servers]]url = "http://localhost:3001/mcp"The MCP client implements protocol version 2025-11-25 and supports tools/list and tools/call operations.
Tool Output
Section titled “Tool Output”Tools return ToolOutput which can contain:
- Text — plain text results via
ToolOutput::success("result") - Error — error messages via
ToolOutput::error("something went wrong")
Tool Timeout and Truncation
Section titled “Tool Timeout and Truncation”- Tools have configurable timeouts to prevent runaway execution (bash defaults to 120s).
- Tool output is truncated when it exceeds size limits, keeping the beginning of the output and appending a truncation notice. The bash tool uses head+tail truncation with a middle notice. This prevents oversized tool results from consuming the context window.
Human-in-the-Loop
Section titled “Human-in-the-Loop”When the --approve flag is set (or on_approval callback registered), agents pause before executing tools and request user approval. The user can:
- Allow — execute this tool call
- Deny — skip this tool call (agent receives an error result and can adjust)
- AlwaysAllow — auto-approve this tool going forward
- AlwaysDeny — auto-deny this tool going forward
Structured Output
Section titled “Structured Output”Set structured_schema on an agent to force structured JSON output. Heartbit injects a synthetic __respond__ tool, guiding the LLM to produce validated JSON in AgentOutput::structured.