Plugins: Extending Hermes With Custom Tools and Hooks
Hermes has a plugin system for adding custom tools, hooks, and integrations without modifying core code. If you want to create a custom tool for yourself, your team, or one project, this is usually the right path. The core idea is simple: drop a directory into ~/.hermes/plugins/ containing a plugin.yaml manifest and Python code, restart Hermes, and your tools appear alongside the built-in ones — the model can call them immediately.
Anatomy of a Plugin
A minimal plugin is just two or three files:
~/.hermes/plugins/hello-world/
├── plugin.yaml # manifest: name, version, description
├── __init__.py # register() — wires schemas to handlers
└── tools.py # tool handlers (what runs when called)
Inside register(ctx), you use the ctx.* API to add capabilities: ctx.register_tool() adds a tool the LLM can call, ctx.register_hook() registers a lifecycle hook like post_tool_call, ctx.register_command() adds a /name slash command, and ctx.register_cli_command() adds a hermes <plugin> <subcommand> entry. You can also inject messages into conversations, ship data files, bundle skills (namespaced as plugin:skill), and register gateway platforms, image/video/context engines, approval transports, and memory backends.
Discovery and the Opt-In Allowlist
Plugins are found from several sources: bundled plugins ship with Hermes; user plugins live in ~/.hermes/plugins/; project plugins live in .hermes/plugins/ (disabled by default, enabled only for trusted repos via HERMES_ENABLE_PROJECT_PLUGINS=true); and distributed pip packages register through hermes_agent.plugins entry points, with a NixOS declarative path available too.
Plugins are opt-in by default (with a few exceptions). Discovery finds them, but nothing with hooks or tools loads until you add the plugin's name to plugins.enabled in ~/.hermes/config.yaml. This stops third-party code from running without your explicit consent. Manage it with hermes plugins, hermes plugins enable <name>, and hermes plugins disable <name>. Bundled platform and backend plugins (like IRC or Teams channel adapters) bypass the gate because they're part of Hermes' built-in surface — the actual channel turns on via its own gateway.platforms.<name>.enabled setting.
Types of Plugins
Hermes has four kinds of plugins:
- General plugins — add tools, hooks, slash commands, and CLI commands; multi-select via enable/disable.
- Memory providers — replace or augment built-in memory; single-select (one active).
- Context engines — replace the built-in context compressor; single-select.
- Model providers — declare an inference backend; many load, the user picks one at a time via
--providerorconfig.yaml.
Not everything is a Python plugin. Some extension surfaces intentionally use config-driven shell commands (TTS, STT, shell hooks), external MCP servers the agent connects to, or drop-in directories with their own manifest format.
Capabilities, Consent, and Pinning
Plugins can declare the privileged host surfaces they want in their plugin.yaml — for example tools.override to replace built-in tools, or llm.model_override. When a plugin declares capabilities, install and enable show the list with one-line risk descriptions and ask once; granting records it under plugins.entries.<id>.granted_capabilities with a consent hash. A plugin update can never silently widen its access — new capabilities stay off until you consent. Note that capabilities are a consent and audit layer, not a sandbox: plugins run as regular in-process Python, so a malicious plugin can ignore every gate. Granting a capability is a statement of trust in the plugin author, and you should only install plugins from sources you trust.
To keep your stack reproducible, hermes plugins install owner/repo --ref <40-char-commit> pins a plugin to an exact commit; hermes plugins update refuses to move a pinned plugin. The curated plugin catalog (hermes plugins search <term>, hermes plugins browse) resolves bare names to SHA-pinned repositories. Plugin packs (hermes-pack.yaml) let you share a set of pinned plugins declaratively, and install goes through the same pinned path as a direct install — with no bulk consent.
Security Scanning and Using Plugins
Every hermes plugins install and update runs a static security scan over the plugin tree before it is activated, reusing the same threat-pattern engine as the Skills Hub guard — catching exfiltration of credential stores, reverse shells, destructive commands, and prompt injection. The verdict is one of three: safe (installs normally), caution (findings shown, confirm to continue), or dangerous (blocked, and --force does not override). After install, plugins can reach MCP servers through ctx.call_mcp(), but only for servers explicitly listed in the per-plugin mcp_allowlist (off by default). The unified interactive UI — hermes plugins with no arguments — shows general plugins as checkboxes and provider plugins as radio pickers, and /plugins shows what's loaded in a running session.
Key Takeaways
- Plugins add custom tools, hooks, slash commands, and integrations without modifying core code.
- Install is a drop-in directory —
plugin.yamlplus Python — and plugins are opt-in viaplugins.enabled. - Four plugin kinds cover general tools, memory providers, context engines, and model providers.
- Capabilities + consent control privileged access, and commits can be SHA-pinned for reproducibility.
- Every install is scanned for threats, with
dangerousverdicts always blocked.