Hermes Agent

View as Markdown

Dial integrates with Hermes in two parts:

  1. Driving Dial so the agent can send texts, place calls, and check status. Pick one of two ways below — the CLI skill or Dial’s native MCP server.
  2. Inbound event handling so Dial events (inbound SMS, completed calls) reach Hermes without the agent having to poll.

Install

There are two ways to let Hermes drive Dial. They do the same outbound job — pick one. The CLI skill teaches the agent to run the dial CLI; the MCP server exposes Dial’s tools to Hermes natively.

First, get the dial CLI on your PATH:

$curl -fsSL https://getdial.ai/install | bash

Then drop the Hermes-specific Dial skill into your config:

$dial onboard --code <code> --agent hermes

onboard finishes verification first, then copies the Dial skill into Hermes’s config directory. The agent loads it on demand and can drive the CLI — see the CLI reference for the full surface.

To rerun the copy later (for example, after a CLI upgrade), run dial onboard --agent hermes again — the verification step is a no-op once you’re onboarded, and the skill file is overwritten in place.

Option 2 — MCP server

Hermes is an MCP client, so it can talk to Dial’s MCP server directly — no dial CLI shell-out. Register the server with the hermes mcp add command, or declare it under mcp_servers in ~/.hermes/config.yaml. The local (stdio) transport runs npx -y @getdial/cli mcp and reuses your saved CLI key; the remote transport points Hermes at https://getdial.ai/mcp and authenticates via OAuth in the browser on first connection.

$hermes mcp add dial --command npx --args -y @getdial/cli mcp

See the MCP page for the full tool list and the remote OAuth flow, and Hermes’s own MCP docs for the hermes mcp CLI, tool filtering (tools.include / tools.exclude), and the complete mcp_servers schema.

Inbound event handling

You have two ways to route Dial events into Hermes. Both rely on Dial’s listen daemon — install it first:

$dial listen install

The daemon is what owns the fan-out queue. See Listen service.

Option 1 — Webhook

Use Hermes’s built-in webhook platform. The daemon POSTs each Dial event to a Hermes subscription URL; Hermes verifies the HMAC signature, applies its --prompt template, and triggers an agent run.

1. Enable and configure the Hermes webhook platform

Turn on the webhook platform in ~/.hermes/config.yaml:

1platforms:
2 webhook:
3 enabled: true
4 extra:
5 host: "127.0.0.1"
6 port: 8644
7 secret: "generate-a-strong-secret-here"

Bind to 127.0.0.1 (not 0.0.0.0) — Dial fans out only to local loopback hosts, and binding to all interfaces would expose Hermes’s webhook port to the network.

Then create a subscription that Dial events will post to:

$hermes webhook subscribe dial \
> --events "message.received,call.ended" \
> --prompt "Dial event {type} on {to}"

Hermes auto-generates a URL and HMAC-SHA256 secret for the subscription and persists them to ~/.hermes/webhook_subscriptions.json. The --prompt template uses {dot.notation} to interpolate fields off the Dial event JSON — e.g. {type}, {from}, {to}, plus event-specific fields like {body} on message.received and {status} / {duration} on call.ended. See Hermes webhook subscriptions for the full template syntax.

2. Register the subscription with Dial

Read the URL and secret straight out of the Hermes file and hand them to dial local-target add url:

$SUBS=~/.hermes/webhook_subscriptions.json
$URL=$(jq -r '.subscriptions[] | select(.name=="dial") | .url' "$SUBS")
$SEC=$(jq -r '.subscriptions[] | select(.name=="dial") | .secret' "$SUBS")
$
$dial local-target add url "$URL" --secret "$SEC" --signature-header "X-Hermes-Signature"

No daemon restart is required — the fan-out registry updates on the fly.

3. Verify

$# Send yourself an SMS to one of your Dial numbers, then check
$dial listen status # confirm the fan-out POST went out
$hermes webhook test dial # confirm Hermes accepted the last signed POST

To remove the wiring later, run dial local-target remove <url> followed by hermes webhook remove dial.

Option 2 — CLI command target

Skip Hermes’s webhook listener entirely and spawn a fresh Hermes agent session per Dial event using a CLI command target. The daemon runs your handler with the event JSON as the final positional argument; the handler builds a prompt and launches Hermes detached. Useful when:

  • You don’t want to expose Hermes’s webhook port (even on loopback).
  • You’d rather not maintain Hermes’s per-subscription HMAC plumbing.
  • A one-shot agent run per event is enough — no need for --events filters and prompt templates.

1. Write a handler

$#!/usr/bin/env bash
$# /usr/local/bin/handle-dial-event-hermes
$event="$1"
$
$type=$(jq -r '.type' <<<"$event")
$from=$(jq -r '.from // ""' <<<"$event")
$body=$(jq -r '.body // ""' <<<"$event")
$
$prompt=$(cat <<EOF
>A Dial event just arrived. Decide what to do and act on it.
>
> type: $type
> from: $from
> body: $body
>
>raw event JSON:
>$event
>EOF
>)
$
$# Detach a fresh Hermes session and return right away.
$nohup hermes -z "$prompt" </dev/null >>~/.dial/hermes.log 2>&1 &
$disown
$exit 0

hermes -z is Hermes’s purest one-shot mode — single prompt in, final response text out on stdout, nothing else. Use hermes chat -q "$prompt" instead if you want tool calls in the transcript too.

2. Register

$chmod +x /usr/local/bin/handle-dial-event-hermes
$dial local-target add cmd /usr/local/bin/handle-dial-event-hermes

The nohup … & disown pattern keeps Hermes running after the handler exits, so a multi-minute agent run never collides with the daemon’s per-attempt --timeout. See CLI command target for the full reference, delivery semantics, and the once-retry behavior.

Use it

Either path works the same in use — just ask your Hermes agent in plain language:

Send an SMS to +14155550123 with the body “running late, ETA 10 min”.

The agent figures out the action and runs it. With the CLI skill it drives the dial CLI; with the MCP server it calls Dial’s tools directly (no CLI shell-out). See Using Dial from an agent for more examples.