The key separation
Hermes keeps two things apart:
- the runtime that decides when tools should be called
- the capability layer that defines what tools exist and how they run
That is what model_tools.py and tools/registry.py are doing for the system.
The loop only needs a small contract
From the perspective of the runtime, tools are mostly abstracted into two operations:
- get tool definitions
- dispatch a function call
That means the runtime does not need intimate knowledge of every tool file. This is exactly the right kind of decoupling for a fast-moving tool ecosystem.
Discovery beats hardcoded lists
Hermes treats tools as discovered capabilities:
- builtin tool files register themselves
- MCP tools can be discovered into the same system
- plugin tools can join the registry too
This means the repository is designed to grow new capabilities without continuously reopening the agent loop as the central edit point.
Toolsets are policy, not just grouping
Toolsets are one of the most important ideas in the runtime.
They let Hermes reason at a policy level:
- which cluster of tools should be visible here
- which toolsets should be forbidden in this environment
- how should a subagent differ from the parent
- what should cron runs be prevented from doing
That is much more expressive than a flat list of allowed names.
The async bridge is real architecture
Many tools are async. The main runtime is not purely async all the way through.
model_tools.py handles this by maintaining persistent loops for the main thread and worker threads, instead of repeatedly creating and destroying event loops.
This avoids a category of failures that many “clean” repos ignore until they start breaking under real async client usage.
The lesson is simple:
runtime glue code is part of the architecture, not an implementation nuisance
What this subsystem teaches
Hermes treats the tool layer like a capability runtime with:
- discovery
- registration
- schema exposure
- toolset filtering
- dispatch
- async adaptation
That is why the tool system stays extensible without forcing the main loop to know too much.