Skip to content

About

Governed MCP gateway: exposes Hermes Agent task execution over MCP Streamable HTTP with OAuth 2.1, RBAC, HITL, and audit

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

8 Commits

Folders and files

Repository files navigation

hermes-mcp-gateway

A governed MCP gateway that exposes Hermes Agent one-shot task execution to remote MCP clients over MCP Streamable HTTP. Clients authenticate with OAuth 2.1 client credentials, receive per-client RBAC (toolset/model/workdir allowlists, concurrency and duration caps), and terminal-capable clients run behind task-level human-in-the-loop approval. Every task, approval, and issued token is written to a SQLite audit store.

Architecture

MCP client ── HTTP (Bearer JWT) ──> gateway (policy / audit)
                                        │
                                        ▼
              hermes -p mcp-worker chat -q <prompt>  (subprocess)
                                        │
                                        ▼
                              per-task status.json + session store

The gateway is a single Starlette app: /token and /.well-known/* handle OAuth discovery and issuance, /healthz reports liveness, and /mcp is the Bearer-authed MCP Streamable HTTP endpoint. Tasks run as governed hermes -p mcp-worker chat -q subprocesses with capped duration, captured stdout/stderr, and the Hermes session id attributed back to the task row.

Quickstart

uv sync

mkdir -p ~/.hermes/mcp-gateway
cp deploy/config.yaml.example ~/.hermes/mcp-gateway/config.yaml
# edit the config: point hermes.bin at hermes and set workdirs to a real path

openssl rand -hex 32   # this is your signing key
export HERMES_MCP_GATEWAY_SIGNING_KEY="<the hex value>"

# hash a client secret and put its sha256 into config for the client
uv run hermes-mcp-gateway clients hash spec-client-secret
# edit config: replace the placeholder secret_hash for your client

uv run hermes-mcp-gateway --check   # validate config, no server started
uv run hermes-mcp-gateway serve     # or just: hermes-mcp-gateway
curl -sf http://127.0.0.1:8778/healthz

Getting a token

The token endpoint speaks client credentials with either client_secret_basic or client_secret_post:

# client_secret_basic: HTTP Basic (client_id:client_secret)
curl -s -u research:spec-client-secret \
  -d grant_type=client_credentials \
  http://127.0.0.1:8778/token

# client_secret_post: credentials in the form body
curl -s \
  -d grant_type=client_credentials \
  -d client_id=research \
  -d client_secret=spec-client-secret \
  http://127.0.0.1:8778/token

The response is {"access_token": "...", "token_type": "Bearer", "expires_in": 600, "scope": "..."}. Pass access_token as the bearer token on /mcp.

MCP client configuration

OAuth bearer handling is client-specific, so configure your client to send the bearer token directly on the /mcp URL.

Claude Code (.mcp.json):

{
  "mcpServers": {
    "hermes-mcp-gateway": {
      "type": "http",
      "url": "http://127.0.0.1:8778/mcp",
      "headers": {
        "Authorization": "Bearer <access_token>"
      }
    }
  }
}

Generic mcpServers JSON (many clients accept this shape):

{
  "mcpServers": {
    "hermes-mcp-gateway": {
      "url": "http://127.0.0.1:8778/mcp",
      "headers": {
        "Authorization": "Bearer <access_token>"
      }
    }
  }
}

Security model

  • Deny-by-default toolset scopes: a client can only request toolsets granted by a toolset:* scope, and any disallowed toolset rejects the whole request.
  • Empty resolved toolsets are forced to the locked-down safe toolset (never the profile's own defaults, which would include terminal).
  • Workdir jail: tasks run only inside a real directory that resolves under a configured workdir root, symlink escapes rejected.
  • Model allowlist: configured models restrict which models a client may pick.
  • Per-client concurrency and duration caps (max_concurrency, max_duration_s) plus a turn budget (max_turns).
  • Task-level human-in-the-loop: requires_approval clients with terminal park the task as pending_approval until an operator approves or denies.
  • Per-task session_id attribution: the Hermes session id is parsed from task stderr and stored.
  • SQLite audit store records a row for every task, approval decision, and issued token.
  • --yolo is never passed to hermes; approval timeouts and non-TTY dangerous command approvals stay at hermes defaults and auto-deny inside hermes.

Operator CLI

The hermes-mcp-gateway entry point serves the gateway by default and exposes operator verbs. Every verb reads --config like serve does.

Command Effect
serve (default) Run the gateway server
clients list Table of clients: id, scopes, approval flag, caps, workdirs, models
clients hash <secret|-> Print sha256:<hex> of a client secret (- reads stdin)
approvals pending Rows of task_id, client_id, created_at, prompt
approvals approve <task_id> Approve and spawn a pending task (no-op if already decided)
approvals deny <task_id> [reason] Deny a pending task with an optional reason
tasks list [--client ID] [--limit N] List tasks, newest first
tasks show <task_id> Full task row plus approval state and output path
tasks cancel <task_id> Cancel a running task (no-op if already finished)
token issue <client_id> [scopes...] Dev-only token issuance (requires HERMES_MCP_GATEWAY_ALLOW_DEV_TOKEN=1)

All verbs exit nonzero on an unknown task or client.

Deployment (systemd user unit)

mkdir -p ~/.config/systemd/user
cp deploy/hermes-mcp-gateway.service ~/.config/systemd/user/
chmod 600 ~/.hermes/mcp-gateway/env   # file holds HERMES_MCP_GATEWAY_SIGNING_KEY
systemctl --user daemon-reload
systemctl --user enable --now hermes-mcp-gateway
journalctl --user -u hermes-mcp-gateway -f

The unit template lives at deploy/hermes-mcp-gateway.service; it runs the gateway from the project venv, reads the signing key from ~/.hermes/mcp-gateway/env, and restarts on failure.

Roadmap

  • Command-level HITL via the hermes approval queue (finer than task-level).
  • RS256 / JWKS token verification for stateless, header-only clients.
  • Notification push (webhook/queue) when a task needs approval or finishes.
  • Per-task cost and token evidence sourced from the session store.

About

Governed MCP gateway: exposes Hermes Agent task execution over MCP Streamable HTTP with OAuth 2.1, RBAC, HITL, and audit

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages