MCP (Model Context Protocol) in Hermes Agent: Connecting External Tool Servers

Connect Hermes Agent to external MCP tool servers for GitHub, databases, APIs, and more.

Tool Protocolsintermediate18 min read
MCP (Model Context Protocol) in Hermes Agent: Connecting External Tool Servers

This page covers how Hermes Agent uses the Model Context Protocol (MCP) to connect to external tool servers. Once wired up, Hermes can call tools from any MCP-compliant server, GitHub, databases, file systems, browser stacks, internal APIs, and more, as if they were native Hermes tools.

What It Does

MCP gives Hermes Agent the following concrete capabilities, as documented by the official Hermes documentation:

  • Access to external tool ecosystems without writing a native Hermes tool first.
  • Local stdio servers and remote HTTP MCP servers in the same configuration.
  • Automatic tool discovery and registration at startup.
  • Utility wrappers for MCP resources and prompts when supported by the server.
  • Per-server filtering so you can expose only the MCP tools you actually want Hermes to see.

In practice, this means you can connect Hermes to a vast array of existing tools and services. Instead of building a custom integration for every external API, you point Hermes at an MCP server that already wraps that API. Hermes discovers the server's tools, registers them with a prefix to avoid name collisions, and makes them available to the agent during reasoning. The agent can then call those tools just like any built-in tool.

The documentation also highlights that MCP support ships with the standard Hermes install, no extra installation step is needed. You add MCP server definitions to your config file, and Hermes handles the rest.

Setup

Prerequisites

  • Hermes Agent installed (the standard install includes MCP support).
  • Node.js and npx available on PATH if you plan to use stdio servers that run via npx (e.g., the filesystem server). The documentation explicitly recommends verifying these: node --version and npx --version.
  • Python and uv available if you need to verify MCP dependencies: cd ~/.hermes/hermes-agent && uv pip install -e ".[mcp]".

Adding an MCP Server Manually

  1. Open your Hermes configuration file at ~/.hermes/config.yaml.
  2. Add an mcp_servers section. Each key under mcp_servers is a name you choose for that server (e.g., filesystem, github, stripe).
  3. For a stdio server, provide command and args. For an HTTP server, provide url and optionally headers.
  4. Start Hermes with hermes chat.
  5. Ask Hermes to use the MCP-backed capability. For example: "List the files in /home/user/projects and summarize the repo structure."

Here is the minimal stdio example from the documentation, verbatim:

mcp_servers:
  filesystem:
    command: "npx"
    args:
      ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]

And the minimal HTTP example:

mcp_servers:
  company_api:
    url: "https://mcp.internal.example.com"
    headers:
      Authorization: "Bearer ***"

Using the Catalog (One-Click Install for Nous-Approved MCPs)

Hermes ships a curated catalog of MCP servers that Nous staff has reviewed and merged. They are disabled by default, you install only what you actually want.

hermes mcp          # interactive picker (default)
hermes mcp catalog  # plain-text list, scriptable
hermes mcp install n8n  # install a catalog entry by name

The picker shows each entry with its current status. For example:

n8n            available     Manage and inspect n8n workflows from Hermes
linear         enabled       Linear issue/project management (remote OAuth)
github         installed (disabled)  GitHub repo + PR tools

Hit Enter on a row to install (and walk through any required credentials), enable, disable, or uninstall.

Catalog entries are stored under optional-mcps/ in the hermes-agent repo, presence in that directory means Nous approval. There is no community submission tier; entries are added by merging a PR.

Catalog entries can require:

  • API key, Hermes prompts at install time and writes the value to ~/.hermes/.env. Non-secret values (base URLs) go to the same file.
  • OAuth (remote MCP), written as auth: oauth in your config; the MCP client opens a browser on first connection.
  • OAuth (third-party provider like Google/GitHub), Hermes points you at hermes auth <provider> if you haven't authenticated already.

Tool Selection at Install Time

