Initial skills documentation — 25 categories, all SKILL.md + references + scripts

This commit is contained in:
root
2026-07-15 17:42:20 -04:00
commit 1b4fcd4427
976 changed files with 188344 additions and 0 deletions
@@ -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.