Chapter 5

The Tool System

The Agent's hands — 43+ built-in tools

How Do Tools Work?

Every tool follows a unified pattern: 1. Name — The tool's unique identifier, like Read, Bash, Grep 2. Description — Tells the LLM what this tool can do 3. Parameters — What inputs the tool needs 4. Execute — The code that actually does the work 5. Result — The output of the operation The LLM decides which tool to use based on the tool's name and description, then passes in the parameters. The tool executes and returns a result.

Basic Tool Structure

openharness/tools/base.py
1class BaseTool(ABC):
2 """Base class for all tools"""
3
4 @property
5 def name(self) -> str:
6 """Tool name, e.g. 'Read', 'Bash'"""
7 ...
8
9 @property
10 def description(self) -> str:
11 """Tool description — how the LLM understands its capabilities"""
12 ...
13
14 def get_input_schema(self) -> dict:
15 """Parameter definition in JSON Schema format"""
16 ...
17
18 async def execute(self, params, context) -> ToolResult:
19 """Execute the tool, return the result"""
20 ...

In OpenHarness, every tool inherits from BaseTool:

Tool Categories

OpenHarness's 43+ tools fall into these categories: 📁 File Operations (6) Read, Write, Edit, Glob, Grep, NotebookEdit The most commonly used category — lets the Agent read, write, and search files. 💻 Shell (1) Bash — execute any shell command. The most powerful tool and the one that needs the most permission control. 🔍 Search (3) WebFetch, WebSearch, ToolSearch Lets the Agent retrieve information from the internet. 🤖 Agent Collaboration (2) Agent, SendMessage Lets one Agent launch sub-agents to handle complex subtasks. 📋 Task Management (5) TaskCreate, TaskGet, TaskList, TaskUpdate, TaskStop Manage background async tasks. 🌐 MCP (3) MCPTool, ListMcpResources, ReadMcpResource Connect to external tools via the standard protocol. 🔄 Workflow (4) EnterPlanMode, ExitPlanMode, EnterWorktree, ExitWorktree Control the Agent's operating mode.
🔧 Tool Explorer — 29+ built-in tools in OpenHarness
ReadRead-only

Read file contents

File
Write

Write to a file

File
Edit

Exact string replacement in files

File
GlobRead-only

Search files by name pattern

File
GrepRead-only

Search file contents (powered by ripgrep)

File
NotebookEdit

Edit Jupyter Notebook cells

File
Bash

Execute shell commands and return output

Shell
WebFetchRead-only

Fetch web page content

Search
WebSearchRead-only

Search the internet for information

Search
ToolSearchRead-only

Search available deferred tools

Search
Agent

Launch a sub-agent for complex tasks

Agent
SendMessage

Send a message to a running agent

Agent
TaskCreate

Create a new background task

Task
TaskGetRead-only

Get task status and details

Task
TaskListRead-only

List all tasks

Task
TaskUpdate

Update task status

Task
TaskStop

Stop a running task

Task
MCPTool

Call a tool on an MCP server

MCP
ListMcpResourcesRead-only

List MCP resources

MCP
ReadMcpResourceRead-only

Read MCP resource content

MCP
EnterPlanModeRead-only

Enter plan mode (read-only exploration)

Workflow
ExitPlanMode

Exit plan mode

Workflow
EnterWorktree

Enter an isolated Git worktree

Workflow
ExitWorktree

Exit Git worktree

Workflow
SkillRead-only

Load and execute a skill (domain knowledge)

Meta
AskUserQuestionRead-only

Ask the user a question for info

Meta
CronCreate

Create a scheduled task

Schedule
CronListRead-only

List scheduled tasks

Schedule
CronDelete

Delete a scheduled task

Schedule

The Registry Pattern

openharness/tools/registry.py
1class ToolRegistry:
2 """Tool Registry — unified management for all tools"""
3
4 def register(self, tool: BaseTool):
5 """Register a new tool"""
6 self._tools[tool.name] = tool
7
8 def get_tool(self, name: str) -> BaseTool:
9 """Get a tool by name"""
10 return self._tools[name]
11
12 def get_tool_schemas(self) -> list[dict]:
13 """Get JSON Schemas for all tools (for the LLM to see)"""
14 return [tool.to_json_schema() for tool in self._tools.values()]

All tools are managed through a unified registry:

ToolResult — The Tool's Return Value

Every tool returns a ToolResult after execution, with two key fields: • output: The tool's output (e.g., file contents, command output) • is_error: Whether an error occurred Importantly, tool errors don't crash the program. Error messages are returned to the LLM as results, and the LLM sees the error and tries to correct course. It's like a person who made a mistake and self-corrects.
📌 Key Takeaway
Unified Interface + Registry = Infinite Extensibility
The beauty of OpenHarness's tool system: every tool follows the same interface (BaseTool), managed through a unified registry. This means adding a new tool doesn't require modifying the engine — just write a new class and register it. That's why OpenHarness can support 43+ tools while keeping the code clean.
🧠 Check Your Understanding
When a tool execution errors out, what does OpenHarness do?
🧠 Check Your Understanding
Why does OpenHarness use the 'registry pattern' to manage tools?