After credentials are configured, Hermes probes the MCP server to list every tool it exposes and presents a checklist:

Select tools for 'linear' (SPACE toggle, ENTER confirm)
[x] find_issues         Find issues matching a query
[x] get_issue           Get a single issue
[x] create_issue        Create a new issue
[ ] delete_workspace    Delete a Linear workspace
...

The pre-checked rows come from:

  • Your prior selection if you've installed this entry before (reinstalls preserve what you had, the manifest's defaults don't override it).
  • The manifest's tools.default_enabled if the entry declares one (some catalog entries pre-prune mutating or rarely-useful tools).
  • Everything if neither applies.

Submit the checklist with ENTER. Only the checked tools end up in mcp_servers.<name>.tools.include. If you select everything, no filter is written (cleanest config shape, identical behavior). If the probe fails (server unreachable, OAuth not yet completed, backing service not running), the install still succeeds: the manifest's tools.default_enabled is applied directly (if declared), or no filter is written (if not). Re-run hermes mcp configure <name> once the server is reachable to refine.

Updating Tool Selection Later

hermes mcp configure linear

This reopens the same checklist with your current selection pre-checked. Use this when you want more tools enabled, or when the server has added new tools that you want to opt into.

Updating the Catalog Manifest

MCPs are never auto-updated. Re-run hermes mcp install <name> to refresh after a Hermes update if a manifest version changed. To add an MCP to the catalog, open a PR against optional-mcps/.

Using Built-in Presets

For well-known MCP servers, hermes mcp add accepts a --preset flag that fills in the transport details so you don't have to look up the command and args. The preset only supplies defaults, anything else (env vars, headers, filtering) you pass on the same command line still wins.

PresetWhat it wires up
codexThe Codex CLI's MCP server (codex mcp-server over stdio). Requires the codex CLI on PATH.
# Add Codex CLI as an MCP server in one line
hermes mcp add codex --preset codex

That writes the equivalent of:

mcp_servers:
  codex:
    command: "codex"
    args: ["mcp-server"]

You can pick any local name (hermes mcp add my-codex --preset codex is fine); the preset only provides the command/args defaults.

Configuration Reference

Hermes reads MCP config from ~/.hermes/config.yaml under mcp_servers. Each server entry is a dictionary. The following table lists every option the documentation documents, its type, and its meaning.

Common Keys

KeyTypeMeaning
commandstringExecutable for a stdio MCP server
argslistArguments for the stdio server
envmappingEnvironment variables passed to the stdio server
urlstringHTTP MCP endpoint
headersmappingHTTP headers for remote servers
client_certstring or listClient certificate for mTLS, a combined PEM path, or [cert, key] / [cert, key, password]
client_keystringClient private-key PEM path (when separate from client_cert)
timeoutnumberTool call timeout
connect_timeoutnumberInitial connection timeout (also bounds the MCP initialize handshake)
idle_timeout_secondsnumberRecycle a stdio server after this many seconds without a tool call (0 = never, default). The server restarts transparently on the next tool call.
max_lifetime_secondsnumberRecycle a stdio server after this total age (0 = never, default). Restarts transparently on next use.
enabledboolIf false, Hermes skips the server entirely
supports_parallel_tool_callsboolIf true, tools from this server may run concurrently
toolsmappingPer-server tool filtering and utility policy

Tools Sub-keys

The tools mapping supports the following sub-keys:

KeyTypeMeaning
includelistWhitelist of tool names to register from this server
excludelistBlacklist of tool names to exclude from this server
promptsboolIf false, disables the list_prompts and get_prompt utility wrappers for this server
resourcesboolIf false, disables the list_resources and read_resource utility wrappers for this server

OAuth Sub-keys

When auth: oauth is set, you can also specify an oauth mapping with these sub-keys:

KeyTypeMeaning
redirect_portnumberFixed port for the callback listener (useful when behind a proxy)
redirect_uristringPublic HTTPS endpoint that forwards to the host (e.g., a Tailscale Funnel or reverse proxy)
redirect_hoststringSet to localhost to use http://localhost:<port>/callback instead of 127.0.0.1 (workaround for WAFs that block 127.0.0.1 in redirect URIs)
client_idstringPre-registered OAuth client ID (for providers that don't support dynamic registration)
client_secretstringPre-registered OAuth client secret

mTLS Configuration Shapes

The client_cert key accepts three shapes, as documented:

  1. A single combined PEM path, one file holding both the certificate and the private key:
mcp_servers:
  internal_api:
    url: "https://mcp.internal.example.com/mcp"
    client_cert: "~/.certs/mcp-client.pem"
  1. A [cert, key] 2-tuple, certificate and key in separate files (equivalent to setting client_cert + client_key):
mcp_servers:
  internal_api:
    url: "https://mcp.internal.example.com/mcp"
    client_cert: ["~/.certs/mcp-client.crt", "~/.certs/mcp-client.key"]
  1. A [cert, key, password] 3-tuple, when the private key is encrypted, the third element is the key passphrase:
mcp_servers:
  internal_api:
    url: "https://mcp.internal.example.com/mcp"
    client_cert: ["~/.certs/mcp-client.crt", "~/.certs/mcp-client.key", "${MCP_KEY_PASSWORD}"]

You can also keep the cert and key fully separate via client_cert (combined PEM) plus an explicit client_key. Paths support ~ expansion; a missing file raises a clear, server-scoped error rather than an opaque TLS handshake failure.

Runtime ${ENV_VAR} Substitution

Inside an entry's transport.command, transport.args, transport.url, and headers, ${VAR} placeholders are resolved at server-connect time from environment variables (which include everything in ~/.hermes/.env). This is useful when a catalog entry wants to reference a value the user configured elsewhere, e.g., ${HOME}/foo or ${MY_PROVIDER_TOKEN}. Note this is distinct from ${INSTALL_DIR} in catalog manifests, which is substituted at install-time with the path the catalog cloned the entry's repo into.

Manifest Version Compatibility

Manifests pin a manifest_version. The catalog is forward-compatible: if a PR adds an entry with a newer manifest_version than your installed Hermes understands, the picker will surface a warning (⚠ '<name>' requires a newer Hermes) for that entry instead of silently hiding it. Run hermes update to install the latest Hermes when you see that.

How It Works

Diagram: How It Works

Architecture and Transport

Hermes Agent supports two kinds of MCP servers: stdio servers and HTTP servers.

Stdio servers run as local subprocesses and talk over stdin/stdout. The documentation recommends using stdio servers when:

  • the server is installed locally
  • you want low-latency access to local resources
  • you are following MCP server docs that show command, args, and env

HTTP servers are remote endpoints Hermes connects to directly. The documentation recommends using HTTP servers when:

  • the MCP server is hosted elsewhere
  • your organization exposes internal MCP endpoints
  • you do not want Hermes spawning a local subprocess for that integration

Tool Registration and Naming

Hermes prefixes MCP tools so they do not collide with built-in names. The pattern is:

mcp_<server_name>_<tool_name>

Examples from the documentation:

ServerMCP toolRegistered name
filesystemread_filemcp_filesystem_read_file
githubcreate-issuemcp_github_create_issue
my-apiquery.datamcp_my_api_query_data

In practice, you usually do not need to call the prefixed name manually, Hermes sees the tool and chooses it during normal reasoning.

Utility Tools for Resources and Prompts

When supported, Hermes also registers utility tools around MCP resources and prompts:

  • list_resources
  • read_resource
  • list_prompts
  • get_prompt

These are registered per server with the same prefix pattern, for example: mcp_github_list_resources, mcp_github_get_prompt.

These utility tools are now capability-aware:

  • Hermes only registers resource utilities if the MCP session actually supports resource operations.
  • Hermes only registers prompt utilities if the MCP session actually supports prompt operations.

So a server that exposes callable tools but no resources/prompts will not get those extra wrappers.

Discovery Time

Hermes discovers MCP servers at startup and registers their tools into the normal tool registry.

Dynamic Tool Discovery

MCP servers can notify Hermes when their available tools change at runtime by sending a notifications/tools/list_changed notification. When Hermes receives this notification, it automatically re-fetches the server's tool list and updates the registry, no manual /reload-mcp required. This is useful for MCP servers whose capabilities change dynamically (e.g., a server that adds tools when a new database schema is loaded, or removes tools when a service goes offline). The refresh is lock-protected so rapid-fire notifications from the same server don't cause overlapping refreshes. Prompt and resource change notifications (prompts/list_changed, resources/list_changed) are received but not yet acted on.

Reloading

If you change MCP config, use:

/reload-mcp

This reloads MCP servers from config and refreshes the available tool list. For runtime tool changes pushed by the server itself, see Dynamic Tool Discovery above.

Toolsets

Each configured MCP server also creates a runtime toolset when it contributes at least one registered tool:

mcp-<server_name>

That makes MCP servers easier to reason about at the toolset level.

OAuth Flow for HTTP Servers

Most hosted MCP servers (Linear, Sentry, Atlassian, Asana, Figma, Stripe, and others) require OAuth 2.1 instead of a static bearer token. Set auth: oauth and Hermes handles discovery, dynamic client registration, PKCE, token exchange, refresh, and step-up auth via the MCP Python SDK.

mcp_servers:
  linear:
    url: "https://mcp.linear.app/mcp"
    auth: oauth

On first connect, Hermes prints an authorize URL, opens your browser when possible, and waits for the OAuth callback on a local loopback port. Tokens are cached at ~/.hermes/mcp-tokens/<server_name>.json with 0o600 perms; subsequent runs reuse them silently until refresh fails.

Remote / Headless Hosts

When Hermes runs on a different machine than your browser, the loopback callback can't reach your laptop. The documentation provides two ways to complete the flow:

  1. Paste-back (no setup): on an interactive terminal Hermes prints "Or paste the redirect URL here…" alongside the authorize URL. Open the URL in your browser, approve, copy the full URL the browser ends up on (the redirect will show a connection error, that's expected), paste it at the prompt. Bare ?code=...&state=... query strings work too.

  2. SSH port forward: ssh -N -L <local_port>:127.0.0.1:<remote_port> user@host in a separate terminal, then let the redirect flow normally.

  3. Proxied callback (redirect_uri): when a public HTTPS endpoint forwards to the host (e.g., a Tailscale Funnel or reverse proxy pointed at the callback port), set oauth.redirect_uri and the browser redirect reaches Hermes on its own, no tunnel or paste needed:

mcp_servers:
  myserver:
    url: "https://mcp.example.com/mcp"
    auth: oauth
    oauth:
      redirect_port: 8765  # fixed port for the proxy to target
      redirect_uri: "https://oauth.example.ts.net/callback"

For fully headless gateways (messaging bot, no interactive terminal at all), the optional mcp-oauth-remote-gateway skill walks the agent through completing the flow manually and writing tokens where Hermes expects them.

Pitfall: WAF Rejects 127.0.0.1 Redirect URIs

A few providers front their authorization server with a WAF that 403s any authorize request whose query string contains a literal 127.0.0.1 (Reclaim.ai's AWS API Gateway is a known example, every attempt returns {"message":"Forbidden"} before reaching the OAuth app). Set oauth.redirect_host: localhost to use http://localhost:<port>/callback instead; the callback listener still binds 127.0.0.1 either way.

Pitfall: Providers That Don't Support Automatic Registration (Google Drive, Atlassian)

Some servers reject the dynamic client registration step (RFC 7591) that bare auth: oauth relies on, Google's official Drive server (https://drivemcp.googleapis.com/mcp/v1) returns a 400 Bad Request, so no OAuth client is created and no token is acquired. The symptom is subtle: these servers also serve tools/list without auth, so hermes mcp login can list the tools and look like it worked, but every real tool call later times out. hermes mcp login now detects this (it checks that a token actually landed on disk) and tells you to supply your own OAuth client. Create one in the provider's console and add it to config:

mcp_servers:
  googledrive:
    url: "https://drivemcp.googleapis.com/mcp/v1"
    auth: oauth
    oauth:
      client_id: "<your_client_id>"
      client_secret: "<your_client_secret>"

Then run hermes mcp login googledrive, with the pre-registered client, Hermes skips registration and runs the normal browser authorization flow.

Pitfall: Config Auto-Reload Race

When you edit ~/.hermes/config.yaml from inside a running Hermes session, the CLI auto-reloads MCP connections with a 30s timeout. That's not enough for an interactive OAuth flow. Add the entry, then run hermes mcp login <name> from a fresh terminal, it waits the full 5 minutes for you to complete auth.

Security Model

Stdio Env Filtering

For stdio servers, Hermes does not blindly pass your full shell environment. Only explicitly configured env plus a safe baseline are passed through. This reduces accidental secret leakage.

Config-Level Exposure Control

The filtering support is also a security control:

  • disable dangerous tools you do not want the model to see
  • expose only a minimal whitelist for a sensitive server
  • disable resource/prompt wrappers when you do not want that surface exposed

Trust Model for Catalog Entries

Installing a catalog entry runs whatever the manifest specifies, git clone, the entry's bootstrap commands (pip install, npm install, etc.), and ultimately the MCP server's own code. Manifests are gated by PR review into the hermes-agent repo, so Nous has reviewed each entry before it shipped, but you should still read the manifest before installing, especially the source: field's repository, the install.bootstrap: commands, and any transport.command: invocation.

Manifests live at optional-mcps/<name>/manifest.yaml on GitHub. The picker also prints the manifest's source: URL at install time so you can quickly verify the upstream repo. The web dashboard's MCP page surfaces the same detail per catalog entry, transport, auth type, the endpoint URL (HTTP) or command + args (stdio), the git install source/ref and bootstrap commands, and setup notes, with the source: rendered as a clickable link, so you can inspect exactly what an entry connects to or runs before clicking Install.

Requirements and Limitations

Prerequisites

  • Hermes Agent installed (MCP support ships with the standard install).
  • For stdio servers that run via npx: Node.js and npx must be available on PATH.
  • For verifying MCP dependencies: Python and uv available (cd ~/.hermes/hermes-agent && uv pip install -e ".[mcp]").

Supported Platforms

The documentation does not specify supported operating systems or architectures. The commands and paths shown (e.g., ~/.hermes/config.yaml, npx, uv) suggest Linux and macOS are primary targets. Windows is not mentioned.

Known Limitations

  • Prompt and resource change notifications: prompts/list_changed and resources/list_changed notifications are received but not yet acted on. Only notifications/tools/list_changed triggers a refresh.
  • Auto-reload race: The CLI auto-reloads MCP connections with a 30s timeout when you edit the config file from inside a running session. This is not enough for an interactive OAuth flow. Use a fresh terminal for hermes mcp login.
  • OAuth dynamic registration failures: Some providers (Google Drive, Atlassian) reject the dynamic client registration step. You must supply your own client_id and client_secret.
  • WAF blocking 127.0.0.1: Some authorization servers block redirect URIs containing 127.0.0.1. Use oauth.redirect_host: localhost as a workaround.
  • Parallel tool calls are opt-in: By default, MCP tools run sequentially. You must set supports_parallel_tool_calls: true to enable concurrent execution. The documentation includes a caution: "Only enable parallel calls for servers whose tools are safe to run concurrently."
  • Utility tools are capability-aware: Hermes only registers resource/prompt utility wrappers if the server session actually supports those capabilities. This is intentional.
  • Empty toolset: If your config filters out all callable tools and disables or omits all supported utilities, Hermes does not create an empty runtime MCP toolset for that server.
  • Catalog entries are not auto-updated: You must re-run hermes mcp install <name> to refresh after a Hermes update if a manifest version changed.

Troubleshooting

MCP Server Not Connecting

The documentation provides these checks:

# Verify MCP deps are installed (already included in standard install)
cd ~/.hermes/hermes-agent && uv pip install -e ".[mcp]"
node --version
npx --version

Then verify your config and restart Hermes.

Tools Not Appearing

Possible causes, as listed in the documentation:

  • the server failed to connect
  • discovery failed
  • your filter config excluded the tools
  • the utility capability does not exist on that server
  • the server is disabled with enabled: false

If you are intentionally filtering, this is expected.

Why Didn't Resource or Prompt Utilities Appear?

Because Hermes now only registers those wrappers when both are true:

  • your config allows them
  • the server session actually supports the capability

This is intentional and keeps the tool list honest.

OAuth Flow Fails on Remote Host

If Hermes runs on a different machine than your browser, use the paste-back method or SSH port forwarding as described in the "Remote / Headless Hosts" section above.

OAuth Flow Times Out

If you edit the config file from inside a running Hermes session, the auto-reload has a 30s timeout. Run hermes mcp login <name> from a fresh terminal instead.

Provider Rejects Dynamic Client Registration

If you get a 400 Bad Request during OAuth setup (e.g., with Google Drive), supply your own client_id and client_secret in the config under oauth.client_id and oauth.client_secret, then run hermes mcp login <name> again.

WAF Returns 403 on Authorization Request

If you see {"message":"Forbidden"} when trying to authorize, set oauth.redirect_host: localhost in your config to use http://localhost:<port>/callback instead of 127.0.0.1.

Manifest Version Warning

If the catalog picker shows a warning like ⚠ '<name>' requires a newer Hermes, run hermes update to install the latest Hermes.

Example Use Cases

GitHub Server with a Minimal Issue-Management Surface

mcp_servers:
  github:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "***"
    tools:
      include: [list_issues, create_issue, update_issue]
      prompts: false
      resources: false

Use it like: "Show me open issues labeled bug, then draft a new issue for the flaky MCP reconnection behavior."

Stripe Server with Dangerous Actions Removed

mcp_servers:
  stripe:
    url: "https://mcp.stripe.com"
    headers:
      Authorization: "Bearer ***"
    tools:
      exclude: [delete_customer, refund_payment]

Use it like: "Look up the last 10 failed payments and summarize common failure reasons."

Filesystem Server for a Single Project Root

mcp_servers:
  project_fs:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/my-project"]

Use it like: "Inspect the project root and explain the directory layout."

Recycling Memory-Heavy Stdio Servers

Browser-based MCP servers (e.g., @playwright/mcp) keep a full Chromium resident after their first tool call, hundreds of MB that never get released. Opt in to automatic recycling and the server is torn down after the idle/lifetime limit, then restarted transparently the next time one of its tools is called (its tools stay registered the whole time):

mcp_servers:
  playwright:
    command: "npx"
    args: ["-y", "@playwright/mcp@latest", "--headless"]
    idle_timeout_seconds: 900  # recycle after 15 min without a tool call
    max_lifetime_seconds: 86400  # and at least once a day regardless

Full Configuration Example

mcp_servers:
  github:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "***"
    tools:
      include: [create_issue, list_issues, search_code]
      prompts: false
  stripe:
    url: "https://mcp.stripe.com"
    headers:
      Authorization: "Bearer ***"
    tools:
      exclude: [delete_customer]
      resources: false
  legacy:
    url: "https://mcp.legacy.internal"
    enabled: false

Sources & References

This page was researched from 1 independent source, combined and verified for completeness.

Newsletter

The #1 AI Newsletter

The most important ai updates, guides, and fixes — one weekly email.

No spam, unsubscribe anytime. Privacy policy

Other Hermes Agent integrations