Summary
hermes mcp add fails with 401 Unauthorized when adding an HTTP MCP server that uses header auth with Authorization: Bearer ${MCP_N8N_API_KEY}, while hermes mcp test <name> succeeds against the same URL and token.
Environment
- Hermes Agent v0.15.1 (2026.5.29)
- macOS
- MCP server: n8n instance-level MCP at
http://localhost:5678/mcp-server/http
- Auth:
hermes mcp add <name> --url ... --auth header
- Token in
~/.hermes/.env as MCP_<SERVER>_API_KEY (JWT only, no Bearer prefix)
Steps to reproduce
-
Set MCP_N8N_API_KEY=<valid n8n MCP JWT> in ~/.hermes/.env (from n8n Settings → MCP Access).
-
Run:
hermes mcp add n8n --url http://localhost:5678/mcp-server/http --auth header
-
Confirm overwrite if prompted; confirm authentication is required.
-
Observe connection failure during the discovery probe.
Expected behavior
The discovery probe succeeds (same as hermes mcp test n8n), tools are listed, and config is saved.
Actual behavior
✗ Failed to connect: Client error '401 Unauthorized' for url 'http://localhost:5678/mcp-server/http'
Meanwhile:
→ Connected, tools discovered (e.g. 27 tools for n8n MCP).
Root cause
cmd_mcp_add in hermes_cli/mcp_config.py builds a fresh server_config with:
server_config["headers"] = {
"Authorization": f"Bearer ${{{env_key}}}"
}
and calls _probe_single_server(name, server_config).
_probe_single_server passes that dict to _connect_server / _run_http without resolving ${ENV} placeholders.
Runtime MCP loading via _load_mcp_config() in tools/mcp_tool.py does call load_hermes_dotenv() and _interpolate_env_vars(). Config loaded for hermes mcp test is also resolved when read through load_config().
So the add probe sends the literal header:
Authorization: Bearer ${MCP_N8N_API_KEY}
which n8n correctly rejects with 401.
Manual verification with curl:
| Authorization header |
HTTP result |
Bearer ${MCP_N8N_API_KEY} (literal) |
401 |
Bearer <jwt> |
200 |
Bearer Bearer <jwt> (double prefix) |
401 |
Proposed fix
In hermes_cli/mcp_config.py, resolve env placeholders (and load dotenv) before probing — mirror _load_mcp_config() behavior:
def _resolve_mcp_server_config(config: dict) -> dict:
from hermes_cli.env_loader import load_hermes_dotenv
from tools.mcp_tool import _interpolate_env_vars
load_hermes_dotenv()
return _interpolate_env_vars(config)
def _probe_single_server(name: str, config: dict, connect_timeout: float = 30):
config = _resolve_mcp_server_config(config)
...
Optional hardening in cmd_mcp_add: strip a leading Bearer from pasted tokens before saving to MCP_*_API_KEY, since the header template already adds Bearer .
Related UX / docs issues (same area)
| Issue |
Notes |
| Wrong config key |
hermes config set mcp.servers.<name>... writes under mcp.servers; runtime reads mcp_servers (top-level). hermes mcp list shows empty until corrected. |
hermes config unset |
Not a valid subcommand; CLI reports invalid choice for unset. |
Double Bearer |
If MCP_*_API_KEY is Bearer <jwt> and header is Bearer ${MCP_*_API_KEY}, n8n receives Bearer Bearer ... → 401. |
Workaround (until fixed)
-
Do not re-run hermes mcp add if hermes mcp test <name> already passes; answer N when asked to save config after a failed add.
-
Use top-level mcp_servers in ~/.hermes/config.yaml (not mcp.servers):
mcp_servers:
n8n:
url: http://localhost:5678/mcp-server/http
headers:
Authorization: Bearer ${MCP_N8N_API_KEY}
enabled: true
-
In ~/.hermes/.env, store only the JWT (no Bearer prefix):
-
n8n MCP expects Authorization: Bearer <token> (see n8n instance-level MCP connection docs).
Local patch note (not upstream)
A temporary fix was applied locally to ~/.hermes/hermes-agent/hermes_cli/mcp_config.py (_resolve_mcp_server_config, _strip_bearer_prefix). This is overwritten on hermes update or reinstall. User config in ~/.hermes/config.yaml and ~/.hermes/.env persists across updates.
References
- Hermes MCP config key:
mcp_servers in ~/.hermes/config.yaml (hermes_cli/mcp_config.py, tools/mcp_tool.py)
- n8n MCP HTTP endpoint:
/mcp-server/http
- Cursor MCP example (separate client):
n8n/cursor-mcp.env.example in this repo
Submitted with love from Team Reagent

