Add MCP bridge to claude-agent so Hermes can delegate to the real Claude Code CLI

New POST /mcp endpoint (Streamable HTTP transport, stateless — fresh
McpServer+transport per request) exposing one tool, ask_claude_code: runs
the real `claude` binary against a prompt, billed against the Pro/Max
subscription rather than API credits. This works specifically because it's
the actual claude CLI making the request server-side — the same reason
Hermes itself can't authenticate with the subscription directly (proven
earlier: Anthropic rejects the OAuth token from any client that isn't the
real CLI's exact request fingerprint). Read-only: no Edit/Write/git-push/
git-commit tools, since this is a quick-answer bridge, not a repo editor.

Tested end-to-end locally (built + ran the image, curled the full MCP
handshake: initialize -> tools/list -> tools/call) before pushing — got a
real 'pong' back from the actual claude CLI through the MCP protocol.

To register it with Hermes (lives in its own data volume, not git — see
.env.example comment):
  docker exec hermes hermes config set mcp_servers.claude-code.url http://claude-agent:3001/mcp
  docker exec hermes hermes config set 'mcp_servers.claude-code.headers.Authorization' 'Bearer <MCP_BRIDGE_KEY>'
This commit is contained in:
2026-08-23 17:24:46 +00:00
parent ec37245b37
commit 732246d4fd
5 changed files with 116 additions and 4 deletions
+7 -3
View File
@@ -120,9 +120,10 @@ services:
- "traefik.http.services.hermes-dashboard.loadbalancer.server.port=9119"
claude-agent:
# Gitea PR-review only now — no Matrix presence (see hermes above; only one agent
# is meant to be in Matrix). Still triggered by Gitea's pull_request webhook and
# posts review comments there, entirely independent of Matrix/LiteLLM.
# No Matrix presence (see hermes above; only one agent is meant to be in Matrix).
# Two things call this now: Gitea's pull_request webhook (PR review), and Hermes,
# over MCP (POST /mcp), to delegate a question to the real `claude` CLI when it
# specifically wants the Claude subscription instead of whatever LiteLLM routed it to.
image: ${GITEA_REGISTRY_IMAGE}
container_name: claude-agent
restart: unless-stopped
@@ -136,6 +137,9 @@ services:
# Claude subscription (Pro/Max) auth via `claude setup-token`, not API billing —
# Claude Code reads this in preference to ANTHROPIC_API_KEY when both could apply.
CLAUDE_CODE_OAUTH_TOKEN: ${CLAUDE_CODE_OAUTH_TOKEN}
# Shared secret for the /mcp bridge endpoint (internal network only either way, but
# this keeps it from being callable by anything that merely reaches the container).
MCP_BRIDGE_KEY: ${MCP_BRIDGE_KEY}
volumes:
- agent_workspace:/workspace
networks: