📑 Table of contents

Hermes Agent Plugins and Extensions: Extend Your AI Agent

Hermes Agent 🟡 Intermediate ⏱️ 10 min read 📅 2026-05-05

Hermes Agent Plugins and Extensions: Extend Your AI Agent

The Hermes Agent plugin system is one of its most powerful features. Rather than a monolithic agent locked into a fixed set of capabilities, Hermes is modular by design: every advanced feature can be added, removed, or replaced through independent plugins. Whether you want to integrate Spotify, add a messaging platform adapter, enable observability via Langfuse, or create your own custom tools — the plugin system makes it possible without touching Hermes' source code.

This article details the plugin system architecture, the four discovery sources, the different plugin types, lifecycle hooks, and how to create and install your own extensions.

Plugin System Architecture

The plugin manager (PluginManager) is a global singleton that handles the discovery, loading, and invocation of all plugins. It is defined in hermes_cli/plugins.py and exposed through module-level functions such as discover_plugins() and invoke_hook().

Each plugin is discovered from one of four locations:

1. Bundled Plugins

Plugins shipped with the Hermes repository reside in <repo>/plugins/<name>/. They are available immediately after installation. The bundled collection includes:

  • disk-cleanup: automatic cleanup of temporary files (test scripts, outputs, cron logs) via post_tool_call and on_session_end hooks
  • spotify: native integration with 7 playback control tools using the Spotify Web API + OAuth PKCE
  • image_gen/openai, image_gen/xai, image_gen/openai-codex: image generation backends (GPT Image, Grok)
  • platforms/irc, platforms/teams: gateway adapters for IRC and Microsoft Teams
  • google_meet: join Google Meet calls, live transcription, real-time speech
  • langfuse: conversation, LLM call, and tool usage observability

2. User Plugins

Each user can install additional plugins in ~/.hermes/plugins/<name>/. This is the default location for third-party plugins installed via hermes plugins install.

3. Project Plugins

Project-specific plugins can be placed in ./.hermes/plugins/<name>/. This mode is opt-in via the HERMES_ENABLE_PROJECT_PLUGINS environment variable. Useful for build or integration plugins specific to a repository.

4. Pip Plugins (Entry Points)

Python packages installed via pip can declare an entry point in the hermes_agent.plugins group. Hermes detects them automatically via importlib.metadata.entry_points(). This is the ideal format for distributing a Hermes plugin on PyPI.

Loading Priority and Collisions

The discovery order is fixed: bundled → user → project → pip. On collision (same plugin key), later sources override earlier ones. A user plugin named disk-cleanup will replace the bundled plugin with the same name. This is explicit and intentional — not a bug.

A plugin's key is derived from its path: a plugin at plugins/image_gen/openai/ has key image_gen/openai, while a plugin at plugins/disk-cleanup/ has key disk-cleanup. This allows tts/openai and image_gen/openai to coexist without collision.

The Manifest File: plugin.yaml

Each directory-based plugin must contain a plugin.yaml (or plugin.yml) file and an __init__.py with a register(ctx) function. The manifest describes the plugin's metadata:

name: spotify
version: 1.0.0
description: "Native Spotify integration  7 tools using Spotify Web API + PKCE OAuth."
author: NousResearch
kind: backend
provides_tools:
  - spotify_playback
  - spotify_devices
  - spotify_queue
  - spotify_search
  - spotify_playlists
  - spotify_albums
  - spotify_library
requires_env:
  - SPOTIFY_CLIENT_ID
  - SPOTIFY_CLIENT_SECRET

Available manifest fields:

  • name: plugin name (default = directory name)
  • version: semantic version
  • description: plugin description
  • author: author name
  • kind: plugin type (see next section)
  • provides_tools: list of exposed tools
  • provides_hooks: list of hooks used
  • requires_env: required environment variables
  • platforms: supported platforms (linux, macos, etc.)

The Four Plugin Types

The kind field in the manifest defines the loading behavior and scope of the plugin.

standalone (default)

The classic plugin: it registers its own hooks and tools. Activation is opt-in via plugins.enabled in config.yaml. This is the most common type for user extensions.

backend

