Hermes Agent as a Gateway: Executor and Self-Hosted MCP
/ 6 min read
Table des matières
An AI agent is more than a model that receives a prompt and returns text. As soon as it needs to stay available, respond through a messaging platform, access tools, and keep an execution environment, it becomes a distributed system.
In my infrastructure, Hermes Agent acts as the gateway and orchestrator. It receives Discord messages, manages conversations, and calls the configured model. External capabilities are not added directly to Hermes: they go through Executor, a self-hosted integration layer that exposes an MCP endpoint.
This separation is the key architectural idea. Hermes reasons and communicates; Executor centralizes access to tools; MCP servers provide specialized capabilities. Everything runs in a Docker stack, with services isolated from direct external access.
The three layers
flowchart LR
User["Discord user"] -->|message| Hermes["Hermes Agent\ngateway run"]
Hermes -->|model call| Models["AI model"]
Hermes -->|MCP HTTP + OAuth| Executor["Executor\nHermes MCP toolkit"]
Executor --> GTFS["GTFS-MCP"]
Executor --> Moodle["Moodle-MCP"]
Executor --> Storage["Executor /data\npersistent volume"]
Hermes: the agent layer
Hermes runs from the nousresearch/hermes-agent:latest image. The container starts with gateway run, rather than as a simple interactive session. This distinction matters: the process stays alive, listens for Discord events, and maintains conversation sessions.
Its configuration is mounted from config/hermes/config.yaml to /opt/data/config.yaml. It describes:
- the model provider;
- the
/workspaceworking directory; - the Discord interface and its authorization rules;
- the remote MCP server Hermes must connect to.
The Discord gateway is deliberately restricted. The configuration allows one specific user and channel, disables general mentions, and does not require the bot to be mentioned in that channel. These are routing and trust decisions, not properties of the model itself.
Hermes delegates inference to the configured model, but that dependency is outside the scope of this article. The focus here is the agent and the tools it can use.
Executor: the integration layer
Executor runs from ghcr.io/rhyssullivan/executor-selfhost:latest. It centralizes integrations and exposes an MCP surface that Hermes can consume.
Its state is stored in the Docker executor volume, mounted at /data. Persistence matters: integrations, configuration, and service state should not disappear whenever the container is recreated.
Hermes does not reference Executor’s general interface, but a dedicated MCP toolkit:
mcp_servers: executor: url: https://<executor-host>/mcp/toolkits/hermes auth: oauthThe name executor therefore becomes a capability boundary in Hermes. The agent does not necessarily know how each downstream tool is implemented; it discovers a surface published by Executor.
MCP as a capability boundary
The Model Context Protocol standardizes how a client discovers and calls external tools. In this setup, Hermes is the MCP client and Executor is the MCP server visible to Hermes.
Hermes supports both local servers and remote MCP servers. My Executor integration uses a remote endpoint authenticated with auth: oauth.
This organization provides a useful separation:
- Hermes keeps the conversation and decision-making logic.
- Executor presents a consistent tool surface to the agent.
- Specialized servers remain independent services.
- Integration credentials and policies can be centralized in Executor.
The Hermes configuration explicitly declares only Executor. Executor is therefore responsible for presenting Hermes with the unified list of available integrations and tools.
The MCPs exposed to Hermes
These are the integrations currently exposed by Executor’s Hermes toolkit:
| Integration | Executor identifier | Role in the setup |
|---|---|---|
| Naurio | naurio_mcp |
Junia ISEN timetable, exams, and personal study slots. |
| GTFS | gtfs |
Public-transit routes and schedules, including Ilévia line 18 passages. |
| Moodle | moodle |
Access to the Junia Learning platform. |
| Dockhand | dockhand_api |
Management and orchestration of the VPS Docker stack. |
| Cloudflare | cloudflare_mcp |
Management of Cloudflare services and security rules. |
| Pocket ID | pocket_id_api |
Interaction with the OIDC/SSO identity provider. |
| GitHub | github_graphql |
Inspecting and managing repositories, issues, and pull requests. |
| Pelican Application / Admin | pelican_api_application |
Administration of the Pelican panel and its nodes. |
| Pelican Client | pelican_api_client |
Control of servers hosted through Pelican. |
| Spotify | spotify |
Currently playing track, search, and playlist management. |
| Firecrawl | firecrawl_mcp |
Scraping, content extraction, and website crawling. |
| GrepApp | grep_mcp |
Searching code across public repositories. |
| Excalidraw | excalidraw_app_demo |
Creating diagrams, mockups, and whiteboards. |
| Context7 | context7_mcp |
Looking up context and documentation for the tools in use. |
This list gives a better idea of what it means to give Hermes tools. The agent does not merely get access to one utility: it gets a cross-functional catalog covering daily life, infrastructure, development, documentation, and diagram creation.
The boundary remains clear: Hermes decides when to use a tool and presents the result in the conversation; Executor handles the integrations and required permissions; each service keeps its own domain logic.
Specialized MCP servers
GTFS-MCP
gtfs-mcp runs from the ghcr.io/frflo/gtfs-mcp:latest image. Its configuration is mounted read-only from config/gtfs-mcp/config.json, and its persistent data lives in the gtfs-data volume mounted at /data.
For example, GTFS can work with data from SNCF Voyageurs and Île-de-France Mobilités. It illustrates the value of a specialized MCP server: transport data becomes agent-friendly tools without adding that domain logic to Hermes itself.
Moodle-MCP
moodle-mcp is another internal MCP server, built from ghcr.io/frflo/moodle-mcp:latest. It is not directly exposed externally.
Its Moodle endpoint and token are injected through environment variables. The token does not appear in versioned configuration. The service is therefore separate from the gateway: Hermes does not need to know Moodle’s implementation details, and secrets remain in the deployment layer designed for them.
Isolation and security
The components are isolated from direct external access. Internal services are not exposed as public ports; whenever external access is required, it goes through the infrastructure’s control and authentication mechanisms.
This separation reduces the attack surface and means that every MCP server does not have to become a public application. Hermes sees only the tools exposed to it by Executor, while specialized services remain behind that boundary.
The path of a request
A typical interaction follows this conceptual chain:
- A message arrives in the authorized Discord channel.
- The Hermes gateway checks the user and channel.
- Hermes restores the corresponding session and prepares the context.
- The model analyzes the request and may choose a tool.
- Hermes sends the call to Executor’s MCP surface.
- Executor applies its integration logic and may forward the call to a specialized server.
- The result returns to the Hermes loop, which can continue reasoning.
- The final response is sent to Discord.
The model therefore does not become a direct administrator of the VPS. It operates within a set of tool surfaces exposed by Hermes and Executor. The quality of the system depends as much on this capability selection as on the model itself.
What this architecture enables
The main benefit is decoupling. A new tool can be added downstream without turning Hermes into a monolith. Another MCP client could also consume Executor, while Hermes remains the conversation-oriented entry point through Discord.
Centralization also provides a natural place to handle integrations, their secrets, and their exposure policies. On the other hand, it introduces another dependency: if Executor is unavailable, the tools it publishes are unavailable too, even if Hermes and the model are still working.
Finally, using latest images makes deployment simple but less reproducible. To reproduce this setup exactly, the image versions or digests, Hermes version, and MCP server versions should also be recorded. The current configuration describes an operational architecture, not a complete infrastructure lockfile.
In summary
Hermes Agent is the interaction and orchestration layer: it receives Discord messages, manages sessions, and drives the model. Executor is the MCP gateway that exposes a centralized tool surface to Hermes. Naurio, GTFS, Moodle, Dockhand, Cloudflare, Pocket ID, GitHub, Pelican, Spotify, Firecrawl, GrepApp, Excalidraw, and Context7 form the practical tool catalog available through that gateway.
The value of this setup does not come from one component, but from the boundaries between them. The agent can reason without knowing every integration; tools can evolve without rewriting the gateway; and the infrastructure retains control over persistence and exposure.