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:- Create
tools/your_tool.py— the tool implementation and registration - Add an import in
model_tools.py_discover_tools()list - Add to
toolsets.py— either_HERMES_CORE_TOOLSor a new toolset
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.
The Or on a Python package being installed:
The
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: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.Add import in model_tools.py
Open The import is wrapped in a try/except so optional tools with missing dependencies don’t prevent other tools from loading.
model_tools.py and add your module to the _modules list inside _discover_tools():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.
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 inrun_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
TheToolRegistry singleton in tools/registry.py is the backbone of the tool system:
registry.register()— called at module import time by each tool fileregistry.get_definitions(tool_names)— returns filtered OpenAI-format schemas (runscheck_fnper tool)registry.dispatch(name, args, **kwargs)— executes a tool handler, bridges async handlers automatically, catches and formats exceptions