🤖HermesBlog
Hermes Feature Guides · Part 108/9/2026

Plugins — Give Hermes New Superpowers Without Touching Core Code

Built-in Plugins — Ready-to-Use Enhancements — easy-to-understand guide based on official docs (updated 2026-08-09)

Built-in plugins: ready-to-use packs

Plugins — Give Hermes New Superpowers Without Touching Core Code

So you’ve been playing with Hermes for a while. You’ve got your checkpoints, you’ve rolled back a few mistakes, and now you’re thinking: “I wish Hermes could do this one specific thing that’s unique to my workflow.”

Good news: you don’t need to modify Hermes core code. You don’t need to fork the repo. You just need to drop a folder into the right place.

Welcome to the Hermes plugin system.

Bundled Plugins at a Glance

These plugins ship inside the Hermes repository and are all opt-in — discovery finds them, but nothing loads until you enable one with hermes plugins enable <name>:

Plugin Kind What it adds
disk-cleanup hooks + slash command Auto-track ephemeral files (test_*, tmp_*) and sweep them on session end
security-guidance hooks Pattern-match dangerous code on write_file / patch and append a security warning (or block) — 25 rules (Apache-2.0 fork of Anthropic’s claude-plugins-official patterns)
teams_pipeline standalone Microsoft Teams meeting pipeline — Graph-backed, transcript-first summaries
google_meet standalone Join Meet calls, live-caption transcription, optional realtime duplex audio
image_gen/openai image backend OpenAI GPT Image 2 and 2.5 Flare/Sunburst generation and editing (API key)
image_gen/openai-codex image backend OpenAI image generation via Codex OAuth
image_gen/xai image backend xAI grok-2-image backend
hermes-achievements dashboard tab Steam-style collectible badges generated from your real session history
kanban/dashboard dashboard tab Kanban board UI for the multi-agent dispatcher — tasks, comments, fan-out

Turn one off again with hermes plugins disable <name>, or by removing it from plugins.enabled in ~/.hermes/config.yaml.

What Exactly Is a Plugin?

Think of a plugin as a small, self-contained add-on that lives in its own directory. It can add new tools, hooks, slash commands, and even entire platform integrations — all without touching the core Hermes codebase.

The basic structure looks like this:

~/.hermes/plugins/my-plugin/
├── plugin.yaml      # manifest
├── __init__.py      # register() — wires schemas to handlers
├── schemas.py       # tool schemas (what the LLM sees)
└── tools.py         # tool handlers (what runs when called)

Drop that folder in, restart Hermes, and your tools appear right alongside the built-in ones. The model can call them immediately.

Your First Plugin in 2 Minutes

Let’s build a complete working example. We’ll create a hello_world tool and log every tool call via a hook.

Step 1: Create ~/.hermes/plugins/hello-world/plugin.yaml:

name: hello-world
version: "1.0"
description: A minimal example plugin

Step 2: Create ~/.hermes/plugins/hello-world/__init__.py:

"""Minimal Hermes plugin — registers a tool and a hook."""

import json


def register(ctx):
    # --- Tool: hello_world ---
    schema = {
        "name": "hello_world",
        "description": "Returns a friendly greeting for the given name.",
        "parameters": {
            "type": "object",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "Name to greet",
                }
            },
            "required": ["name"],
        },
    }

    def handle_hello(params, **kwargs):
        del kwargs
        name = params.get("name", "World")
        return json.dumps({"success": True, "greeting": f"Hello, {name}!"})

    ctx.register_tool(
        name="hello_world",
        toolset="hello_world",
        schema=schema,
        handler=handle_hello,
    )

    # --- Hook: log every tool call ---
    def on_tool_call(tool_name, params, result):
        print(f"[hello-world] tool called: {tool_name}")

    ctx.register_hook("post_tool_call", on_tool_call)

Step 3: Restart Hermes. That’s it. The model can now call hello_world, and every tool invocation gets logged.

One important note: the model-facing tool description lives in schema["description"]. The optional ctx.register_tool(description=...) is separate registry metadata. If you provide both, keep them in sync — the model sees the schema value.

What Else Can Plugins Do?

The ctx.* API is your toolbox. Here’s what you can do inside register(ctx):

