Appearance
Custom Tools & Python Plugins
Hermes has 60+ built-in tools, but your environment has its own, an internal API, a deployment script, a database query that matters to your team. You can register custom tools that appear alongside the built-in ones, gated by the same toolset and approval system. This lesson covers the tool registry and backend plugins.

What you'll learn
- Custom tools are registered in Python, one file in
tools/, one entry intoolsets.py - All handlers must return JSON strings and include a
check_fnso the tool only appears when its requirements are met - Backend plugins (
~/.hermes/plugins/) are Python packages that load at startup, use them for tools you don't want in the core repo
The problem
Built-in tools cover files, terminal, web, browser, and more. But they don't cover your internal API, your deployment system, or your team's specific workflows. Custom tools fill that gap. They're first-class, they appear in hermes tools list, respect the approval system, and are gated by toolset enable/disable just like built-in tools.
Options & when to use each
| Approach | Good for | When to use it |
|---|---|---|
| Custom tool in core | Tools you want to contribute back, or tools tightly coupled to your Hermes install | Permanent additions to your Hermes setup |
Backend plugin (~/.hermes/plugins/) | Tools specific to your environment, not meant for upstream | Team-specific tools, internal APIs, anything you don't want in the core repo |
| MCP server | Tools that already exist as a server, or tools shared across multiple agent platforms | Existing services, cross-platform tools |
| Desktop plugin | UI extensions, not tool extensions | Visual dashboards, status panels, command palette entries |
Build it: a custom tool
Step 1: Create the tool file
Create tools/health_check.py in your Hermes source directory (~/.hermes/hermes-agent/tools/):
python
import json
import subprocess
from tools.registry import registry
def check_requirements() -> bool:
"""Only show this tool if the health check script exists."""
import os
return os.path.exists("/usr/local/bin/health-check")
def health_check_tool(service: str = "", task_id: str = None) -> str:
"""Run a health check on a specified service or all services."""
if service:
result = subprocess.run(
["/usr/local/bin/health-check", "--service", service],
capture_output=True, text=True, timeout=30
)
else:
result = subprocess.run(
["/usr/local/bin/health-check", "--all"],
capture_output=True, text=True, timeout=30
)
return json.dumps({
"success": result.returncode == 0,
"service": service or "all",
"output": result.stdout,
"errors": result.stderr,
})
registry.register(
name="health_check",
toolset="custom",
schema={
"name": "health_check",
"description": "Run a health check on a specified service or all services. "
"Use this to verify that services are running correctly.",
"parameters": {
"type": "object",
"properties": {
"service": {
"type": "string",
"description": "The service name to check. Omit to check all services.",
},
},
},
},
handler=lambda args, **kw: health_check_tool(
service=args.get("service", ""),
task_id=kw.get("task_id"),
),
check_fn=check_requirements,
requires_env=[],
)Step 2: Wire it into a toolset
In toolsets.py, add the tool name to a toolset:
python
# In the TOOLSETS dict:
"custom": {
"health_check",
# ... other custom tools
},Or add it to _HERMES_CORE_TOOLS if you want it available on every platform by default.
Step 3: Enable the toolset
Enable the new toolset via the CLI to make it available to the agent. This step connects your Python handler to the active session.
bash
hermes tools enable customThen /reset to start a new session with the updated toolset. Tool changes don't take effect mid-conversation, this preserves prompt caching.
Step 4: Verify the tool is available
Check the active tool registry to confirm your setup. You should see the health check tool listed and ready for invocation.
bash
hermes tools list | grep health_checkBuild it: a backend plugin
For tools you don't want in the core repo, write a backend plugin:
bash
mkdir -p ~/.hermes/plugins/my-toolsCreate ~/.hermes/plugins/my-tools/__init__.py:
python
def register_tools(registry):
"""Called at startup. Register your tools here."""
from .health_check import register_health_check
register_health_check(registry)Backend plugins are Python packages that load at Hermes startup. They're isolated from the core repo, update Hermes without losing your custom tools.
What goes wrong
| Mistake | How you notice it | The fix |
|---|---|---|
| Tool doesn't appear in the tool list | hermes tools list doesn't show your tool | The toolset isn't enabled, or the check_fn returned False. Enable the toolset with hermes tools enable, and verify the check_fn succeeds by running its conditions manually |
| Tool returns non-JSON | Hermes errors when parsing the tool output | All handlers must return JSON strings. Wrap your output in json.dumps() |
| Tool works in CLI but not gateway | Works in hermes chat but not from Telegram/Discord | Gateway sessions may have different toolsets enabled. Run hermes tools and check the per-platform enablement |
| Hardcoded paths break across machines | Tool works on your machine but not on the server | Use get_hermes_home() from hermes_constants for all paths, never hardcode ~/.hermes/ |
Confirm it worked
Run through this quick test sequence to validate your custom tool integration. This confirms the registry, enables the toolset, and invokes it within a chat session.
bash
# 1. Verify the tool is registered
hermes tools list | grep health_check
# 2. Enable the toolset
hermes tools enable custom
# 3. Start a new session and test
hermes chat -q "Run a health check on all services"
# 4. The agent should call the health_check tool and report resultsNext: Capstone: The Operations Console , building a complete Hermes-based operations dashboard.