hermes-mcp-bridge
Provides tools for interacting with a local Hermes agent HTTP API, enabling AI agents to submit synchronous or asynchronous tasks, poll run status, retrieve durable task results, list registered agents, and inspect task artifacts.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@hermes-mcp-bridgeask hermes to summarize the latest build logs and save the result"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
hermes-mcp-bridge
A small, self-hosted Node.js server that speaks the Model Context Protocol (MCP) over Streamable HTTP and exposes a local Hermes agent HTTP API as four MCP tools. A remote AI agent can send tasks, follow long-running work, read durable results, and inspect artifacts. Node.js >= 18.17, ESM, zero runtime dependencies.
Architecture
+----------------------+ HTTPS +------------------------+
| Remote AI agent | --------------> | Tunnel / reverse proxy |
| (MCP client) | | TLS termination |
+----------------------+ +-----------+------------+
|
v
+-------------------+-------------------+
| Bridge (Node.js) on 127.0.0.1:8765 |
| bearer auth | rate limit | audit log |
+--------+-------------------+--------+
| |
v v
+----------------------------+ +-----------------------------+
| Local agent HTTP API | | Tasks directory |
| /v1/chat/completions | | <id>/result.json |
| /v1/runs, /v1/runs/<run_id>| | <id>/... artifact files |
+----------------------------+ +-----------------------------+Synchronous tasks use
POST /v1/chat/completions; asynchronous tasks usePOST /v1/runsand are polled throughGET /v1/runs/<run_id>.The listener binds to loopback only; the tunnel or proxy is the sole ingress.
Related MCP server: Codex Harness MCP
Tools
Tool | Arguments | Returns |
|
|
|
|
| Run status with |
| none |
|
|
| Parsed |
|
| A listing |
Task ids match [A-Za-z0-9][A-Za-z0-9._-]{0,127} and are generated when omitted. Prefer
hermes_result for final answers and hermes_files for artifacts.
Sessions and agents
Both session and agent are optional; omitting them keeps the isolated, single-agent
behavior (session_id = <prefix>-<id> on the default agent).
session(^[a-z0-9][a-z0-9._-]{0,63}$) reuses conversation state across tasks. The bridge maps it togb-sess-<agent>-<session>, sent assession_idonPOST /v1/runsand as theX-Hermes-Session-Idheader onPOST /v1/chat/completions. Calls in the same session are serialized by an in-process lock. Async accepts also sendIdempotency-Key: <id>and write<task>/run.json({run_id, session_id, task_id, agent, agent_base_url, created_at}) right after the accept, sohermes_statuscan resolverun_id → task_id → agentwithout guessing.agentselects a registered agent. The registry is a YAML file outside the code (AGENTS_FILE, default~/.hermes/agents.yaml, mode600);agents.example.yamlin this repo shows the schema. Each entry hasname,description,capabilities,kind(local/remote),profile,base_url, andkey_env/key_file— the name of the variable or the path to a600file holding the key, never the key itself.base_urlmust be loopback or Tailscale (127.0.0.1,::1,100.64.0.0/10,fd7a:115c:a1e0::/48). A gateway profile served by a multiplexing gateway is addressed ashttp://<host>:<port>/p/<profile>; the bridge appends/v1/...and probes/health.Unknown agents fail with
unknown_agentbefore any upstream call; agents without a resolvable key are listed asavailable: false(unavailable_reason: "missing_credentials") and rejected withagent_unavailable. Reusing a task id that belongs to another agent fails withduplicate_id.The registry hot-reloads on mtime change; an invalid file keeps the last good state and is reported as
registry_errorin/health. A missing file degrades to a synthetic default agent (degraded: "no_registry").
Protocol
JSON-RPC 2.0 over HTTP
POST /mcp. The server is stateless; noMcp-Session-Idis required or issued.Headers:
Authorization: Bearer <token>,Content-Type: application/json, andAccept: application/json, text/event-stream. Responses useapplication/json.Methods:
initialize,tools/list,tools/call, andping. JSON-RPC notifications (requests without anid) are answered with202 Accepted.initializenegotiates the version, advertises thetoolscapability, and returnsserverInfo;pingreturns{}.tools/callreturns content blocks andstructuredContent. Tool failures setisError: trueand return{"error": "<code>", "message": "<text>"}.Batches up to 20 are supported. JSON-RPC errors:
-32700parse,-32600invalid request,-32601unknown method,-32602invalid params.GET /health(alsoHEAD) is unauthenticated and returns JSON such as{"status":"ok","service":"hermes-mcp-bridge","version":"1.0.0","tools":5,"agents":{"default":"hermes","count":2,"available":2}}.HTTP errors:
401bad token,405non-POST,413body too large,415wrong content type,429rate limited withRetry-After.
Security
Authentication is a single 64-hex-character bearer token generated from 32 random bytes, stored only in
.env(mode600), never taken from argv or written to logs. It is SHA-256 hashed and compared with a constant-time comparison; failures return401withWWW-Authenticate.Per-IP token-bucket rate limiting (default 30 requests/minute, auth failures included) returns
429withRetry-After.Request bodies are capped at 256 KiB from both
Content-Lengthand streamed bytes. Upstream responses,result.json, and file reads have separate caps.Task file access is read-only. Path traversal (
..), absolute paths, backslashes, NUL bytes, and symlinks are rejected, and the resolved path must stay inside the task directory afterrealpath. Only regular UTF-8 text files without NUL bytes are returned.The audit log is JSONL with timestamp, client IP, method, path, status, duration, RPC method, tool, and error code. Headers, bodies, task text, tokens, and keys are never recorded. It is mode
600and rotates at 5 MiB.The listener binds only to
127.0.0.1; TLS ends at the tunnel or proxy. The local agent API key is read at runtime fromHERMES_ENV_PATHand never copied here.The systemd units apply hardening flags:
NoNewPrivileges,PrivateTmp,ProtectSystem=strict,ProtectHome=read-only, explicitReadWritePaths,ProtectKernelTunables,ProtectControlGroups,RestrictSUIDSGID,LockPersonality, and a restrictedRestrictAddressFamilieslist.
Setup
Requires Node.js 18.17+ and a local Hermes agent reachable over HTTP with an API key. There are no npm dependencies to install.
git clone https://github.com/Heldinhow/hermes-mcp-bridge.git
cd hermes-mcp-bridge
cp .env.example .env && chmod 600 .env
node scripts/gen-token.mjsgen-token.mjs writes 64 hex characters to MCP_AUTH_TOKEN in .env, atomically and with
mode 600. It prints the path, never the value. scripts/rotate-token.sh rotates the token,
rewrites .env, and restarts the bridge.
Edit .env for your environment:
MCP_PORT=8765
MCP_AUTH_TOKEN=<generated>
HERMES_ENV_PATH=/path/to/agent/.env
HERMES_CONTRACT_PATH=/path/to/contract.md
TASKS_DIR=/path/to/tasksHERMES_ENV_PATHpoints at the file holding the local agent'sAPI_SERVER_KEY. OptionalAPI_SERVER_HOSTandAPI_SERVER_PORTvalues are read from it (defaults:127.0.0.1:8642). The key is held in memory only and is exposed by no tool or log.HERMES_CONTRACT_PATHpoints at a Markdown contract injected as system instructions on everyhermes_task. Use it to define the agent's role, response JSON, allowed actions, escalation rules, and language.TASKS_DIRholds one subdirectory per task id, created on demand; files are only reachable throughhermes_resultandhermes_files. Use absolute or$HOMEpaths (~is not expanded).
Run the server and check health:
node src/server.js
curl -s http://127.0.0.1:8765/healthInstall the user units and start both services:
./scripts/install-systemd.sh
systemctl --user status hermes-mcp-bridge.service
systemctl --user status hermes-mcp-tunnel.serviceThe installer renders systemd/*.service into
${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user and starts both services. Run
loginctl enable-linger "$USER" once to survive logout; optional settings are documented
in .env.example.
Exposing it over HTTPS
Keep the bridge on loopback and let a tunnel or proxy terminate TLS. Pick one of the
options below; the MCP endpoint is always the public HTTPS URL plus /mcp.
Tailscale Funnel (recommended)
If the host is already on a tailnet, Funnel is the shortest path: no extra daemon, no DNS records, and a stable hostname that survives restarts.
Enable HTTPS certificates and Funnel for your tailnet in the Tailscale admin console (DNS page). Both are off by default.
Expose the local port through Tailscale:
tailscale funnel --bg --https=443 http://127.0.0.1:8765Port
443gives the bare hostname; other ports also work, e.g.--https=8443serves on:8443instead.--bgpersists the configuration across reboots and is not tied to your shell.Read the URL Tailscale prints and check it:
tailscale funnel status curl -s https://<machine>.<tailnet-domain>/healthIf you chose a non-443 port, append it to the host before
/healthand/mcp.
The MCP URL is https://<machine>.<tailnet-domain>/mcp (or ...:<port>/mcp). Write it to
PUBLIC_URL if you script clients against that file. To revoke the public endpoint:
tailscale funnel resetNote that Funnel makes the bridge reachable from the entire internet, not only from your
tailnet; the bearer token is the only gate, so keep .env at mode 600 and rotate the
token if it leaks.
scripts/run-tunnel.sh and systemd/hermes-mcp-tunnel.service implement the Cloudflare
quick-tunnel alternative below. If you use Funnel, the bridge unit is enough: skip or
disable the tunnel unit.
cloudflared quick tunnel
cloudflared tunnel --no-autoupdate --url http://127.0.0.1:8765The provider prints a public https://<tunnel-host> URL. scripts/run-tunnel.sh wraps this
mode, writes the URL to PUBLIC_URL, and is what the tunnel unit runs. Quick-tunnel
hostnames change on restart, so update clients afterwards.
cloudflared named tunnel
cloudflared tunnel create hermes-mcp-bridge
cloudflared tunnel route dns hermes-mcp-bridge mcp.example.com
cloudflared tunnel run hermes-mcp-bridgeRoute the hostname to http://127.0.0.1:8765 in your tunnel configuration.
nginx with certbot
server {
listen 443 ssl;
server_name mcp.example.com;
location / {
proxy_pass http://127.0.0.1:8765;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
}sudo certbot --nginx -d mcp.example.comMCP client configuration
Most clients accept an entry like the one below; exact field names vary, but the URL and header are what matter.
{
"mcpServers": {
"hermes": {
"type": "http", "url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer ${HERMES_MCP_TOKEN}" }
}
}
}Store the token in the environment as HERMES_MCP_TOKEN, not in the configuration file. If
your client does not expand environment variables, launch it through a wrapper that reads
the token from the protected .env and sets the header. Never commit, paste, or log it.
Testing
node test/smoke.mjs --base https://mcp.example.comThe smoke test reads MCP_AUTH_TOKEN from .env without printing it and checks /health,
401 without auth, initialize, tools/list (four tools), a sync task, hermes_result,
hermes_files, traversal/absolute-path rejection, and the body limit (--id picks the id).
License
MIT.
This server cannot be deployed
Maintenance
Related MCP Connectors
Discover and call AI agents via MCP. Supports A2A agents and platform agents with async tasks.
Query, browse, and automate OmegaAI workspaces from any MCP client. Streamable HTTP with OAuth 2.0.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Create, browse, remix, collaborate on, and run durable AI workflow nodes from MCP hosts.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables MCP agents to delegate tasks to a local Hermes Agent for terminal, file, browser, and coding operations, and schedule recurring jobs.MIT
- FlicenseNot gradedqualityAmaintenanceEnables MCP-compatible AI clients to invoke CLI-driven agent tools over Streamable HTTP, including shell execution, file operations, patching, image viewing, web search, and nested agent tasks, with permission modes and real-time progress streaming.-
- FlicenseNot gradedqualityBmaintenanceEnables governed remote execution of Hermes Agent tasks over MCP with client authentication, RBAC, audit logging, and human-in-the-loop approvals.-
- AlicenseNot gradedqualityBmaintenanceEnables controlling a Hermes Agent instance from any MCP client, exposing a broad set of Hermes CLI operations over MCP Streamable HTTP with bearer auth.MIT