Summary
hermes mcp addfails with 401 Unauthorized when adding an HTTP MCP server that uses header auth withAuthorization: Bearer ${MCP_N8N_API_KEY}, whilehermes mcp test <name>succeeds against the same URL and token.Environment
http://localhost:5678/mcp-server/httphermes mcp add <name> --url ... --auth header~/.hermes/.envasMCP_<SERVER>_API_KEY(JWT only, noBearerprefix)Steps to reproduce
Set
MCP_N8N_API_KEY=<valid n8n MCP JWT>in~/.hermes/.env(from n8n Settings → MCP Access).Run:
Confirm overwrite if prompted; confirm authentication is required.
Observe connection failure during the discovery probe.
Expected behavior
The discovery probe succeeds (same as
hermes mcp test n8n), tools are listed, and config is saved.Actual behavior
Meanwhile:
hermes mcp test n8n→ Connected, tools discovered (e.g. 27 tools for n8n MCP).
Root cause
cmd_mcp_addinhermes_cli/mcp_config.pybuilds a freshserver_configwith:and calls
_probe_single_server(name, server_config)._probe_single_serverpasses that dict to_connect_server/_run_httpwithout resolving${ENV}placeholders.Runtime MCP loading via
_load_mcp_config()intools/mcp_tool.pydoes callload_hermes_dotenv()and_interpolate_env_vars(). Config loaded forhermes mcp testis also resolved when read throughload_config().So the add probe sends the literal header:
Authorization: Bearer ${MCP_N8N_API_KEY}which n8n correctly rejects with 401.
Manual verification with curl:
Bearer ${MCP_N8N_API_KEY}(literal)Bearer <jwt>Bearer Bearer <jwt>(double prefix)Proposed fix
In
hermes_cli/mcp_config.py, resolve env placeholders (and load dotenv) before probing — mirror_load_mcp_config()behavior:Optional hardening in
cmd_mcp_add: strip a leadingBearerfrom pasted tokens before saving toMCP_*_API_KEY, since the header template already addsBearer.Related UX / docs issues (same area)
hermes config set mcp.servers.<name>...writes undermcp.servers; runtime readsmcp_servers(top-level).hermes mcp listshows empty until corrected.hermes config unsetunset.BearerMCP_*_API_KEYisBearer <jwt>and header isBearer ${MCP_*_API_KEY}, n8n receivesBearer Bearer ...→ 401.Workaround (until fixed)
Do not re-run
hermes mcp addifhermes mcp test <name>already passes; answer N when asked to save config after a failed add.Use top-level
mcp_serversin~/.hermes/config.yaml(notmcp.servers):In
~/.hermes/.env, store only the JWT (noBearerprefix):n8n MCP expects
Authorization: Bearer <token>(see n8n instance-level MCP connection docs).Local patch note (not upstream)
A temporary fix was applied locally to
~/.hermes/hermes-agent/hermes_cli/mcp_config.py(_resolve_mcp_server_config,_strip_bearer_prefix). This is overwritten onhermes updateor reinstall. User config in~/.hermes/config.yamland~/.hermes/.envpersists across updates.References
mcp_serversin~/.hermes/config.yaml(hermes_cli/mcp_config.py,tools/mcp_tool.py)/mcp-server/httpn8n/cursor-mcp.env.examplein this repoSubmitted with love from Team Reagent