Skip to content

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.

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 a ToolDefinition with the tool’s name, description, and JSON Schema parameters.
  • execute() receives the LLM’s input as a serde_json::Value and returns a ToolOutput.

Tools are stored as Arc<dyn Tool> and registered via the builder:

let agent = AgentRunner::builder(provider)
.tools(vec![Arc::new(MyTool)])
.build()?;

When the LLM generates multiple tool calls in a single response, Heartbit executes them concurrently using tokio::JoinSet. This is automatic — no configuration needed.

Heartbit ships with 14 built-in tools available by default in env-based mode (no config file):

ToolDescription
bashExecute bash commands. Working directory persists between calls. Default timeout: 120s, max: 600s.
readRead a file with line numbers. Detects binary files. Max size: 256 KB.
writeWrite content to a file. Creates parent directories. Read-before-write guard.
editReplace an exact string in a file (must appear exactly once). Read-before-write guard.
patchApply unified diff patches to one or more files. Single-pass hunk application.
globFind files matching a glob pattern. Skips hidden files.
grepSearch file contents with regex. Uses rg when available, falls back to built-in.
listList directory contents as an indented tree. Skips common build artifacts.
webfetchFetch content from a URL via HTTP GET. Supports text, markdown, HTML. Max: 5 MB.
websearchSearch the web via Exa AI. Requires EXA_API_KEY.
todowriteWrite/replace the full todo list. Only 1 item in progress at a time.
todoreadRead the current todo list.
skillLoad skill definitions from SKILL.md files.
questionAsk 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.

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.

Tools return ToolOutput which can contain:

  • Text — plain text results via ToolOutput::success("result")
  • Error — error messages via ToolOutput::error("something went wrong")
  • 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.

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

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.