Skip to main content
Tools are the actions the agent can take — terminal commands, file reads, web searches, and so on. Each tool is a self-contained Python module that registers itself with the central registry at import time.
Before writing a new tool, ask: should this be a skill instead? If the capability can be expressed as instructions plus shell commands using existing tools, a skill is almost always the better choice. Tools are for capabilities that require end-to-end Python integration, custom auth flows, binary data, or streaming that can’t go through the terminal.

The 3-file requirement

Adding a tool requires changes in exactly 3 files:
  1. Create tools/your_tool.py — the tool implementation and registration
  2. Add an import in model_tools.py _discover_tools() list
  3. Add to toolsets.py — either _HERMES_CORE_TOOLS or a new toolset
1

Create tools/your_tool.py

Each tool file co-locates its schema, handler function, availability check, and registry call. The registry handles schema collection, dispatch, availability checking, and error wrapping.
All tool handlers must return a JSON string. If the handler returns a plain string, Python dict, or raises an unhandled exception, the agent will receive an error. Wrap all return values in json.dumps().

check_fn

The check_fn parameter is a zero-argument callable that returns True when the tool is available. The registry calls it before returning schemas — tools whose check returns False are silently excluded from the tool list.Use check_fn to gate a tool on environment variable availability:
Or on a Python package being installed:

requires_env

The requires_env list tells the setup wizard and hermes doctor which environment variables this tool needs. It does not gate availability on its own — that is check_fn’s job.
2

Add import in model_tools.py

Open model_tools.py and add your module to the _modules list inside _discover_tools():
The import is wrapped in a try/except so optional tools with missing dependencies don’t prevent other tools from loading.
3

Add to toolsets.py

Open toolsets.py and add your tool to the appropriate toolset.To add to the core toolset (available on all platforms — CLI, Telegram, Discord, etc.), add to _HERMES_CORE_TOOLS:
To create a new named toolset, add an entry to the TOOLSETS dict:

Tool schema format

Tool schemas follow the OpenAI function calling format. The registry wraps schemas in {"type": "function", "function": ...} when returning definitions to the agent loop.
The description field of each parameter is critical — the agent uses it to decide what values to pass. Be precise about units, formats, and constraints.

Agent-level tools

Some tools are intercepted by the agent loop in run_agent.py before handle_function_call() is called. These tools need access to agent-level state (like TodoStore or MemoryStore) that the registry doesn’t hold. Current agent-level tools: todo, memory, session_search, delegate_task. If your tool needs access to the agent’s own state, follow the pattern in tools/todo_tool.py. The tool’s schema is still registered with the registry (so it appears in the model’s tool list), but the actual dispatch is handled by run_agent.py.

Registry internals

The ToolRegistry singleton in tools/registry.py is the backbone of the tool system:
  • registry.register() — called at module import time by each tool file
  • registry.get_definitions(tool_names) — returns filtered OpenAI-format schemas (runs check_fn per tool)
  • registry.dispatch(name, args, **kwargs) — executes a tool handler, bridges async handlers automatically, catches and formats exceptions
The registry is imported first; all tool files import from it. This makes the dependency chain strictly acyclic.