Capability How
Add tools ctx.register_tool(name=..., toolset=..., schema=..., handler=...)
Add hooks ctx.register_hook("post_tool_call", callback)
Add slash commands ctx.register_command(name, handler, description) — adds /name in CLI and gateway sessions
Dispatch tools from commands ctx.dispatch_tool(name, args) — invokes a registered tool with parent-agent context auto-wired
Add CLI commands ctx.register_cli_command(name, help, setup_fn, handler_fn) — adds hermes <plugin> <subcommand>
Inject messages ctx.inject_message(content, role="user", session_key=...)
Ship data files Path(__file__).parent / "data" / "file.yaml"
Bundle skills ctx.register_skill(name, path) — namespaced as plugin:skill
Gate on env vars requires_env: [API_KEY] in plugin.yaml
Distribute via pip [project.entry-points."hermes_agent.plugins"]
Register a gateway platform ctx.register_platform(name, label, adapter_factory, check_fn, ...)
Register an image-generation backend ctx.register_image_gen_provider(provider)
Register a video-generation backend ctx.register_video_gen_provider(provider)
Register a context-compression engine ctx.register_context_engine(engine)
Register a terminal execution backend ctx.register_terminal_environment_provider(provider)
Route human approval prompts ctx.register_approval_transport(name, present_fn)
Register a memory backend Subclass MemoryProvider in plugins/memory/<name>/__init__.py
Run a host-owned LLM call ctx.llm.complete(...) / ctx.llm.complete_structured(...)
Call an MCP tool ctx.call_mcp(server, tool, arguments, timeout=30)
Register an inference backend LLM provider registration

Installing Plugins Safely

General plugins and user-installed backends are disabled by default — discovery finds them, but nothing with hooks or tools loads until you add the plugin’s name to plugins.enabled in ~/.hermes/config.yaml. You can flip state with hermes plugins (interactive toggle), hermes plugins enable <name>, or hermes plugins disable <name>.

For a reproducible install, pin a full immutable commit (tags, branches, and abbreviated SHAs are not accepted):

hermes plugins install owner/repo --ref 0123456789abcdef0123456789abcdef01234567

Hermes checks out the commit detached, verifies that HEAD exactly matches the requested SHA, and records the canonical source, installed revision, and pin status in the current profile. hermes plugins update refuses to move a pinned plugin; choose a new exact commit explicitly with hermes plugins install <source> --force --ref <new-commit>. The same pin is available in Hermes Desktop under Skills → Plugins → Install from Git, and hermes plugins list prints the pin in its Source column (git pinned@<sha8>).

Installing from a private repository

hermes plugins install clones non-interactively (it never prompts for a username or password), so a private repo needs a credential Hermes can find on its own. For an https:// source it tries, in order:

  1. GITHUB_TOKEN or GH_TOKEN from your .env (GitHub hosts only).
  2. The gh CLI’s login (gh auth login), GitHub hosts only.
  3. Your git credential helper (git credential fill) for that host — works for GitLab, Bitbucket and self-hosted servers if a credential is already stored.

The credential is sent as a one-shot HTTP header for that install or update; it is never written into the plugin’s .git/config or the install metadata. SSH sources (git@host:owner/repo.git) authenticate through your ssh-agent as before.

A Word of Caution

Project-local plugins under ./.hermes/plugins/ are disabled by default. This is a safety feature. Only enable them for trusted repositories by setting HERMES_ENABLE_PROJECT_PLUGINS=true before starting Hermes.

Bundled Plugins You Can Turn On

Hermes also ships a handful of plugins in-tree under <repo>/plugins/<name>/. They use the exact same plugin surface as anything you write yourself — hooks, tools, slash commands — just maintained by the Hermes team. See the Plugins page for the general plugin system, and Build a Hermes Plugin to write your own.

Like user plugins, bundled ones are opt-in: discovery finds them, but nothing loads until you run hermes plugins enable <name> (or add the name to plugins.enabled). They are never auto-enabled, not even on a fresh install.