A backend plugin provides an alternative implementation for an existing tool. Bundled backends activate automatically — they must "just work" without additional configuration. The active backend is selected via the corresponding config key (e.g., image_gen.provider: openai).

User-installed backends in ~/.hermes/plugins/ remain opt-in via plugins.enabled.

exclusive

Reserved for memory providers. Only one active provider per category. Selection is done via <category>.provider in config.yaml. The memory discovery system handles its own lifecycle — the general PluginManager does not load these directly.

Hermes ships with 8 bundled memory providers: Honcho, Mem0, Supermemory, Holographic, Hindsight, Byterover, OpenViking, and RetainDB.

platform

Messaging platform adapters for the gateway. Bundled platform plugins activate automatically (IRC, Teams) to be available out of the box. Manually installed platforms in ~/.hermes/plugins/ remain opt-in.

The plugin registers via ctx.register_platform(), which accepts an adapter factory, a dependency check function, and metadata.

The PluginContext: Registration API

When a plugin is loaded, the manager calls its register(ctx) function with a PluginContext instance. This context is the facade through which the plugin interacts with the framework:

  • ctx.register_tool(): registers a tool in the global registry alongside native tools. Accepts a name, toolset, JSON schema, handler, and options (async, check_fn, requires_env, description, emoji)
  • ctx.register_hook(): registers a callback for a lifecycle hook
  • ctx.register_command(): registers a slash command (e.g., /lcm) usable in CLI and gateway. Conflicts with built-in commands are automatically rejected
  • ctx.register_cli_command(): registers a CLI subcommand (e.g., hermes honcho ...)
  • ctx.register_platform(): registers a gateway platform adapter
  • ctx.register_skill(): registers a read-only skill accessible via skill_view('plugin:name')
  • ctx.register_context_engine(): registers an alternative context engine (only one allowed)
  • ctx.register_image_gen_provider(): registers an image generation backend
  • ctx.dispatch_tool(): calls a registry tool with the parent agent's context
  • ctx.inject_message(): injects a message into the active conversation

Lifecycle Hooks

Hooks are insertion points in the agent's lifecycle. A plugin can register callbacks to intercept or observe the following events:

LLM Communication Hooks

  • pre_llm_call: before each model call. Can return context to inject into the user message (preserves the system prompt cache)
  • post_llm_call: after each model response. Useful for latency metrics, logging, compression

Tool Hooks

  • pre_tool_call: before tool execution. A plugin can return {"action": "block", "message": "..."} to block the call. Ideal for security policies, rate limiting, approval workflows
  • post_tool_call: after tool execution. For logging, monitoring, automatic cleanup

API Hooks

  • pre_api_request: before each HTTP request to the LLM provider. Langfuse uses this to trace requests
  • post_api_request: after the response. Ideal for capturing token usage, costs, errors

Session Hooks

  • on_session_start: when a session starts
  • on_session_end: when a session ends. The disk-cleanup plugin uses this to clean temporary files
  • on_session_finalize: when the session is finalized in the database
  • on_session_reset: on session reset (/new command)

Advanced Hooks

  • pre_gateway_dispatch: before dispatching an incoming message in the gateway. Allows skip, rewrite, or allow actions
  • pre_approval_request / post_approval_response: observers of the dangerous command approval cycle
  • subagent_stop: when a subagent (delegate_task) completes
  • transform_terminal_output: transform terminal output
  • transform_tool_result: transform tool results before returning to the model

Hook Robustness

Each callback runs in its own try/except block. A misbehaving plugin cannot break the main agent loop — errors are logged and the next hook executes normally.

Plugin Configuration

Opt-in Activation

By default, standalone plugins are inactive. Activation is done in ~/.hermes/config.yaml:

plugins:
  enabled:
    - disk-cleanup
    - google_meet
    - observability/langfuse

Explicit Disable

The disabled key always overrides enabled. Useful for excluding a plugin even if it's bundled:

plugins:
  disabled:
    - disk-cleanup

Auto-activation for Backends and Platforms

Bundled backends (image_gen/*) and bundled platform plugins (IRC, Teams) activate automatically. No need to list them in enabled.

CLI Management

Hermes provides dedicated commands:

hermes plugins list          # List all discovered plugins
hermes plugins install owner/repo   # Install from GitHub
hermes plugins install https://github.com/owner/repo.git
hermes plugins enable name    # Enable a plugin
hermes plugins disable name   # Disable a plugin
hermes plugins remove name    # Remove a user plugin

Installation clones the Git repository into ~/.hermes/plugins/<name>/. If the plugin contains an after-install.md file, it is displayed with Rich Markdown rendering to guide configuration.

Creating Your Own Plugin

Minimum Structure

A standalone plugin requires two files:

~/.hermes/plugins/my-plugin/
├── plugin.yaml
└── __init__.py

plugin.yaml:

name: my-plugin
version: 1.0.0
description: "Description of my plugin"
author: "Your Name"
kind: standalone
requires_env:
  - MY_PLUGIN_API_KEY

init.py:

def register(ctx):
    """Entry point called by the PluginManager."""

    # Register a hook
    ctx.register_hook("post_tool_call", on_tool_used)

    # Register a tool
    ctx.register_tool(
        name="my_tool",
        toolset="custom",
        schema={
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "The query"}
            },
            "required": ["query"]
        },
        handler=handle_my_tool,
        description="Tool description for the LLM",
        emoji="🔧",
    )

    # Register a slash command
    ctx.register_command(
        name="my-action",
        handler=handle_command,
        description="Command description",
        args_hint="<parameter>",
    )

def on_tool_used(tool_name, args, result, **kwargs):
    """Callback called after each tool use."""
    print(f"Tool used: {tool_name}")

def handle_my_tool(args, **kwargs):
    """Tool handler."""
    query = args.get("query", "")
    return f"Result for: {query}"

def handle_command(raw_args):
    """Slash command /my-action handler."""
    return f"Action executed with: {raw_args}"

Image Generation Plugin (backend)

name: my-img-backend
version: 1.0.0
description: "Custom image generation backend"
kind: backend
requires_env:
  - MY_IMG_API_KEY

The __init__.py must instantiate a class inheriting from ImageGenProvider and register it via ctx.register_image_gen_provider(provider). The provider name is what gets compared to the image_gen.provider key in config.yaml.

Observability Plugin (hooks)

The Langfuse plugin is an excellent example of a hook-oriented plugin. It attaches to six hooks (pre_api_request, post_api_request, pre_llm_call, post_llm_call, pre_tool_call, post_tool_call) to trace the entire conversation flow.

Platform Plugin (gateway)

Platform adapters (IRC, Teams) use kind: platform and register via ctx.register_platform(). The factory receives a PlatformConfig and must return a BasePlatformAdapter instance. The gateway calls check_fn() before instantiation to verify dependencies are satisfied.

Best Practices

  • Isolate dependencies: list all required environment variables in requires_env so users know what to configure
  • Handle errors gracefully: a plugin should never crash the agent. Use try/except in handlers and hook callbacks
  • Don't pollute the namespace: observer-type hooks should only read data, not modify it. Use pre_tool_call with a block return for policies
  • Check for key collisions: verify your plugin's key (derived from path) doesn't collide with existing plugins
  • Document after-install.md: provide a post-installation guide for non-automatable configuration steps

Bundled Plugins

Hermes Agent already ships with a rich collection of plugins:

  • Memory providers (8): Honcho, Mem0, Supermemory, Holographic, Hindsight, Byterover, OpenViking, RetainDB — each with its own approach to cross-session memory
  • Image generation (3 backends): OpenAI (GPT Image), OpenAI Codex (OAuth), xAI (Grok)
  • Platforms (2): IRC and Microsoft Teams
  • Utilities: disk-cleanup (automatic cleanup), example-dashboard (dashboard template)
  • Observability: Langfuse (complete traces)
  • Integrations: Google Meet, Spotify

This collection already covers the majority of common use cases. The plugin system remains open for the specific needs you want to add.

Conclusion

The Hermes Agent plugin system transforms an already powerful AI agent into a truly extensible platform. With four discovery sources, four plugin types, sixteen lifecycle hooks, and a complete registration API via PluginContext, the extension possibilities are vast. Whether you want to monitor costs via Langfuse, add an adapter for your internal chat platform, or build a specialized business tool, the plugin system is designed for it — without compromising core stability. Each hook is isolated, each plugin is opt-in, and every collision is handled explicitly.