hermes-mcp-bridge
Bridges a self-hosted Hermes agent to remote MCP clients over Streamable HTTP, exposing kanban and vault tools via its REST API. It enforces strict security through Cloudflare Access JWT validation and prevents the agent from approving its own work.
README
hermes-mcp-bridge
A Streamable HTTP MCP bridge between a self-hosted Hermes agent and a remote MCP client (Claude on the web or mobile, or any other compatible client), behind Cloudflare Access.
Two constraints define it, and they pull in opposite directions: expose nothing to the Internet, and give no automated process the rights it would need to do real damage.
The full account of getting this into production, including the five walls hit along the way (in French): Brancher un agent self-hosted sur Claude sans lui donner les clés de la maison.
What it does
Seven tools, all going through Hermes' dashboard REST API — not the Docker socket, not its SQLite databases:
| Tool | Role |
|---|---|
kanban_board |
kanban state, column by column |
kanban_task |
card detail: body, status, comments, result |
kanban_create |
drops a card in triage, unassigned |
kanban_comment |
comments on a card without approving it |
vault_list |
lists the files produced by the agent |
vault_read |
reads one of those files |
ask |
one-off question through the OpenAI-compatible gateway |
Going through the public API pays off three times over: the bridge needs no special privilege, it depends on no internal detail (so it survives Hermes upgrades), and its surface is exactly that of an API already designed to be called.
What it will never do
Approve a card.
The agent is allowed to draft a cold outreach email. It is not allowed to send it. Between the two sits a kanban column: the card stops there awaiting approval, and a human is the one who moves it to "done". That gesture is the safeguard.
Exposing that transition would have emptied it of meaning: an agent able to approve its own work is no longer supervised, it just has one more step to clear. So there is no code for it in this repository, and a test fails if anyone adds some.
Same logic at creation time: a card dropped by the bridge is born in triage, unassigned, therefore frozen. The remote agent can propose work, not start it.
Quick start
git clone https://github.com/mickadoua/hermes-mcp-bridge.git
cd hermes-mcp-bridge
cp .env.example .env # then fill in HERMES_API_URL, ACCESS_AUD, PUBLIC_HOSTNAMES
pip install -e ".[dev]"
python -m hermes_mcp_bridge
In a container, with the tunnel connector alongside:
docker compose up -d --build
No port is published on the host: the tunnel goes out, nothing comes in.
Configuration
Everything comes from the environment; see .env.example for the annotated list. The four values that matter:
| Variable | Why it matters |
|---|---|
HERMES_API_URL |
the dashboard's internal URL, as the bridge container reaches it |
ACCESS_TEAM_DOMAIN |
the expected JWT issuer (https://<team>.cloudflareaccess.com) |
ACCESS_AUD |
the audience tag of this application — without it, a token issued for another app on the same account would pass |
PUBLIC_HOSTNAMES |
the public hostnames served by the tunnel; left empty, the SDK rejects everything with a 421 (see below) |
The dashboard's REST paths are grouped at the top of hermes_mcp_bridge/hermes.py. If your version of Hermes exposes them elsewhere, that is the only place to change.
Security
The bridge validates for itself the JWT that Cloudflare Access injects into every request: signature against the account's public keys, issuer, audience, expiry.
This is not redundant with the filtering at the edge. The container listens on 0.0.0.0 and shares a Docker network with other services: without this layer, any neighbouring container drives the kanban with no authentication at all, never going through Cloudflare. The edge protects you from the Internet, not from the neighbours. It is also the condition Cloudflare sets for enabling "managed OAuth": only enable it for an MCP server that validates the Access JWT.
ACCESS_VERIFY_JWT=false exists for local development, and the bridge logs it loudly at every startup.
The Cloudflare-side setup — tunnel, the two policies (machines and humans, which do not mix), managed OAuth, redirect URI — is described in docs/cloudflare-access.md.
The 421 that catches everyone out
The Python MCP SDK ships DNS-rebinding protection. streamable_http_app() takes a host parameter that defaults to 127.0.0.1, and if the policy is not configured explicitly, the SDK derives one restricted to the loopback: every public hostname is rejected with 421 Invalid Host header, leaving nothing behind but a single server-side log line — the client only sees a generic transport error.
That is what PUBLIC_HOSTNAMES is for: each name is expanded into a "with port" variant, then handed to the SDK. Two tests pin the behaviour in both directions (tests/test_server.py).
Tests
pytest
The suite checks above all the thing people forget to check: that a legitimate token gets through. A validator that rejects everything looks exactly like a correct one; "no token → 401" and "forged token → 401" prove nothing on their own.
Licence
MIT.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。