Hermes Agent: Google Vertex AI & Agent-to-Agent (A2A) Setup Guide
This guide documents how to set up Hermes Agent with Google Vertex AI models using Application Default Credentials (ADC), and how to configure and test A2A (Agent-to-Agent) communication in every direction Hermes supports — fully scoped to a project directory (e.g., /path/to/project).
1. Overview & Architecture
- Hermes Agent: Open-source AI agent framework by Nous Research with multi-platform messaging, tool calling, and long-term memory.
- LLM Provider: Google Cloud Vertex AI (Gemini 3.x / 2.5 models) with OAuth2 authentication via
gcloudADC. - Inter-Agent Protocol: A2A (Agent-to-Agent) v1.0, an open specification stewarded by the Linux Foundation. Hermes implements both directions of the protocol simultaneously from a single running gateway process:
- Inbound (server): Exposes Hermes as a JSON-RPC 2.0 endpoint with an Agent Card at
GET /.well-known/agent-card.json. - Outbound (client): Lets the Hermes agent itself discover and call other A2A-compliant agents mid-conversation via tools (
a2a_discover,a2a_call,a2a_list,a2a_history,a2a_orchestrate).
- Inbound (server): Exposes Hermes as a JSON-RPC 2.0 endpoint with an Agent Card at
Directory Isolation & Scoping (HERMES_HOME)
Hermes supports the HERMES_HOME environment variable to isolate configuration, credentials, session state, and memory. In this project:
/path/to/project/
├── .gitignore # Ignores .hermes/ and .envrc
├── .envrc # direnv auto-export (HERMES_HOME=$(pwd)/.hermes)
├── run-hermes.sh # Executable helper launcher script
├── AGENTS.md # Project AGENTS.md
├── docs/
│ └── hermes-vertex-a2a-setup.md # This documentation
└── .hermes/ # Project-scoped Hermes home directory
├── config.yaml # Project-specific Hermes configuration
├── .env # Project environment variables
├── SOUL.md # Agent persona and identity
├── sessions/ # Chat and gateway session logs
├── logs/ # Gateway and runtime logs
├── memories/ # Persistent memory files
└── skills/ # Installed and bundled skills
Trade-offs vs. the default global ~/.hermes/:
| Property | Project-Scoped (HERMES_HOME="$(pwd)/.hermes") |
Global (~/.hermes/) |
|---|---|---|
| Isolation | High — separate config, memory, sessions, logs per project. | Low — shared across all CLI runs and projects. |
| Reproducibility | High — configuration lives with the project repo. | Low — changes on one project affect all projects. |
| Ease of Use | Requires export HERMES_HOME=... or ./run-hermes.sh. |
Just run hermes from anywhere. |
| Secrets Safety | Easy to isolate per project; keep .hermes/ gitignored. |
Shared keys in ~/.hermes/.env. |
2. The A2A Scenarios This Guide Covers
Hermes' A2A support is bidirectional and works with any A2A v1.0-compliant peer, not just other Hermes instances. Below are the distinct roles Hermes can play, each with its own value proposition, all verified working in this setup.
| # | Scenario | Value Proposition |
|---|---|---|
| A | Hermes as an A2A server (inbound), called by an external A2A client (a2acli) |
Expose your Hermes agent — with its full toolset, memory, and Vertex-backed reasoning — as a standard, interoperable service any A2A tool or framework can call, without writing custom integration code. |
| B | Hermes as an A2A client (outbound), calling another A2A agent (a mock server via a2acli serve --echo) |
Let Hermes delegate a task mid-conversation to a specialist agent (research, coding, a different model, a different vendor's agent) and use the reply as part of its own answer — composing multiple agents into one workflow. |
| C | Hermes calling itself over A2A (self-loop) | Useful for testing your own Agent Card and inbound behavior exactly as a remote peer would see it, without needing a second agent. Also the basis for patterns like "spin off a sub-task to my own agent identity in a fresh context." |
| D | Fire-and-forget background gateway | Run Hermes as a long-lived background process (or eventually an OS service) that keeps listening for both normal chat and A2A requests without you having to keep a terminal open. |
| E | Persistent OS service (macOS launchd) | Move beyond a background shell job to a properly supervised service that survives terminal closure, crashes, and (optionally) reboots/login — the standard way to run an "always-on" agent. |
| F | Remote A2A access (not configured here) | Documented for completeness: with a bearer token and A2A_HOST=0.0.0.0, other machines — not just localhost — could call this Hermes instance over the network. |
3. Prerequisites & GCP Configuration
- Google Cloud Project:
YOUR_GOOGLE_CLOUD_PROJECT - Vertex AI API: Ensure
aiplatform.googleapis.comis enabled:gcloud services enable aiplatform.googleapis.com --project=YOUR_GOOGLE_CLOUD_PROJECT - Application Default Credentials (ADC): Authenticate with
gcloud:
This createsgcloud auth application-default login~/.config/gcloud/application_default_credentials.json.
4. Installation & Project Scoping Setup
A. Installing Hermes Agent
If Hermes Agent CLI is not already installed on your machine:
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
source ~/.zshrc
B. Project Helper Script (run-hermes.sh)
Use the helper launcher script run-hermes.sh to ensure HERMES_HOME is always pointed at the project folder:
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export HERMES_HOME="${DIR}/.hermes"
export PATH="${HOME}/.local/bin:${PATH}"
exec hermes "$@"
Ensure it has execute permissions:
chmod +x run-hermes.sh
C. Install a2acli (external A2A client, used in Scenario A)
a2acli is a standalone, A2A Specification v1.0-compliant command-line client — not part of Hermes — used here to prove Hermes' inbound A2A server works with any conformant external tool, not just other Hermes instances.
brew tap ghchinoy/tap
brew install a2acli
a2acli version # verify install
5. Google Vertex AI Configuration
Edit .hermes/config.yaml to specify Vertex AI as the provider, your GCP project, and region:
model:
default: "google/gemini-3.6-flash"
provider: "vertex"
vertex:
project_id: "YOUR_GOOGLE_CLOUD_PROJECT"
region: "global"
Supported Gemini Models on Vertex AI
| Model Name | Model Slug / ID | Best Used For |
|---|---|---|
| Gemini 3.6 Flash | google/gemini-3.6-flash |
Fast, high-capacity default chat model (verified working) |
| Gemini 3.5 Flash Lite | google/gemini-3.5-flash-lite |
Ultra-fast, lightweight tasks (verified working) |
| Gemini 3.1 Flash Lite Preview | google/gemini-3.1-flash-lite-preview |
Documented fallback if newer aliases are unavailable in your region/project |
| Gemini 3 Flash Preview | google/gemini-3-flash-preview |
Documented fallback |
| Gemini 3 Pro Preview | google/gemini-3-pro-preview |
High-reasoning tasks |
Note on Region: Set
region: "global"inconfig.yamlwhen using Gemini 3.x models — regional endpoints (us-central1, etc.) may 404 on 3.x preview models.
Note on ADC credential type: Hermes' Vertex adapter expects a service-account JSON by default and only falls back to a plain OAuth user-credentials file (what
gcloud auth application-default loginactually produces) after a failed parse. This project's setup required a small, local patch toagent/vertex_adapter.pyto trygoogle.auth.load_credentials_from_file()as a fallback whenservice_account.Credentials.from_service_account_file()raises — this is what let plain ADC (not a service account key) work end-to-end. If you update Hermes and lose this behavior, either re-apply that fallback or use a service-account JSON viaVERTEX_CREDENTIALS_PATHinstead.
6. A2A (Agent-to-Agent) Configuration
A. Enable Inbound A2A Gateway
In .hermes/config.yaml, configure the gateway to serve A2A on port 9900:
gateway:
platforms:
a2a:
enabled: true
extra:
port: 9900
B. Enable the A2A Plugin
The A2A platform is a plugin and must be explicitly enabled (separately from the gateway.platforms.a2a.enabled flag above, which only controls whether the gateway starts it):
./run-hermes.sh plugins enable a2a-platform
Verify:
./run-hermes.sh plugins list # a2a-platform should show "enabled"
C. Enable the Outbound a2a Toolset
In .hermes/config.yaml, add a2a to the CLI platform toolsets (the a2a toolset is off by default even when the plugin is enabled):
platform_toolsets:
cli:
- hermes-cli
- a2a
⚠️ Known limitation: outbound
a2atools do not load in standalonehermes chatHermes lazy-loads bundled platform plugins (A2A, Telegram, Discord, etc.) to keep plain
hermes chatstartup fast — the platform's Python module is only imported the first time something asks the internalplatform_registryfor it by name. The gateway process does this automatically at startup (because it has to start the A2A server). A standalonehermes chat/hermes --tuiinvocation, however, never asks for thea2aplatform, so:
_get_platform_tools()(config resolution) correctly reportsa2aas enabled.validate_toolset("a2a")(the actual gate used to load tool schemas) still returnsFalse, because the platform module — and therefore thea2a_call/a2a_discover/etc. tool registrations — was never imported.Symptom:
hermes chat -t a2a -q "..."(or any standalone chat/TUI session) printsWarning: Unknown toolsets: a2aand the model has no reala2a_*tools, even though config andhermes plugins listboth look correct.Reliable workaround (documented here, verified working): Send your request through the running gateway's live session instead of a standalone CLI invocation — see Scenario B and C below. The gateway process resolves the A2A platform at startup, so
a2a_discover/a2a_call/etc. are genuinely available to whatever agent session handles a gateway-delivered message (A2A inbound task, Telegram message, etc.).If you need outbound A2A tools inside a plain
hermes chatsession specifically, this is a gap worth tracking upstream; no supported CLI flag currently forces early platform resolution outside the gateway path.
7. Scenario Walkthroughs & Verification
All scenarios below were executed and verified against this exact setup.
Start the gateway (required for Scenarios A, B, C)
./run-hermes.sh gateway run &
Check it's up:
./run-hermes.sh gateway status
curl -s http://127.0.0.1:9900/.well-known/agent-card.json
Scenario A — Hermes as an A2A server, called by a2acli (external client)
Value proposition: Prove Hermes is a standards-compliant, interoperable A2A service — callable by any conformant client, not just other Hermes agents.
1. Discover the Agent Card:
a2acli discover --service-url http://127.0.0.1:9900 --output text
Confirmed output includes agent name (hermes-agent.local), streaming capability, and the full list of advertised skills/toolsets.
2. Send a message and wait for the result:
a2acli send "What is 17 * 23? Reply with just the number." \
--service-url http://127.0.0.1:9900 --output json --wait
Confirmed output:
{"id":"task-e4aefd89eaf749a6", ... "state":"TASK_STATE_COMPLETED", ... "text":"391" ...}
(17 * 23 = 391 — correct, answered by the Vertex-backed Gemini model, delivered via raw A2A JSON-RPC.)
Scenario B — Hermes as an A2A client, calling another agent
Value proposition: Have Hermes delegate part of a task to a separate agent — a specialist, a different framework, or just an isolated echo/test service — mid-conversation, then use the result.
1. Stand up a target agent to call. For this test we used a2acli's built-in mock echo server (any A2A-compliant agent works — a second Hermes, a LangChain agent, etc.):
a2acli serve --echo --port 9001 --transport jsonrpc &
(--transport jsonrpc matters — Hermes' outbound a2a_call expects a JSON-RPC-bound peer; the default HTTP+JSON echo mode is not compatible with Hermes' outbound client.)
2. Ask the running Hermes gateway session to call it (see the limitation note in §6 — this must go through the gateway, not a standalone hermes chat):
curl -s -X POST http://127.0.0.1:9900/ \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "SendMessage",
"params": { "message": { "messageId": "m2", "role": "ROLE_USER",
"parts": [{"text": "Use your a2a_call tool to send the message ping-relay to the agent at http://127.0.0.1:9001, then reply with exactly what it returned."}]
}}
}'
Confirmed output: Hermes' reply was `ping-relay` — the exact text the mock agent echoed back, proving Hermes genuinely invoked its outbound a2a_call tool against a separate live A2A peer.
3. Discovery also works outbound:
curl -s -X POST http://127.0.0.1:9900/ \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0", "id": 2, "method": "SendMessage",
"params": { "message": { "messageId": "m4", "role": "ROLE_USER",
"parts": [{"text": "Call a2a_discover on http://127.0.0.1:9001 right now. Reply with ONLY the agent name field you get back, nothing else."}]
}}
}'
Confirmed output: a2acli-mock-agent — the real name field from the target agent's card, fetched via the genuine a2a_discover tool.
Scenario C — Hermes calling itself over A2A
Value proposition: Validate your own Agent Card and inbound handling exactly as an external peer would experience them, using nothing but the one running instance.
Since the gateway session already has both the inbound server and outbound tools loaded, this is the same pattern as Scenario B, pointed at Hermes' own address:
curl -s -X POST http://127.0.0.1:9900/ \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0", "id": 3, "method": "SendMessage",
"params": { "message": { "messageId": "m5", "role": "ROLE_USER",
"parts": [{"text": "Use a2a_discover on http://127.0.0.1:9000 and tell me your own agent name as it appears on your own Agent Card."}]
}}
}'
Anti-loop safety:
A2A_MAX_PINGPONG_TURNS(default5, max20) caps per-context back-and-forth turns specifically to prevent two agents (or one agent calling itself) from looping forever. No action needed for a single self-discover/self-call, but be aware of this cap if scripting longer self-referential exchanges.
Scenario D — Fire-and-forget background process (test-level, done here)
Value proposition: Keep the agent listening for chat and A2A requests without occupying a foreground terminal — useful for iterative testing within a single working session.
./run-hermes.sh gateway run > .hermes/logs/gateway.log 2>&1 &
This is what was used throughout this guide's verification steps. It is not persistent across terminal/session restarts or reboots — see Scenario E for that.
Scenario E — Persistent OS service via macOS launchd (documented, not enabled)
Value proposition: Move from an ad hoc background shell job to a properly supervised, restart-on-crash, (optionally) start-on-login service — the standard way to run an always-on agent on macOS, without a terminal ever being open.
Hermes has first-class support for this via launchd on macOS (and systemd on Linux). This was not enabled for this project (we used the simpler background-job approach in Scenario D for testing), but it is fully supported and scoped correctly even for a non-default HERMES_HOME like this project's:
./run-hermes.sh gateway install --start-now --start-on-login
This registers a launchd plist under ~/Library/LaunchAgents/, with a service label automatically scoped to this project's HERMES_HOME (Hermes derives a unique suffix from the HERMES_HOME path when it isn't a named profile, so multiple project-scoped Hermes instances on the same machine don't collide).
Useful related commands once installed:
./run-hermes.sh gateway status # check if the service is running
./run-hermes.sh gateway stop # stop the service
./run-hermes.sh gateway uninstall # remove the launchd service entirely
If you want the gateway to survive terminal closure and reboots but haven't set this up yet, this is the recommended next step — the background
&job used in this guide's testing does not survive a reboot or a closed terminal session cleanly.
Scenario F — Remote A2A access (not configured, documented for completeness)
Value proposition: Allow A2A clients on other machines — not just 127.0.0.1 — to call this Hermes instance, e.g. a teammate's machine, a cloud-hosted orchestrator, or a CI pipeline.
Current state: Not configured. No bearer token is set, so per Hermes' security model the server is bound to 127.0.0.1 only and will refuse to widen even if you set A2A_HOST without also setting a token.
To enable, add to .hermes/.env:
# Per-peer tokens (preferred) — each remote agent gets its own credential:
A2A_PEER_TOKENS="alice:tok1,bob:tok2"
# OR a single shared token:
A2A_BEARER_TOKEN="some-long-random-token"
# Only after a token is set, widen the bind host:
A2A_HOST=0.0.0.0
Then restart the gateway. Other security-relevant knobs (rate limiting, trusted-peer allow-lists, push-notification HMAC secrets) are listed in README.md/plugin.yaml inside Hermes' plugins/platforms/a2a/ directory and summarized in the original A2A docs page.
8. Deployment Topologies & Hermes Best Practices
Hermes supports a spectrum of deployment topologies ranging from zero-isolation local experimentation to fully sandboxed, multi-agent production architectures.
Deployment Topology Spectrum
[1. Global Local] ──> [2. Scoped Home / Profiles] ──> [3. Sandboxed Execution] ──> [4. Fully Containerized] ──> [5. Isolated Worker VM]
(Simple dev machine) (Isolated per project) (Docker terminal backend) (Hermes in s6 Container) (Remote gateway + SSH)
-
Global Local Setup (
~/.hermes/):- Use Case: Personal single-agent development on a local machine.
- Isolation: None. Hermes runs directly as your local user with full user-account filesystem and shell permissions.
-
Project-Scoped Home (
HERMES_HOME) or First-Class Profiles (hermes profile create): (This project's setup)- Use Case: Project-specific agents, multi-agent experimentation, or git-isolated repository workflows.
- Native Profiles: Running
hermes profile create codercreates~/.hermes/profiles/coder/and generates a nativecoderalias andlaunchdservice slot. - Isolation: Config, memory, session history, and skills are isolated; terminal commands still run as the local OS user unless sandboxing is enabled.
-
Sandboxed Command Execution (
terminal.backend: docker/modal/daytona/vercel_sandbox):- Use Case: Production gateway deployments, untrusted input handling, or multi-tenant A2A endpoints.
- Mechanism: Hermes runs on the host (or gateway), but every terminal command executed by the agent runs inside a single, persistent Docker sandbox container (
--cap-drop ALL,no-new-privileges,pids-limit 256). - Best Practice: The security documentation explicitly recommends container backends for production gateways to eliminate dangerous command approval prompts entirely.
-
Fully Containerized Hermes (
nousresearch/hermes-agentimage):- Use Case: Cloud servers, VPS deployments (Hetzner, AWS, GCP VM), or Kubernetes clusters.
- Mechanism: Hermes itself runs inside Docker supervised by
s6-overlay(PID 1). All profiles, gateways, and the web dashboard (:9119) run supervised inside one container with automatic restart-on-crash behavior.
-
Isolated Worker VM / Network Boundary (
terminal.backend: ssh):- Use Case: High-security enterprise environments or multi-agent testbeds.
- Mechanism: The Hermes gateway process (handling messaging, LLM inference, memory, and A2A connections) runs on one machine, while command execution is routed over SSH (
terminal.backend: ssh) to a separate worker VM or disposable instance.
Summary: Where This Setup Sits & Recommended Next Steps
- Current Setup: Level 2 (Project-Scoped
HERMES_HOME, Vertex AI ADC, local terminal execution, background gateway job). - Recommended Next Step for Local Persistence: Run
run-hermes.sh gateway install --start-now --start-on-loginto convert the ad hoc background job into an OS-supervisedlaunchdservice (Level 2 + Level 5 service layer). - Recommended Next Step for Security Hardening: If exposing A2A to remote peers or untrusted inputs, set
terminal.backend: dockerin.hermes/config.yamlto isolate tool-call execution inside a Docker container sandbox.
9. Troubleshooting & FAQ
- Issue: "Vertex AI credentials could not be resolved"
- Cause:
GOOGLE_APPLICATION_CREDENTIALS/VERTEX_CREDENTIALS_PATHinvalid, or ADC not logged in. - Fix: Run
gcloud auth application-default loginand ensurevertex.project_idis set in.hermes/config.yaml.
- Cause:
- Issue: "Service account info was not in the expected format, missing fields token_uri, client_email"
- Cause: Hermes tried to parse a plain ADC user-credentials file as a service-account key. See the note in §5 about the local
vertex_adapter.pyfallback patch. - Fix: Confirm the fallback to
google.auth.load_credentials_from_file()is present inagent/vertex_adapter.py, or switch to a real service-account JSON viaVERTEX_CREDENTIALS_PATH.
- Cause: Hermes tried to parse a plain ADC user-credentials file as a service-account key. See the note in §5 about the local
- Issue: 404 Error on Gemini 3.x Models
- Cause: Requested on a regional Vertex endpoint where 3.x preview models aren't deployed.
- Fix: Set
region: "global"in.hermes/config.yaml.
- Issue:
403 Permission Deniedon Vertex AI- Cause: GCP identity lacks Vertex AI access.
- Fix: Grant
roles/aiplatform.useronYOUR_GOOGLE_CLOUD_PROJECTto the ADC identity.
- Issue:
Warning: Unknown toolsets: a2a/ outbounda2a_callnot available inhermes chat- Cause: Known Hermes deferred-plugin-loading gap — see the boxed note in §6.
- Fix: Route the request through the running gateway session (Scenario B/C) instead of standalone
hermes chat.
- Issue:
a2acli serve --echotarget rejects Hermes'a2a_call- Cause:
a2acli serve --echodefaults to HTTP+JSON transport; Hermes' outbound client expects JSON-RPC. - Fix: Start the mock server with
--transport jsonrpcexplicitly.
- Cause:
10. External References & Resources
Hermes Agent & Nous Research
- Hermes Agent Official Website & Downloads
- Hermes Agent User Guide: A2A (Agent-to-Agent) Protocol
- Hermes Agent Guide: Google Vertex AI Integration
- Hermes Agent Guide: Docker Deployment
- Hermes Agent Security & Sandboxing Model
- Hermes Agent GitHub Repository
A2A (Agent-to-Agent) Specification & Tools
- A2A Protocol Official Specification v1.0
a2acliRepository (Standalone A2A Command-Line Client)- Linux Foundation A2A Project