Skip to Content

Connect Hermes

This page is for your own Hermes Agent install β€” not the Companion Hermes app from the Hub app store.

The Companion app does not fork Hermes. It builds on the official Hermes Agent image and layers a terminal UI and setup flow on top, so everything below works against a stock install.

Before you start

You need two API keys and the addresses of both servers.

<hub-url> and <memory-url> below are your two servers’ base URLs β€” everything before /api/mcp. Neither has a port you can assume, so take both from the Hub dashboard if you are unsure: Hub, Memory.

Hermes can do this itself. Everything on this page β€” the MCP servers and first-class memory β€” is also published as an agent-readable skill. Ask it to set itself up from https://docs.ci.computer/connect/SKILL.md, or install the skillΒ  so it is there next time. You still supply the two keys. See Or let your agent do it.

Add the servers

Hermes has a command for this, and it is the better path: it tests each server before you commit to it, and it keeps your key out of config.yaml. Editing the file by hand still works and is documented below β€” the result is the same shape either way.

With hermes mcp add

hermes mcp add hub --url "<hub-url>/api/mcp" --auth header hermes mcp add ci_server --url "<memory-url>/api/mcp" --auth header

The name is the config key, so choose it deliberately: it is what hermes mcp test and hermes mcp remove take afterwards, and it decides the environment variable name below. hub and ci_server match the rest of this page.

Each run asks whether the server needs authentication, takes the key, then connects:

Does this server require authentication? [Y/n]: y API key / Bearer token: βœ“ Saved to ~/.hermes/.env as MCP_HUB_API_KEY Connecting to 'hub'... βœ“ Connected! Found 76 tool(s) from 'hub': ... Enable all 76 tools? [Y/n/select]:

Read the tool count before answering. That number is the check the scope trap describes, arriving at the one moment it is cheap to act on. A memory server reporting zero tools has a key without the Intents scope.

Zero tools does not stop the save. Hermes warns ⚠ Server connected but reported no tools and then asks Save config anyway? [Y/n] β€” where the capital Y is the default, so pressing Enter keeps it. Answer n on a memory server showing zero, fix the key’s scopes, and add it again.

What it writes

mcp_servers: hub: url: "<hub-url>/api/mcp" headers: Authorization: Bearer ${MCP_HUB_API_KEY} enabled: true

Your key is not in there. It goes to $HERMES_HOME/.env β€” ~/.hermes/.env, or %LOCALAPPDATA%\hermes\.env on native Windows β€” and config.yaml only references it. On macOS and Linux that file is written at mode 0600; on Windows POSIX modes do not apply, and it inherits the ACLs of your local app-data directory, which are user-private by default. The variable is MCP_ + the server name uppercased + _API_KEY, so hub gives MCP_HUB_API_KEY and ci_server gives MCP_CI_SERVER_API_KEY.

That split is the main reason to prefer this over hand-editing. config.yaml is the file that gets screenshotted, pasted into a support thread, and synced between machines; a bearer token written inline goes everywhere it goes.

The key is written before the connection is tried, not after β€” the opposite of hermes memory setup further down, which saves nothing when its check fails. So a failed add still leaves a key behind: Hermes reports βœ— Failed to connect, offers Save config anyway (you can test later)? [y/N], and on yes writes the entry with enabled: false plus hermes mcp test <name> to retry. hermes mcp remove <name> takes the entry out of config.yaml but leaves the variable in .env β€” delete that line yourself if you want the key gone.

Raise the memory timeout by hand

One line the command cannot write, because there is no flag for it β€” --connect-timeout sets a different thing. Add it to the memory server:

mcp_servers: ci_server: timeout: 600

It is deliberate: recall over a large memory store can take longer than the default allows, and the resulting failure looks like a broken server rather than a slow one. Hermes’ default is 300 seconds, so only a value above that actually buys you headroom β€” setting a smaller number makes the timeout you were trying to avoid more likely. There is no need to set connect_timeout; its default of 60 is already right for these servers.

Or edit config.yaml yourself

Hermes is configured through config.yaml, in $HERMES_HOME β€” ~/.hermes/config.yaml on a stock macOS, Linux or WSL2 install, %LOCALAPPDATA%\hermes\config.yaml on native Windows. Both servers go under mcp_servers:

mcp_servers: hub: url: "<hub-url>/api/mcp" headers: Authorization: "Bearer <hub-key>" ci_server: url: "<memory-url>/api/mcp" headers: Authorization: "Bearer <memory-key>" timeout: 600

This puts both keys in config.yaml in the clear. To keep the indirection the command gives you, write Bearer ${MCP_HUB_API_KEY} here and put the key in ~/.hermes/.env yourself β€” the variable name is yours to choose when you do it this way.

Do not add a transport: line

Hermes’ MCP client already defaults to Streamable HTTP for any entry that has a url, so the field is unnecessary. Hermes does accept a transport key β€” but its only other value is sse, and setting that on these servers points the client at a transport they do not serve.

Leave it out. This is the opposite of the OpenClaw page, where the field is required. The two clients genuinely differ here. hermes mcp add never writes the field, which is one more reason to prefer it.

Restart and verify

Test each server before restarting anything

hermes mcp test <name> connects from the command line, so you can check your work without ending your session:

Testing 'ci_server'... Transport: HTTP β†’ https://memory.example.com/api/mcp Authorization: Bear***f3a1 βœ“ Connected (92ms) βœ“ Tools discovered: 3

The key is masked in that output, and this is the only command on the page that prints an actual tool count. A failure reads βœ— Connection failed with the reason and the elapsed time.

Count, do not just look

A server that connected but advertises zero tools looks healthy everywhere else. hermes mcp list shows it as βœ“ enabled alongside the working ones, because that column is status and its Tools column is which tools you selected β€” neither is a count. If the memory server discovers nothing, the key is missing the Intents scope β€” see the scope trap.

Restart Hermes

config.yaml is read at startup, so end the current session and start a new one β€” quit the chat and run hermes again. There is no reload command, and an open session will not pick this up.

Not hermes gateway restart. That subcommand manages the messaging gateway β€” Telegram, Discord, WhatsApp β€” and has nothing to do with reloading config.yaml. If you also run the backend server for the desktop app or a remote client, restart that separately with hermes serve --stop and then hermes serve. That works on every platform, Windows included β€” though the stop is a hard kill there (taskkill /F) rather than the graceful signal it sends elsewhere, so give an in-flight request a moment before restarting.

Confirm the tools arrived in the session

Ask the agent what tools it has, or inspect its startup log for the registered servers. mcp test proved the server answers; this proves the running session picked it up.

Which side of WSL are you installing into?

Skip this unless you are on Windows. Hermes supports both a native Windows install and a WSL2 one, and they are separate installations with separate homes:

Where Hermes runs$HERMES_HOMEReached from PowerShell as
Native Windows%LOCALAPPDATA%\hermes${env:LOCALAPPDATA}\hermes
Inside WSL2~/.hermes in the distro\\wsl$\<distro>\home\<user>\.hermes

Everything on this page β€” the plugin directory, config.yaml, .env β€” belongs to whichever one runs your agent. A ~ typed in PowerShell resolves to C:\Users\<you>, which a WSL2 Hermes will never scan; a ~ typed inside the distro is its own Linux home, which a native Hermes will never scan. If you are not sure which you have, run hermes --version in each and see which answers.

First-class memory

Everything above gives your agent memory it can call. Hermes also has a native memory provider interface, separate from MCP, and that is what produces passive capture and injected context β€” conversations recorded without anyone calling a tool.

The provider is published as @companionintelligence/hermes-memory. Nothing about it requires Node or npm: the npm registry is used purely as versioned tarball hosting, and Hermes discovers providers by scanning a directory. You can do this instead of the MCP sections above if passive memory is all you want.

The official way

Fetch the plugin

Hermes discovers memory providers by scanning $HERMES_HOME/plugins/ for a plugin directory β€” one whose __init__.py implements a memory provider. There is no registry to register with β€” put the files in place and they are found.

$HERMES_HOME is not ~/.hermes on native Windows. The Windows installer sets it to %LOCALAPPDATA%\hermes; only a Linux, macOS or WSL2 install uses ~/.hermes. Extract into the wrong one and everything appears to work β€” the files land, no error β€” and then hermes memory setup does not list companionintelligence, because Hermes never scans there. Use the tab that matches where Hermes runs, and see Which side of WSL? if you run both.

rm -rf ~/.hermes/plugins/companionintelligence mkdir -p ~/.hermes/plugins/companionintelligence curl -fL https://registry.npmjs.org/@companionintelligence/hermes-memory/-/hermes-memory-2026.8.3.tgz \ | tar -xz -C ~/.hermes/plugins/companionintelligence --strip-components=1

Removing the directory first is deliberate. tar -x overwrites but never removes, so extracting over an older install leaves behind modules a later version deleted β€” in a directory Python imports from. Your credentials live outside this directory and are not affected.

Run the wizard

hermes memory setup

Pick companionintelligence from the list, then give it your server URL and API key.

This is a shell command, not a chat message. Typed at the Hermes chat prompt (❯), Hermes answers it like a question β€” helpfully, plausibly, and nothing gets configured. Run it in a terminal.

The key needs different scopes than an MCP key. This provider talks REST, not MCP, so the key must carry the Memory scope (plus Intents for the intent tools) at capability Read & write β€” the dialog’s default. A key minted for the MCP section above (MCP + Intents) is refused on every capture call, and the provider fails silently rather than break your session. One key with Memory, Intents and MCP covers both setups. See Get Your API Keys.

The wizard tests the connection against GET /api/health before saving and saves nothing if it fails, so a wrong URL cannot leave you half-configured. The health endpoint does not authenticate, though β€” the probe validates the address, not the key, so a mistyped or wrong-scoped key saves cleanly and surfaces only as memory that never accumulates. On success the wizard writes the key to .env (mode 0600 on macOS and Linux; on Windows it inherits your app-data ACLs), the URL to companionintelligence/config.json, and sets memory.provider in config.yaml.

Verify

hermes memory status

Expect Provider: companionintelligence and Status: available βœ“. Start a new session to activate.

Note what that proves: status confirms the configuration exists, and the wizard’s probe confirmed the address β€” neither validates the key. The proof that capture is actually on: have a short conversation, then look for the turn in the Companion Memory dashboard, or start a fresh session and ask about something from the last one.

hermes plugins list will still show this as not enabled. That is not a missed step. Hermes has two plugin subsystems and deliberately keeps memory providers out of the general one β€” it coerces them to kind: exclusive and routes them to its own discovery. Setting memory.provider, which the wizard does, is the entire activation. Enabling it in the general registry activates nothing and prompts you to grant a built-in tool override this plugin never uses.

The URL default the wizard offers is http://127.0.0.1:8642, which is correct only when Hermes runs on the Hub host itself. A bare hostname gets http:// prepended for you. The value is written once and never re-resolved, so give the address that works from wherever this agent runs β€” see Find your address first.

The cihub connect way

If you are working on the Hub host itself:

cihub connect hermes --memory-url "<memory-url>" --memory-key "<memory-key>"

It probes the server first β€” the key as well as the address, unlike the wizard’s own check β€” then fetches and unpacks the pinned version for you. Memory credentials still stay with the wizard, which owns them, so you finish with hermes memory setup. Add --dry-run to see what it would do and write nothing.

Adding --hub-url and --hub-key registers the Hub MCP server in config.yaml for you, as the mcp_servers.hub block, so you can skip Add the servers for that one. Start a new Hermes session afterwards β€” mcp_servers is read at startup.

Note that it writes the key inline rather than as the ${MCP_HUB_API_KEY} reference hermes mcp add produces. If you would rather your token stayed out of config.yaml, use hermes mcp add for the Hub server and let cihub connect handle only the plugin.

It configures the machine it runs on. If Hermes lives elsewhere, use the official way there.

When memory does not accumulate

The wizard did not list companionintelligence. The files are not where Hermes scans, and there are two ways to land there. On native Windows, the usual one is the directory: $HERMES_HOME is %LOCALAPPDATA%\hermes, not ~/.hermes, so an extraction into the latter succeeds and is never seen β€” as is one done on the Windows side for a Hermes that lives in WSL2, or vice versa. Otherwise check $HERMES_HOME/plugins/companionintelligence/__init__.py exists at all: --strip-components=1 is what puts the files at the top of that directory rather than one level down.

Run hermes memory status β€” or print the home Hermes resolves, using the tab for the shell you are in:

hermes --version && echo "[$HERMES_HOME]"

Brackets so an unset value is visibly empty. An empty [] β€” from the form above that matches your shell β€” means the platform default is in use: %LOCALAPPDATA%\hermes on native Windows, ~/.hermes everywhere else.

The wizard printed Nothing was saved. The connection test failed, and the wizard refused to save a configuration it had just proved does not work. The message names the cause: a connection refused, a 401, or an unexpected status.

Setup succeeded, nothing accumulates. First: configuration is read at startup, so start a new session. If a new session still captures nothing, suspect the key β€” wrong, missing the Memory scope, or minted Read-only. The wizard’s check cannot catch any of those, and the provider fails silently rather than break your session, so re-run hermes memory setup with a re-checked key.

Turning it off. hermes memory off clears memory.provider and returns you to built-in memory. That alone is enough; the /plugins page does not gate this plugin.

Last updated on