Skip to content

Connect the Hermes agent to NomOS

Hermes (Nous Research, MIT) is a self-learning agent and at the same time an MCP host. NomOS governs it: the agent asks NomOS for knowledge and has planned actions checked against the room constitution before acting.

Hermes is a model-agnostic, self-learning agent and at the same time an MCP host: it can attach to any MCP server. NomOS is one such server. The integration gives a Hermes agent governed access to NomOS.

Guiding principle (ADR-037): Hermes is the actor, NomOS is the authority. Hermes acts; NomOS supplies the knowledge and the binding verdict. Hermes memory and skills stay with the agent. Knowledge flows into the memory only as a reviewed proposal confirmed by the owner, never as a direct write.

Hermes configures MCP servers in «~/.hermes/config.yaml» under «mcp_servers:». For your own login, use OAuth (next section). The Bearer path is for a machine identity without a browser (NHI): the room owner creates an NHI agent in the agents area under «External agents» and receives a client ID and secret. The entry reads the token from the environment:

~/.hermes/config.yaml

mcp_servers:
ainomos:
url: "https://<your-installation>/api/mcp"
headers:
Authorization: "Bearer ${AINOMOS_TOKEN}"

The secret itself is not a bearer token. Exchange the client ID and secret for a short-lived access token through the client credentials flow:

curl -s -X POST "https://<your-installation>/auth/realms/nimbus/protocol/openid-connect/token" \
-d grant_type=client_credentials -d client_id=<CLIENT_ID> -d client_secret=<CLIENT_SECRET>

Set the «access_token» from the response as «AINOMOS_TOKEN» in the shell. The token expires after a few minutes; fetch a new one when needed.

export AINOMOS_TOKEN="<ACCESS_TOKEN>"

Note: Hermes reads the variable «AINOMOS_TOKEN» from the environment at connect time. It must be set in the shell where the Hermes session runs.

Hermes can also run OAuth 2.1 itself (discovery, PKCE, token exchange) and refresh the token on its own. For that, replace the header lines with «auth: oauth» plus the pre-registered client «hermes-agent»:

~/.hermes/config.yaml

mcp_servers:
ainomos:
url: "https://<your-installation>/api/mcp"
auth: oauth
oauth:
client_id: "hermes-agent"

The public PKCE client «hermes-agent» is pre-registered in Keycloak (ADR-038), like «claude-desktop». Start the OAuth login once:

hermes mcp login ainomos

Hermes then opens the browser login, captures the loopback callback itself, and refreshes the token on its own. You no longer fetch a token by hand. The browser flow is verified: the login reports «Authorization Successful», after which Hermes keeps the session itself. OAuth is therefore the recommended route for your own login; you need the Bearer path only for a machine identity.

Copy the governed-agent skill into «~/.hermes/skills/». The command requires a copy of the NomOS repository:

cp -r docs/integrations/hermes/company-brain-governed-agent ~/.hermes/skills/

The skill forces Hermes to call «validate_action» before risky actions (writing files, opening a PR, sending messages, changing infrastructure, reading sensitive data) and to treat NomOS as the authority for policies, decisions, and knowledge.

Mandatory in profiles that work with NomOS: write access to memory and skills only with approval.

~/.hermes/config.yaml
memory:
write_approval: true
skills:
write_approval: true
  • Minimal toolset: only enable what the use case actually needs. This also limits what can bypass the check.
  • No YOLO mode, no broad local secrets.
  • Separate profiles per customer/room; sandbox (Docker/Modal) where possible.

list_rooms()

Lists the rooms the requesting person is a member of. This is the entry point for finding the right room (by slug or display name).

ask_brain(room, question)

Returns a governed answer with citations and a «run_id» through the full governance path (retrieval, rule check, evidence). «room» accepts the slug or the display name of one of your member rooms. For follow-up questions, pass the «conversation_id» from the previous answer: the history flows in as context, and the answer returns the «conversation_id». Citations still apply per answer.

validate_action(room, action, content, tool?)

The runtime PDP: submit a planned action before the agent acts, and get a verdict («allow», «caution», «require_approval», or «block»), the applicable binding rules, an AI assessment marked as advisory, and an «evidence_id». If the action names a tool, pass «tool»: the verdict then also checks the room’s tool rules and, for «require_approval», names the approval path. «room» accepts the slug or the display name.

propose_decision(room, type, title, context_md, options_md, recommendation_md)

Captures a decision pending in the dialogue as a draft in the room (ADR, BDR, or SDR, with context, options, and recommendation). Only the responsible decision maker in the knowledge library can make the decision binding; the proposer is recorded as its origin. Each proposer can hold a limited number of open drafts per room; the room details show the cap and whether it comes from the room or the installation.

Check that Hermes sees the server and the tools:

Hermes

hermes mcp

If «ainomos» and the tools appear, run two probes in the chat:

  • «list my NomOS rooms» → Hermes calls «list_rooms()» and shows the allowed rooms.
  • «validate action in room [room name]: delete prod database» → the verdict should be «block».

«validate_action» is a cooperative PDP: Hermes calls it voluntarily. If the Hermes toolset is not constrained, the agent can use tools directly and bypass the check. Without the gateway, protection therefore comes from a minimal toolset, the mandatory skill, and a sandbox. For tools that run through the NomOS gateway, NomOS enforces the rules technically: in the «Assisted» room mode, it stops blocked calls, and in «Strict» mode, it also requires approvals. Operations turns the gateway on; without that setting, it is off.

The end-to-end run is complete: Hermes connects through OAuth, «list_rooms» returns the governed rooms, and «ask_brain» and «validate_action» respond with a verdict and evidence. This verifies the whole server-side path, including the OAuth login. Without the gateway, protection rests on the constrained toolset.