A few worth knowing:

  • disk-cleanup — tracks ephemeral files (test_*, tmp_*, *.test.*) created inside HERMES_HOME or /tmp/hermes-* and sweeps them on session end. It adds a /disk-cleanup slash command with status, dry-run, quick, deep, track, and forget subcommands. Safety is strict: it only ever touches paths under HERMES_HOME or /tmp/hermes-*, rejects Windows mounts like /mnt/c/..., and never removes well-known state dirs (logs/, memories/, sessions/, cron/, cache/, skills/, plugins/, disk-cleanup/ itself) even when empty. Your project trees (workspace/, projects/, plans/, home/) and kanban/ are never tracked at all — a test_*.py inside your project is source code, not scratch.
  • security-guidance — pattern-matches dangerous code on write_file / patch / skill_manage and appends a warning (or blocks, with SECURITY_GUIDANCE_BLOCK=1). 25 rules, warn-by-default (Apache-2.0 fork of Anthropic’s claude-plugins-official patterns).
  • observability/langfuse — no longer bundled. Langfuse tracing now ships as the official langfuse catalog plugin, with the same hooks, HERMES_LANGFUSE_* environment variables and capture modes. Install it with hermes plugins install langfuse; profiles that had observability/langfuse enabled get it installed and enabled automatically by hermes update (or on the first start after updating) and keep tracing to the same Langfuse project. Setup, tuning and capture modes are documented in the plugin’s README.
  • google_meet — joins Meet calls and transcribes them from Meet’s own live captions (the bot never decodes the meeting audio, so no STT billing — and captions are lossy and English-biased). Realtime mode (mode='realtime') is speak-only on the audio side: replies are synthesized by OpenAI Realtime and played into the call through a virtual microphone, while what the bot hears is still the caption stream. meet_status reports micState so a silent bot can be diagnosed.
  • teams_pipeline, image_gen/*, hermes-achievements, and kanban/dashboard round out the set.

Langfuse is no longer a bundled plugin

Langfuse tracing moved out of the Hermes repository and now ships as the official langfuse catalog plugin, with the same hooks, HERMES_LANGFUSE_* environment variables and capture modes. Install it with:

hermes plugins install langfuse

Profiles that had observability/langfuse enabled get the plugin installed and enabled automatically by hermes update (or on the first start after updating), and keep tracing to the same Langfuse project. Setup, tuning and capture modes are documented in the plugin’s README.

NeMo Relay is no longer a bundled plugin

NeMo Relay moved into Hermes core, so don’t run hermes plugins enable observability/nemo_relay. To configure Relay middleware or exporters, use a standard Relay plugins.toml instead.

Hermes loads Relay’s user configuration (~/.config/nemo-relay/plugins.toml) first, then its machine-wide system configuration (/etc/nemo-relay/plugins.toml, or %ProgramData%\nemo-relay\plugins.toml on Windows). The system configuration has higher precedence. Set HERMES_NEMO_RELAY_PLUGINS_TOML before starting Hermes only when you want an explicit file to replace the user configuration — the system configuration still wins. This policy is process-wide for every profile hosted by that Hermes process. Run hermes doctor to see which files apply. See the NeMo Relay observability configuration for ATOF, ATIF, and OpenTelemetry options.

The old HERMES_NEMO_RELAY_ATOF_* and HERMES_NEMO_RELAY_ATIF_* settings no longer configure exporters. When HERMES_NEMO_RELAY_PLUGINS_TOML is unset, the gateway warns about remaining legacy variables and hermes doctor reports them. Independently discovered Relay user or system exporters still apply.

Automatic migration. hermes update (and hermes migrate relay, or hermes migrate relay --all-profiles for every profile home) converts the legacy variables into <hermes home>/relay-plugins.toml, sets HERMES_NEMO_RELAY_PLUGINS_TOML in that profile’s .env, and comments the legacy lines out (nothing is deleted). Under a multiplexed gateway every profile home gets its own file. The generated file is validated through Relay before it is written; this is the shape it produces (note the type = "file" sink discriminator — a sink without it is rejected):

version = 1

[[components]]
kind = "observability"
enabled = true

[components.config]
version = 4
enable_full_payloads = false

[components.config.atof]
enabled = true

[[components.config.atof.sinks]]
type = "file"
output_directory = "/home/you/.hermes/telemetry/nemo-relay/atof"
filename = "hermes-atof.jsonl"
mode = "append"

[components.config.atif]
enabled = true
agent_name = "Hermes Agent"
model_name = "unknown"
output_directory = "/home/you/.hermes/telemetry/nemo-relay/atif"
filename_template = "trajectory-{session_id}.json"

[components.config.opentelemetry]
enabled = false
endpoints = []

[components.config.policy]
unknown_component = "warn"
unknown_field = "warn"
unsupported_value = "error"

Then add HERMES_NEMO_RELAY_PLUGINS_TOML=/home/you/.hermes/relay-plugins.toml to .env and restart the gateway.

Session-span segmentation (continuous sessions)

Relay exports a span when its scope closes. A continuous gateway session can keep its session span open for days even though each turn span exports normally. Optional segmentation rotates only the session scope at a turn boundary:

gateway:
  telemetry:
    session_segments:
      on_compaction: false  # rotate after context compaction
      max_turns: 0          # 0 = unlimited; N = turns per segment
Key Default Behavior
on_compaction false Rotate after compaction completes, at the next turn boundary.
max_turns 0 Rotate after every N completed turns; 0 disables the cap.

Both defaults preserve one session scope for the full session. Rotated spans retain the same session_id and add hermes.session.segment plus hermes.session.segment_reason (compaction or max_turns).

Where to Go Next

The plugin system is your gateway to making Hermes truly yours. Whether you’re building a custom tool for yourself, your team, or a specific project, this is the right path.

For a complete step-by-step guide with a full working example, check out the Build a Hermes Plugin developer guide. It walks you through everything from project structure to distribution.

Your checkpoints keep you safe. Your plugins make you powerful. Combine them, and there’s not much you can’t do.

📖 Official Docs

This article is based on the official Hermes Agent documentation:Official docs › user-guide/features/built-in-plugins