Initial skills documentation — 25 categories, all SKILL.md + references + scripts
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,344 @@
|
||||
# Native MCP Client
|
||||
|
||||
Hermes Agent has a built-in MCP client that connects to MCP servers at startup, discovers their tools, and makes them available as first-class tools the agent can call directly. No bridge CLI needed -- tools from MCP servers appear alongside built-in tools like `terminal`, `read_file`, etc.
|
||||
|
||||
## When to Use
|
||||
|
||||
Use this whenever you want to:
|
||||
- Connect to MCP servers and use their tools from within Hermes Agent
|
||||
- Add external capabilities (filesystem access, GitHub, databases, APIs) via MCP
|
||||
- Run local stdio-based MCP servers (npx, uvx, or any command)
|
||||
- Connect to remote HTTP/StreamableHTTP MCP servers
|
||||
- Have MCP tools auto-discovered and available in every conversation
|
||||
|
||||
For ad-hoc, one-off MCP tool calls from the terminal without configuring anything, see the `mcporter` skill instead.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **mcp Python package** -- optional dependency; install with `pip install mcp`. If not installed, MCP support is silently disabled.
|
||||
- **Node.js** -- required for `npx`-based MCP servers (most community servers)
|
||||
- **uv** -- required for `uvx`-based MCP servers (Python-based servers)
|
||||
|
||||
Install the MCP SDK:
|
||||
|
||||
```bash
|
||||
pip install mcp
|
||||
# or, if using uv:
|
||||
uv pip install mcp
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Add MCP servers to `~/.hermes/config.yaml` under the `mcp_servers` key:
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
time:
|
||||
command: "uvx"
|
||||
args: ["mcp-server-time"]
|
||||
```
|
||||
|
||||
Restart Hermes Agent. On startup it will:
|
||||
1. Connect to the server
|
||||
2. Discover available tools
|
||||
3. Register them with the prefix `mcp_time_*`
|
||||
4. Inject them into all platform toolsets
|
||||
|
||||
You can then use the tools naturally -- just ask the agent to get the current time.
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
Each entry under `mcp_servers` is a server name mapped to its config. There are two transport types: **stdio** (command-based) and **HTTP** (url-based).
|
||||
|
||||
### Stdio Transport (command + args)
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
server_name:
|
||||
command: "npx" # (required) executable to run
|
||||
args: ["-y", "pkg-name"] # (optional) command arguments, default: []
|
||||
env: # (optional) environment variables for the subprocess
|
||||
SOME_API_KEY: "value"
|
||||
timeout: 120 # (optional) per-tool-call timeout in seconds, default: 120
|
||||
connect_timeout: 60 # (optional) initial connection timeout in seconds, default: 60
|
||||
```
|
||||
|
||||
### HTTP Transport (url)
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
server_name:
|
||||
url: "https://my-server.example.com/mcp" # (required) server URL
|
||||
headers: # (optional) HTTP headers
|
||||
Authorization: "Bearer sk-..."
|
||||
timeout: 180 # (optional) per-tool-call timeout in seconds, default: 120
|
||||
connect_timeout: 60 # (optional) initial connection timeout in seconds, default: 60
|
||||
```
|
||||
|
||||
### All Config Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|-------------------|--------|---------|---------------------------------------------------|
|
||||
| `command` | string | -- | Executable to run (stdio transport, required) |
|
||||
| `args` | list | `[]` | Arguments passed to the command |
|
||||
| `env` | dict | `{}` | Extra environment variables for the subprocess |
|
||||
| `url` | string | -- | Server URL (HTTP transport, required) |
|
||||
| `headers` | dict | `{}` | HTTP headers sent with every request |
|
||||
| `timeout` | int | `120` | Per-tool-call timeout in seconds |
|
||||
| `connect_timeout` | int | `60` | Timeout for initial connection and discovery |
|
||||
|
||||
Note: A server config must have either `command` (stdio) or `url` (HTTP), not both.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Startup Discovery
|
||||
|
||||
When Hermes Agent starts, `discover_mcp_tools()` is called during tool initialization:
|
||||
|
||||
1. Reads `mcp_servers` from `~/.hermes/config.yaml`
|
||||
2. For each server, spawns a connection in a dedicated background event loop
|
||||
3. Initializes the MCP session and calls `list_tools()` to discover available tools
|
||||
4. Registers each tool in the Hermes tool registry
|
||||
|
||||
### Tool Naming Convention
|
||||
|
||||
MCP tools are registered with the naming pattern:
|
||||
|
||||
```
|
||||
mcp_{server_name}_{tool_name}
|
||||
```
|
||||
|
||||
Hyphens and dots in names are replaced with underscores for LLM API compatibility.
|
||||
|
||||
Examples:
|
||||
- Server `filesystem`, tool `read_file` → `mcp_filesystem_read_file`
|
||||
- Server `github`, tool `list-issues` → `mcp_github_list_issues`
|
||||
- Server `my-api`, tool `fetch.data` → `mcp_my_api_fetch_data`
|
||||
|
||||
### Auto-Injection
|
||||
|
||||
After discovery, MCP tools are automatically injected into all `hermes-*` platform toolsets (CLI, Discord, Telegram, etc.). This means MCP tools are available in every conversation without any additional configuration.
|
||||
|
||||
### Connection Lifecycle
|
||||
|
||||
- Each server runs as a long-lived asyncio Task in a background daemon thread
|
||||
- Connections persist for the lifetime of the agent process
|
||||
- If a connection drops, automatic reconnection with exponential backoff kicks in (up to 5 retries, max 60s backoff)
|
||||
- On agent shutdown, all connections are gracefully closed
|
||||
|
||||
### Idempotency
|
||||
|
||||
`discover_mcp_tools()` is idempotent -- calling it multiple times only connects to servers that aren't already connected. Failed servers are retried on subsequent calls.
|
||||
|
||||
## Transport Types
|
||||
|
||||
### Stdio Transport
|
||||
|
||||
The most common transport. Hermes launches the MCP server as a subprocess and communicates over stdin/stdout.
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
filesystem:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
|
||||
```
|
||||
|
||||
The subprocess inherits a **filtered** environment (see Security section below) plus any variables you specify in `env`.
|
||||
|
||||
### HTTP / StreamableHTTP Transport
|
||||
|
||||
For remote or shared MCP servers. Requires the `mcp` package to include HTTP client support (`mcp.client.streamable_http`).
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
remote_api:
|
||||
url: "https://mcp.example.com/mcp"
|
||||
headers:
|
||||
Authorization: "Bearer sk-..."
|
||||
```
|
||||
|
||||
If HTTP support is not available in your installed `mcp` version, the server will fail with an ImportError and other servers will continue normally.
|
||||
|
||||
## Security
|
||||
|
||||
### Environment Variable Filtering
|
||||
|
||||
For stdio servers, Hermes does NOT pass your full shell environment to MCP subprocesses. Only safe baseline variables are inherited:
|
||||
|
||||
- `PATH`, `HOME`, `USER`, `LANG`, `LC_ALL`, `TERM`, `SHELL`, `TMPDIR`
|
||||
- Any `XDG_*` variables
|
||||
|
||||
All other environment variables (API keys, tokens, secrets) are excluded unless you explicitly add them via the `env` config key. This prevents accidental credential leakage to untrusted MCP servers.
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
# Only this token is passed to the subprocess
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..."
|
||||
```
|
||||
|
||||
### Credential Stripping in Error Messages
|
||||
|
||||
If an MCP tool call fails, any credential-like patterns in the error message are automatically redacted before being shown to the LLM. This covers:
|
||||
|
||||
- GitHub PATs (`ghp_...`)
|
||||
- OpenAI-style keys (`sk-...`)
|
||||
- Bearer tokens
|
||||
- Generic `token=`, `key=`, `API_KEY=`, `password=`, `secret=` patterns
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "MCP SDK not available -- skipping MCP tool discovery"
|
||||
|
||||
The `mcp` Python package is not installed. Install it:
|
||||
|
||||
```bash
|
||||
pip install mcp
|
||||
```
|
||||
|
||||
### "No MCP servers configured"
|
||||
|
||||
No `mcp_servers` key in `~/.hermes/config.yaml`, or it's empty. Add at least one server.
|
||||
|
||||
### "Failed to connect to MCP server 'X'"
|
||||
|
||||
Common causes:
|
||||
- **Command not found**: The `command` binary isn't on PATH. Ensure `npx`, `uvx`, or the relevant command is installed.
|
||||
- **Package not found**: For npx servers, the npm package may not exist or may need `-y` in args to auto-install.
|
||||
- **Timeout**: The server took too long to start. Increase `connect_timeout`.
|
||||
- **Port conflict**: For HTTP servers, the URL may be unreachable.
|
||||
|
||||
### "MCP server 'X' requires HTTP transport but mcp.client.streamable_http is not available"
|
||||
|
||||
Your `mcp` package version doesn't include HTTP client support. Upgrade:
|
||||
|
||||
```bash
|
||||
pip install --upgrade mcp
|
||||
```
|
||||
|
||||
### Tools not appearing
|
||||
|
||||
- Check that the server is listed under `mcp_servers` (not `mcp` or `servers`)
|
||||
- Ensure the YAML indentation is correct
|
||||
- Look at Hermes Agent startup logs for connection messages
|
||||
- Tool names are prefixed with `mcp_{server}_{tool}` -- look for that pattern
|
||||
|
||||
### Connection keeps dropping
|
||||
|
||||
The client retries up to 5 times with exponential backoff (1s, 2s, 4s, 8s, 16s, capped at 60s). If the server is fundamentally unreachable, it gives up after 5 attempts. Check the server process and network connectivity.
|
||||
|
||||
## Examples
|
||||
|
||||
### Time Server (uvx)
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
time:
|
||||
command: "uvx"
|
||||
args: ["mcp-server-time"]
|
||||
```
|
||||
|
||||
Registers tools like `mcp_time_get_current_time`.
|
||||
|
||||
### Filesystem Server (npx)
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
filesystem:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"]
|
||||
timeout: 30
|
||||
```
|
||||
|
||||
Registers tools like `mcp_filesystem_read_file`, `mcp_filesystem_write_file`, `mcp_filesystem_list_directory`.
|
||||
|
||||
### GitHub Server with Authentication
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_xxxxxxxxxxxxxxxxxxxx"
|
||||
timeout: 60
|
||||
```
|
||||
|
||||
Registers tools like `mcp_github_list_issues`, `mcp_github_create_pull_request`, etc.
|
||||
|
||||
### Remote HTTP Server
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
company_api:
|
||||
url: "https://mcp.mycompany.com/v1/mcp"
|
||||
headers:
|
||||
Authorization: "Bearer sk-xxxxxxxxxxxxxxxxxxxx"
|
||||
X-Team-Id: "engineering"
|
||||
timeout: 180
|
||||
connect_timeout: 30
|
||||
```
|
||||
|
||||
### Multiple Servers
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
time:
|
||||
command: "uvx"
|
||||
args: ["mcp-server-time"]
|
||||
|
||||
filesystem:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
|
||||
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_xxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
company_api:
|
||||
url: "https://mcp.internal.company.com/mcp"
|
||||
headers:
|
||||
Authorization: "Bearer sk-xxxxxxxxxxxxxxxxxxxx"
|
||||
timeout: 300
|
||||
```
|
||||
|
||||
All tools from all servers are registered and available simultaneously. Each server's tools are prefixed with its name to avoid collisions.
|
||||
|
||||
## Sampling (Server-Initiated LLM Requests)
|
||||
|
||||
Hermes supports MCP's `sampling/createMessage` capability — MCP servers can request LLM completions through the agent during tool execution. This enables agent-in-the-loop workflows (data analysis, content generation, decision-making).
|
||||
|
||||
Sampling is **enabled by default**. Configure per server:
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
my_server:
|
||||
command: "npx"
|
||||
args: ["-y", "my-mcp-server"]
|
||||
sampling:
|
||||
enabled: true # default: true
|
||||
model: "gemini-3-flash" # model override (optional)
|
||||
max_tokens_cap: 4096 # max tokens per request
|
||||
timeout: 30 # LLM call timeout (seconds)
|
||||
max_rpm: 10 # max requests per minute
|
||||
allowed_models: [] # model whitelist (empty = all)
|
||||
max_tool_rounds: 5 # tool loop limit (0 = disable)
|
||||
log_level: "info" # audit verbosity
|
||||
```
|
||||
|
||||
Servers can also include `tools` in sampling requests for multi-turn tool-augmented workflows. The `max_tool_rounds` config prevents infinite tool loops. Per-server audit metrics (requests, errors, tokens, tool use count) are tracked via `get_mcp_status()`.
|
||||
|
||||
Disable sampling for untrusted servers with `sampling: { enabled: false }`.
|
||||
|
||||
## Notes
|
||||
|
||||
- MCP tools are called synchronously from the agent's perspective but run asynchronously on a dedicated background event loop
|
||||
- Tool results are returned as JSON with either `{"result": "..."}` or `{"error": "..."}`
|
||||
- The native MCP client is independent of `mcporter` -- you can use both simultaneously
|
||||
- Server connections are persistent and shared across all conversations in the same agent process
|
||||
- Adding or removing servers requires restarting the agent (no hot-reload currently)
|
||||
@@ -0,0 +1,210 @@
|
||||
# Webhook Subscriptions
|
||||
|
||||
Create dynamic webhook subscriptions so external services (GitHub, GitLab, Stripe, CI/CD, IoT sensors, monitoring tools) can trigger Hermes agent runs by POSTing events to a URL.
|
||||
|
||||
## Setup (Required First)
|
||||
|
||||
The webhook platform must be enabled before subscriptions can be created. Check with:
|
||||
```bash
|
||||
hermes webhook list
|
||||
```
|
||||
|
||||
If it says "Webhook platform is not enabled", set it up:
|
||||
|
||||
### Option 1: Setup wizard
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
Follow the prompts to enable webhooks, set the port, and set a global HMAC secret.
|
||||
|
||||
### Option 2: Manual config
|
||||
Add to `~/.hermes/config.yaml`:
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
host: "0.0.0.0"
|
||||
port: 8644
|
||||
secret: "generate-a-strong-secret-here"
|
||||
```
|
||||
|
||||
### Option 3: Environment variables
|
||||
Add to `${HERMES_HOME:-~/.hermes}/.env`:
|
||||
```bash
|
||||
WEBHOOK_ENABLED=true
|
||||
WEBHOOK_PORT=8644
|
||||
WEBHOOK_SECRET=generate-a-strong-secret-here
|
||||
```
|
||||
|
||||
After configuration, start (or restart) the gateway:
|
||||
```bash
|
||||
hermes gateway run
|
||||
# Or if using systemd:
|
||||
systemctl --user restart hermes-gateway
|
||||
```
|
||||
|
||||
Verify it's running:
|
||||
```bash
|
||||
curl http://localhost:8644/health
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
All management is via the `hermes webhook` CLI command:
|
||||
|
||||
### Create a subscription
|
||||
```bash
|
||||
hermes webhook subscribe <name> \
|
||||
--prompt "Prompt template with {payload.fields}" \
|
||||
--events "event1,event2" \
|
||||
--description "What this does" \
|
||||
--skills "skill1,skill2" \
|
||||
--deliver telegram \
|
||||
--deliver-chat-id "12345" \
|
||||
--secret "optional-custom-secret"
|
||||
```
|
||||
|
||||
Returns the webhook URL and HMAC secret. The user configures their service to POST to that URL.
|
||||
|
||||
### Filter or transform payloads before the agent runs
|
||||
|
||||
Two mechanisms narrow broad event streams (e.g. Todoist/GitHub fire on every update) so only relevant payloads wake the agent:
|
||||
|
||||
- **Declarative `filters`** (config.yaml routes only): list of conditions on payload fields, event type, or headers — operators `equals`, `not_equals`, `contains`, `exists`, `missing`, `in`, `in_file`, `regex`, with `all`/`any`/`not` grouping. Non-matching events are ignored with HTTP 200.
|
||||
- **Route scripts** (`--script` on subscribe, or `script:` on a config route): a script under `~/.hermes/scripts/` receives the payload as JSON on stdin. JSON stdout replaces the payload before prompt templating; empty stdout, `[SILENT]`, or a nonzero exit ignores the webhook. `.sh`/`.bash` run with bash, everything else with Python. Scripts cannot live outside `~/.hermes/scripts/` (path traversal is blocked).
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe todoist-hermes \
|
||||
--prompt "Task changed: {payload.content}" \
|
||||
--script "todoist-hermes-label.py" \
|
||||
--deliver telegram --deliver-chat-id "12345"
|
||||
```
|
||||
|
||||
Full filter syntax: https://hermes-agent.nousresearch.com/docs/user-guide/messaging/webhooks#payload-filters
|
||||
|
||||
### List subscriptions
|
||||
```bash
|
||||
hermes webhook list
|
||||
```
|
||||
|
||||
### Remove a subscription
|
||||
```bash
|
||||
hermes webhook remove <name>
|
||||
```
|
||||
|
||||
### Test a subscription
|
||||
```bash
|
||||
hermes webhook test <name>
|
||||
hermes webhook test <name> --payload '{"key": "value"}'
|
||||
```
|
||||
|
||||
## Prompt Templates
|
||||
|
||||
Prompts support `{dot.notation}` for accessing nested payload fields:
|
||||
|
||||
- `{issue.title}` — GitHub issue title
|
||||
- `{pull_request.user.login}` — PR author
|
||||
- `{data.object.amount}` — Stripe payment amount
|
||||
- `{sensor.temperature}` — IoT sensor reading
|
||||
|
||||
If no prompt is specified, the full JSON payload is dumped into the agent prompt.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### GitHub: new issues
|
||||
```bash
|
||||
hermes webhook subscribe github-issues \
|
||||
--events "issues" \
|
||||
--prompt "New GitHub issue #{issue.number}: {issue.title}\n\nAction: {action}\nAuthor: {issue.user.login}\nBody:\n{issue.body}\n\nPlease triage this issue." \
|
||||
--deliver telegram \
|
||||
--deliver-chat-id "-100123456789"
|
||||
```
|
||||
|
||||
Then in GitHub repo Settings → Webhooks → Add webhook:
|
||||
- Payload URL: the returned webhook_url
|
||||
- Content type: application/json
|
||||
- Secret: the returned secret
|
||||
- Events: "Issues"
|
||||
|
||||
### GitHub: PR reviews
|
||||
```bash
|
||||
hermes webhook subscribe github-prs \
|
||||
--events "pull_request" \
|
||||
--prompt "PR #{pull_request.number} {action}: {pull_request.title}\nBy: {pull_request.user.login}\nBranch: {pull_request.head.ref}\n\n{pull_request.body}" \
|
||||
--skills "github-code-review" \
|
||||
--deliver github_comment
|
||||
```
|
||||
|
||||
### Stripe: payment events
|
||||
```bash
|
||||
hermes webhook subscribe stripe-payments \
|
||||
--events "payment_intent.succeeded,payment_intent.payment_failed" \
|
||||
--prompt "Payment {data.object.status}: {data.object.amount} cents from {data.object.receipt_email}" \
|
||||
--deliver telegram \
|
||||
--deliver-chat-id "-100123456789"
|
||||
```
|
||||
|
||||
### CI/CD: build notifications
|
||||
```bash
|
||||
hermes webhook subscribe ci-builds \
|
||||
--events "pipeline" \
|
||||
--prompt "Build {object_attributes.status} on {project.name} branch {object_attributes.ref}\nCommit: {commit.message}" \
|
||||
--deliver discord \
|
||||
--deliver-chat-id "1234567890"
|
||||
```
|
||||
|
||||
### Generic monitoring alert
|
||||
```bash
|
||||
hermes webhook subscribe alerts \
|
||||
--prompt "Alert: {alert.name}\nSeverity: {alert.severity}\nMessage: {alert.message}\n\nPlease investigate and suggest remediation." \
|
||||
--deliver origin
|
||||
```
|
||||
|
||||
### Direct delivery (no agent, zero LLM cost)
|
||||
|
||||
For use cases where you just want to push a notification through to a user's chat — no reasoning, no agent loop — add `--deliver-only`. The rendered `--prompt` template becomes the literal message body and is dispatched directly to the target adapter.
|
||||
|
||||
Use this for:
|
||||
- External service push notifications (Supabase/Firebase webhooks → Telegram)
|
||||
- Monitoring alerts that should forward verbatim
|
||||
- Inter-agent pings where one agent is telling another agent's user something
|
||||
- Any webhook where an LLM round trip would be wasted effort
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe antenna-matches \
|
||||
--deliver telegram \
|
||||
--deliver-chat-id "123456789" \
|
||||
--deliver-only \
|
||||
--prompt "🎉 New match: {match.user_name} matched with you!" \
|
||||
--description "Antenna match notifications"
|
||||
```
|
||||
|
||||
The POST returns `200 OK` on successful delivery, `502` on target failure — so upstream services can retry intelligently. HMAC auth, rate limits, and idempotency still apply.
|
||||
|
||||
Requires `--deliver` to be a real target (telegram, discord, slack, github_comment, etc.) — `--deliver log` is rejected because log-only direct delivery is pointless.
|
||||
|
||||
## Security
|
||||
|
||||
- Each subscription gets an auto-generated HMAC-SHA256 secret (or provide your own with `--secret`)
|
||||
- The webhook adapter validates signatures on every incoming POST
|
||||
- Static routes from config.yaml cannot be overwritten by dynamic subscriptions
|
||||
- Subscriptions persist to `~/.hermes/webhook_subscriptions.json`
|
||||
|
||||
## How It Works
|
||||
|
||||
1. `hermes webhook subscribe` writes to `~/.hermes/webhook_subscriptions.json`
|
||||
2. The webhook adapter hot-reloads this file on each incoming request (mtime-gated, negligible overhead)
|
||||
3. When a POST arrives matching a route, the adapter formats the prompt and triggers an agent run
|
||||
4. The agent's response is delivered to the configured target (Telegram, Discord, GitHub comment, etc.)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If webhooks aren't working:
|
||||
|
||||
1. **Is the gateway running?** Check with `systemctl --user status hermes-gateway` or `ps aux | grep gateway`
|
||||
2. **Is the webhook server listening?** `curl http://localhost:8644/health` should return `{"status": "ok"}`
|
||||
3. **Check gateway logs:** `grep webhook ~/.hermes/logs/gateway.log | tail -20`
|
||||
4. **Signature mismatch?** Verify the secret in your service matches the one from `hermes webhook list`. GitHub sends `X-Hub-Signature-256`, GitLab sends `X-Gitlab-Token`.
|
||||
5. **Firewall/NAT?** The webhook URL must be reachable from the service. For local development, use a tunnel (ngrok, cloudflared).
|
||||
6. **Wrong event type?** Check `--events` filter matches what the service sends. Use `hermes webhook test <name>` to verify the route works.
|
||||
Reference in New Issue
Block a user