Skip to content

Connect: Hermes Agent

Recipe · through our MCP server · their docs checked 2026-10-30

Hermes Agent (Nous Research) has a built-in MCP client. It loads our tools from one entry in its config file. Data enters at Hermes' tool call and leaves as the MCP result.

Our side

On the same machine, in development:

def local_app() -> Any:
    """MCP only, no auth, loopback peers only: a framework on this machine, in development."""
    from agent_adapter.integrations.mcp import streamable_http_app

    return streamable_http_app(registry)  # http://127.0.0.1:8000/mcp; remote peers get 403

Anywhere else, the public app:

def public_app(tools: Registry, host: str, agent_token: str, reviewer_token: str) -> Any:
    """REST at /, MCP at /mcp, one bearer check for both: for a host on the internet."""
    from mcp.server.transport_security import TransportSecuritySettings

    from agent_adapter.server import create_app

    tokens = {
        agent_token: Principal("hosted-agent"),  # the token the remote framework sends
        reviewer_token: Principal("reviewer@example.com", scopes=frozenset({APPROVER_SCOPE})),
    }
    security = TransportSecuritySettings(allowed_hosts=[host], allowed_origins=[f"https://{host}"])
    return create_app(
        tools,
        authenticate=bearer_auth(tokens),
        host="0.0.0.0",  # a public bind: the public-host checks apply
        allowed_hosts=[host],  # REST: any other Host header gets 421
        mcp_options={"transport_security": security},  # MCP: the same rule
    )

Their side

~/.hermes/config.yaml:

mcp_servers:
  shop_tools:
    url: "https://tools.example.com/mcp"   # local: http://127.0.0.1:8000/mcp
    headers:
      Authorization: "Bearer ${AA_AGENT_TOKEN}"   # the agent token, never the reviewer's
    timeout: 30            # seconds per tool call
    connect_timeout: 30    # seconds for the first handshake
    tools:
      exclude: [send_invoice]   # confirm tools: Hermes cannot resume them
Key Meaning
mcp_servers.<name>.url our /mcp endpoint
headers sent on every request; carries the bearer token
timeout, connect_timeout call and handshake limits, in seconds
tools.include / tools.exclude allow or block tools by name (exclude takes globs)
auth: oauth Hermes' OAuth 2.1 flow; our bearer_auth takes static tokens, so use headers

Checklist

Check Why
the token in headers maps to its own Principal Hermes' runs and approvals stay apart from other hosts'
exclude confirm tools, or serve hosted Hermes does not send our resume keys back; a parked call never runs
Hermes' own "Allow Once / Deny" prompt is extra it adds a gate; our executor still decides confirm tools
everything on the public-host checklist

Sources