Package Your Own Model Provider for Hermes as a pip Plugin

Picture this: your team runs a shared inference gateway in-house, serving your own fine-tuned models plus a few proxied third-party ones. Every time a new model goes live, you hand-edit Hermes’ config.yaml — endpoint, API key, model names, one field at a time — and then every colleague repeats the same ritual on their own machine. Wouldn’t it be great to wrap “how to talk to our company’s models” in a single package, so anyone can just pip install it and go? PR #85504, merged August 13, turns that into reality: model providers can now be ordinary Python packages that self-register via entry points. Once installed, Hermes discovers them at startup and their models appear in the model registry just like built-in providers.
The mechanism: one entry point does the registration
A provider is Hermes’ adapter layer for “how to reach a model service” — it knows the endpoint shape, where the API key goes, and how requests are sent. Before, providers could only be registered via filesystem plugins or the built-in set. PR #85504 adds a “step 0” to the discovery flow in providers/__init__.py: scan the entry points of every installed Python distribution and look for anyone declaring themselves a provider.
The setup is small: in your own package’s pyproject.toml, declare an entry point pointing at a zero-argument registration function:
[project.entry-points."hermes_agent.plugins"]
acme-inference = "acme_hermes_plugin:register"
The register() function builds a ProviderProfile and calls register_provider():
# acme_hermes_plugin.py
from providers import register_provider
from providers.base import ProviderProfile
def register():
register_provider(
ProviderProfile(
name="acme-inference",
display_name="Acme Inference",
base_url="https://inference.internal.acme.com/v1",
env_vars=("ACME_API_KEY",),
auth_type="api_key",
)
)
The ProviderProfile fields are exactly what “connecting to a model service” requires: name, display name, default endpoint, the env var that holds the API key, the auth type, plus optional extras like a fallback model list (fallback_models). The directory-style provider plugins the official docs describe ($HERMES_HOME/plugins/model-providers/<name>/__init__.py) use the same API — the entry-point mechanism simply swaps “drop a directory” for “pip install”; the registration code itself is identical.
Besides the module:func callable form, the entry point may also be a bare module name (acme-inference = "acme_hermes_plugin") — Hermes imports the module and relies on its module-level register_provider call, mirroring the __init__.py contract of filesystem plugins. Once installed and enabled through the gate below, Hermes invokes it at startup and your models show up in the hermes model picker and everywhere else providers are listed.
The critical gate: installed ≠ enabled
This is the single most important rule of the whole mechanism, so read it twice: pip-installing a package does not mean it loads. The entry-point scan shares the same switches as the general plugin manager — the plugins.enabled allow-list and the plugins.disabled deny-list:
plugins:
enabled:
- acme-inference # only entry points listed here are loaded
disabled:
- some-other-plugin # the deny-list always wins
If your package name is not in plugins.enabled, Hermes won’t even import it — a pip package is never executed merely because it’s installed. That matters for security: any package you pip install could quietly declare a hermes_agent.plugins entry point, but only the one you explicitly allowlisted actually takes effect.
Safety details worth knowing
Beyond the allow-list gate, the design layers in several protections:
- Provider-only lane: the
hermes_agent.pluginsentry-point group is shared with general plugins (UI plugins, tool plugins, etc.). The difference: general plugin registration functions take an argument (register(ctx)), while provider registration hooks are zero-arg by contract. The scanner skips any callable that requires arguments, so general plugins are never mistaken for providers — and you don’t get a spam of TypeError warnings either. - One broken package can’t take Hermes down: if a third-party package’s entry point fails to load, the failure is swallowed per-entry and logged as a warning; provider discovery continues. A bad package won’t prevent Hermes from starting.
- Built-ins always win name collisions: the entry-point scan runs first in the discovery order, and
register_provider()is last-writer-wins — so a pip package can never shadow a bundled provider or one under$HERMES_HOME. It can register a brand-new name, but it can’t hijack an existing first-party one.
When it’s worth doing
- Share one model integration across a team: wrap your internal gateway as a pip package; colleagues run
pip installplus one allow-list line and they’re done — no more per-machine endpoint editing. - Private / self-hosted models: internal fine-tunes, self-hosted vLLM clusters — package the provider, version it with pip, upgrade with
pip install -U. - Publish to the world: if your provider integration has general value, ship it to PyPI — anyone can install it and enable it with a one-line config.
If you already extend Hermes’ tooling with MCP (see our MCP config & context variables guide), provider plugins complete the other half of the picture: MCP manages tools, providers manage models — both are declarative integrations, one over the MCP protocol, one over entry points. The full plugin CLI surface is on the hermes plugins command page; and one reminder — in multi-profile setups, plugins.enabled is configured per profile (see the profiles multi-instance guide), so don’t assume an allow-list set in one profile applies everywhere.
In one line: provider plugins turn “wire up a new model service” from hand-editing config into “pip install + one allow-list line” — the friction lives in security (the allow-list is explicit), the convenience lives in engineering (packaged, shareable, versioned).