I now run a headless Hermes Agent on a Mac mini in my home office. Nothing is plugged in but power and a network cable, and it answers me on Telegram from anywhere in the world. Once I had my Hermes Agent up and running, I immediately wanted more capabilities, not just the ability to chat.
If you haven’t set up your headless Hermes Agent yet, start with Part 1. Part 2 takes everything a step further by giving your headless Hermes Agent real tools to work with.
You’ll add AgentMail so your agent has its own email address, Brave Search so it can look things up on the web, and Firecrawl so it can read any page you point it at. Every step in this beginner’s guide is copy-paste. And you’ll learn the one prompting pattern that makes a local model use these tools reliably.
Before You Start Giving Your Headless Hermes Agent Tools
This step-by-step tutorial picks up where Part 1 ended: a headless Mac mini answering you on Telegram, with the agent account, the warm Ollama service, and Hermes installed. If that isn’t true yet, do Part 1 first.
You’ll add three tools: AgentMail (the agent gets its own email address), Brave Search (web search), and Firecrawl (reading web pages), then learn the one prompt shape that makes a local model use them reliably.
How you’ll work
As in Part 1, commands in gray boxes get pasted into your SSH session: from your controller’s terminal, ssh agent@YOUR-TAILSCALE-IP, enter the agent password, and you’re in. Run whoami any time to confirm; it should print agent.
Where API keys live: Each tool gives you an API key (a long secret string that proves it’s you). We’ll store every key in one protected file on the agent account, ~/.hermes/.env, and the tool configuration will reference them from there. Never paste keys into notes apps, chat messages, or your admin account. You can save these API keys in a password manager should you ever need to reference them again.
How these tools connect: MCP servers
All three connect through MCP servers: small connector programs Hermes runs automatically via npx. You declare all three in one block of the Hermes config file. One prerequisite: Node.js, installed once from your admin account:
brew install node
Back in your SSH session as agent, confirm: npx --version prints a number.
AgentMail: Give the Agent an Email Address
AgentMail provides email inboxes built specifically for AI agents. The free plan includes attachments, threads, drafts, and scheduled send.
1. Create the account and inbox
- On your controller’s browser, go to https://agentmail.to and sign up (free).
- In the AgentMail console, create an inbox. It gets an address like
yourname@agentmail.to. Write down the exact address shown in the console; you’ll need it verbatim later. - Find the API keys section of the console and create a key. Copy it.
2. Save the key, then declare the MCP server
- In your SSH session as
agent, save the API key into Hermes’s protected secrets file (replacePASTE-YOUR-KEY-HEREwith the real key, keeping the quotes):
echo 'AGENTMAIL_API_KEY="PASTE-YOUR-KEY-HERE"' >> ~/.hermes/.env
- Open the Hermes configuration file in a simple text editor inside your SSH session:
nano ~/.hermes/config.yaml
- Use the arrow keys to scroll to the very bottom of the file, then type (or paste) this block exactly, including the two-space indentation:
mcp_servers:
agentmail:
command: npx
args: ["-y", "agentmail-mcp"]
env:
AGENTMAIL_API_KEY: ${AGENTMAIL_API_KEY}
- Save and exit nano: press
Ctrl+O, thenReturnto save, thenCtrl+Xto exit. - Tell Hermes to load it, either by sending
/reload-mcpto your bot in Telegram, or by restarting the gateway:
hermes gateway restart
Important: The mcp_servers: heading appears only once in the file; the next two tools get added under this same block. The ${AGENTMAIL_API_KEY} reference pulls the key from .env, so the config never contains the secret.
3. Test it (do all three)
- From your normal personal email, send a short message to the agent’s inbox address.
- In Telegram, ask the agent:
Check your email and summarize the newest message.Confirm it reads your test email back correctly. - Then:
Reply to that email saying thanks, this is a test.Confirm the reply lands in your personal inbox (check spam the first time).
Sending cap: New agent inboxes are limited to 10 sends per day until verified.
Brave Search: Let It Look Things Up
1. Get a key
- Go to https://brave.com/search/api and sign up for the Search API (there’s a free tier).
- Create an API key in the dashboard and copy it.
2. Save the key, then add the server to the same blockSave the key into the secrets file:
- Save the key into the secrets file:
echo 'BRAVE_API_KEY="PASTE-YOUR-KEY-HERE"' >> ~/.hermes/.env
- Open the config again (
nano ~/.hermes/config.yaml) and add this entry under the existing mcp_servers: heading from Step 1.2, at the same indentation as agentmail:
brave-search:command: npxargs: ["-y", "@modelcontextprotocol/server-brave-search"]env:BRAVE_API_KEY: ${BRAVE_API_KEY}
- Save and exit (
Ctrl+O, Return,Ctrl+X), then/reload-mcpin Telegram or:
hermes gateway restart
3. Test it
- In Telegram, ask something that requires fresh information: Search the web: what happened in tech news today?
- Confirm the answer contains genuinely current results. If it comes back empty, check in the Brave dashboard that the key is active and the Search API subscription is enabled.
Firecrawl: Let It Read Web Pages
1. Get a key
- Go to https://firecrawl.dev, sign up (free tier available), and create an API key.
2. Save the key, then add the server to the same block
- Save the key into the secrets file:
echo 'FIRECRAWL_API_KEY="PASTE-YOUR-KEY-HERE"' >> ~/.hermes/.env
- Open the config (
nano ~/.hermes/config.yaml) and add this entry under the samemcp_servers: heading:
firecrawl:
command: npx
args: ["-y", "firecrawl-mcp"]
env:
FIRECRAWL_API_KEY: ${FIRECRAWL_API_KEY}
- Save and exit, then
/reload-mcpin Telegram or:
hermes gateway restart
3. Confirm all three servers are connected
Before testing each tool, check that Hermes sees all three:
hermes mcp list
You should see agentmail, brave-search, and firecrawl listed as connected. If one is missing or errored, the usual causes are indentation in the YAML block (the entries must line up exactly as shown) or a typo in the key name between .env and the ${…} reference.
4. Test it
- In Telegram:
Scrape https://example.com and tell me what it says. - Confirm it returns the page’s text rather than an error. If it errors, double-check the key and whether you’ve hit the free-tier rate limit.
How to Ask: The Prompt Shape That Works
A local model will not use tools gracefully on its own. It reaches for code instead of the right tool, leaks your instructions into content it sends, retries tasks it already finished, and reports success for calls that did nothing. One prompt shape prevents all of it:
Single task, [N] tool calls only.
First, call [lookup tool] to get [the live identifier].
Then call [action tool] exactly once with these parameters:
[parameter]: [value]
[parameter]: use the result from the lookup call
---BODY START---
[any content the tool will send on your behalf, e.g. an email body]
---BODY END---
===INSTRUCTIONS (do NOT include in the output)===
Do not use execute_code or the shell.
Do not call any other tool. Do not retry.
Reply only DONE or ERROR with the full error text.
Each line earns its place:
- Name the tool, forbid the shell. Otherwise, the model writes a Python script instead of calling the tool.
- Lookup first, then act. Tools that need an ID (an inbox, a folder) want it from a live lookup call, not typed by hand. Hand-typed identifiers fail with errors like “404 Inbox not found” even when they look correct.
- Fence the content. The BODY markers keep your instructions out of the email your recipient receives.
- Demand DONE or ERROR. It ends the task cleanly (no retry loops) and surfaces the real error message when something fails.
Two habits to pair with it: verify results in the system of record (the sent folder, not the chat; “tool completed” only means the call returned), and share files as links in the body rather than attachments (attachment fields want raw file data or direct-download URLs, both fragile with a local model; a cloud-drive share link a human can click just works). If a task ever loops, /reset in Telegram kills it.
A worked example: “Email Scott the guide” becomes: Single task, two tool calls only. First, call the AgentMail list-inboxes tool to get the inbox. Then call the send tool exactly once: to scott@example.com, subject “Setup Guide”, sender from the lookup. Fenced body with the share link, then the instructions block. Save prompts that work as Hermes skills so they become one-word commands.
Check Out What Your Hermes Agent Can Do Now
Your headless Hermes Agent can message you on Telegram, send and receive email, search the web, and read any page you point it at, all from a silent box that phones home to no one.
Save the prompts that work as Hermes skills, and they become one-word commands. That’s where the setup stops feeling like tinkering and starts feeling like having a real assistant.
And be sure to read about my experience. I spent weeks tuning a headless Hermes Agent, so you don’t have to.