Skip to main content
Glama
README.md
# hermes-cowork-bridge

An MCP (Model Context Protocol) stdio server that bridges Claude Cowork / Claude Desktop to a locally running [Hermes Agent](https://github.com/NousResearch/hermes-agent) instance, plus a full setup guide for Hermes itself and pairing the bridge with filesystem/shell access.

It exposes three tools:

- **`hermes_delegate`** — send a task to Hermes for autonomous execution (terminal, file ops, web search, memory, skills, browser automation). Returns a `run_id`.
- **`hermes_check_run`** — poll a delegated task's status/result by `run_id`.
- **`hermes_health`** — check that Hermes is alive and reachable, and inspect its current status.

## Why this exists

Claude Desktop's built-in "Add custom connector" UI has a known bug where servers with no OAuth flow fail to register ("Couldn't register with sign-in service"). This bridge sidesteps that entirely by registering as a **stdio** MCP server directly in `claude_desktop_config.json`, rather than going through the remote-connector OAuth discovery path.

## Requirements

- Python 3.10+
- WSL2 with a Linux distro (Ubuntu recommended) if running on Windows
- Node.js installed on the Windows side (for the filesystem/shell additions below) — verify with `node --version` in PowerShell
- Claude Desktop

---

## Part 0 — Installing and configuring Hermes Agent itself

If Hermes isn't installed yet, do this first. If it's already running, skip to Part 1, but read the config notes below — several of them fix real problems that surface once Hermes is under real use.

### Install

```bash
pip install hermes-agent
# or follow the current install instructions at
# github.com/NousResearch/hermes-agent
```

### Pick a model and provider

Hermes needs an LLM behind it. Run the interactive picker:

```bash
hermes model
```

Select a provider (OpenRouter is a solid default — wide model selection, one API key covers many models) and a specific model. Note the exact model ID shown — model catalogs change over time, and a model that existed at install time can be delisted later, causing silent 404s on every task. If that happens, rerun `hermes model` and pick a currently-live model from the actual list shown, don't reuse an old ID from memory or documentation.

### Config file locations

- `~/.hermes/config.yaml` — main config (model, provider, approvals, context limits)
- `~/.hermes/.env` — secrets and server settings (API keys, `API_SERVER_ENABLED`, `API_SERVER_KEY`)

### Settings worth checking/changing for a personal, single-user setup

Open `~/.hermes/config.yaml` and check these:

**Context file limit** — if you have a large `AGENTS.md` or similar context file, the default character limit can silently truncate it:
```yaml
context_file_max_chars: 80000
```
(default is often much lower — raise it if your context file is large; check for a truncation warning in the gateway startup logs to confirm this was actually the problem before changing it)

**Subagent auto-approval** — if Hermes spawns subagents/tool calls that sit waiting for approval with nothing to approve them (common in fully unattended/API-driven use), set:
```yaml
agent:
  subagent_auto_approve: true
```

**Global approvals mode** — if delegated tasks hang on `waiting_for_approval` indefinitely with no way to approve them (e.g. calling Hermes via its API server from another program, not the interactive CLI), set:
```yaml
approvals:
  mode: off
```
This disables all safety/approval prompts. Appropriate for a trusted, single-user, personal setup — not appropriate for multi-user, production, or anything where you want a human-in-the-loop gate before actions execute.

**Fallback provider** — protect against a single provider's rate limits or outages taking down every task:
```yaml
fallback_providers:
  - provider: <a different provider you have access to>
    model: <a specific model on that provider>
```

### Enable the API server

This bridge talks to Hermes over its API server, not the interactive CLI. In `~/.hermes/.env`:
```
API_SERVER_ENABLED=true
API_SERVER_KEY=<generate one yourself, see below>
```

This key isn't issued by anyone — it's a shared secret you invent, used only to authenticate this bridge to your own local Hermes instance. Generate a random one with either:
```bash
python3 -c "import secrets; print(secrets.token_hex(16))"
```
or
```bash
openssl rand -hex 16
```
Whatever string you get, use it as `API_SERVER_KEY` here **and** as `HERMES_API_KEY` in the bridge's own `.env` (Part 1, step 3) — the two must match exactly, character for character. There's no registration, no external service, no "correct" value — it just has to be the same string in both places.

### Start the gateway

```bash
hermes gateway
```
Leave this running. If you see a `NotFoundError` / `404` on startup or on the first real task, the configured model has likely been delisted — rerun `hermes model` and pick a currently-live one.

### Health check before moving on

In a second terminal:
```bash
curl http://127.0.0.1:8642/health/detailed
```
Confirm you get a real JSON response back, not a connection error, before proceeding to the bridge setup.

---

## Part 1 — The Hermes bridge itself

1. Clone this repo into your WSL2 home directory, e.g.:
   ```bash
   git clone <this-repo-url> ~/hermes-cowork-bridge
   cd ~/hermes-cowork-bridge
   ```

2. Create a virtual environment and install dependencies:
   ```bash
   python3 -m venv venv
   source venv/bin/activate
   pip install -r requirements.txt
   ```

3. Copy `.env.example` to `.env` and set the same key you set in `~/.hermes/.env`'s `API_SERVER_KEY` above:
   ```bash
   cp .env.example .env
   # edit .env — HERMES_API_KEY must exactly match API_SERVER_KEY from ~/.hermes/.env
   ```

4. Sanity check both files agree:
   ```bash
   grep API_SERVER_KEY ~/.hermes/.env
   grep HERMES_API_KEY ~/hermes-cowork-bridge/.env
   ```
   The values after the `=` must be identical.

---

## Part 2 — Locating and editing claude_desktop_config.json

Claude Desktop's config file location depends on how it was installed:

**MSIX-packaged (Microsoft Store) installs on Windows:**
```
C:\Users\<your-windows-username>\AppData\Local\Packages\Claude_<package-id>\LocalCache\Roaming\Claude\claude_desktop_config.json
```
The `<package-id>` segment is unique per install. If you don't know it, find it with PowerShell:
```powershell
Get-ChildItem "C:\Users\<your-windows-username>\AppData\Local\Packages" -Filter "*Claude*"
```

**Standard (non-Store) installs:**
```
%APPDATA%\Claude\claude_desktop_config.json
```

If the file doesn't exist yet, create it — an empty install has no `mcpServers` key until you add one.

---

## Part 3 — Registering all three MCP servers

Use `claude_desktop_config.example.json` in this repo as your starting template. It defines three servers together, since they're commonly used as a set:

- **`hermes`** — this bridge, giving Cowork task-delegation access to Hermes
- **`filesystem`** — direct read/write access to your WSL2 filesystem
- **`shell`** — direct command execution

Replace every placeholder before use:
- `YOUR_WSL_USERNAME` → your actual WSL2 username (run `whoami` inside WSL to confirm)
- `YOUR_WSL_DISTRO_NAME` → your WSL distro name as registered with Windows (run `wsl --list` in PowerShell to see it — commonly `Ubuntu`)
- Adjust the repo path if you cloned this bridge somewhere other than your home directory

### The filesystem and shell servers specifically

These two matter enough to call out in detail, because the naive setup fails in a specific, non-obvious way.

**The problem:** if you configure `filesystem` or `shell` to launch via `wsl.exe` and then run `npx` inside WSL, it frequently fails with:
```
env: 'node': No such file or directory
```
This happens because Claude Desktop launches `wsl.exe` with a minimal environment that doesn't inherit your normal WSL shell's PATH — so even though `node` and `npx` work fine when you open a WSL terminal yourself, they're unreachable when Desktop spawns the process.

**The fix:** run these two servers via **Windows-native Node**, not WSL's Node. Point `npx.cmd` (Windows) at the WSL filesystem using the `\\wsl$` UNC path instead of a Linux path:

```json
"filesystem": {
  "command": "C:\\Program Files\\nodejs\\npx.cmd",
  "args": [
    "-y",
    "@modelcontextprotocol/server-filesystem",
    "\\\\wsl$\\YOUR_WSL_DISTRO_NAME\\home\\YOUR_WSL_USERNAME"
  ]
}
```

Adjust `C:\\Program Files\\nodejs\\npx.cmd` if your Node install lives elsewhere — confirm the real path with `where npx` in PowerShell.

**For shell access specifically:** the package name `@modelcontextprotocol/server-shell` does not exist on npm (a common mistake to copy from outdated guides) and will fail with a 404. Use a real, published package instead — `bash-mcp` is a working option:

```json
"shell": {
  "command": "C:\\Program Files\\nodejs\\npx.cmd",
  "args": ["-y", "bash-mcp"]
}
```

Be aware this launches on the **Windows host** via `cmd.exe`, not inside WSL. If you need it to run Linux/bash commands inside your WSL distro specifically, prefix every command with `wsl.exe -d YOUR_WSL_DISTRO_NAME --`, e.g.:
```
wsl.exe -d YOUR_WSL_DISTRO_NAME -- ps aux | grep hermes
```

### Full example config

See `claude_desktop_config.example.json` in this repo for the complete three-server block, ready to drop into your real config file once placeholders are replaced.

---

## Part 4 — Apply and verify

1. Fully quit Claude Desktop — right-click its icon in the system tray and choose Exit/Quit. Closing the window alone is not enough; the process keeps running in the background and won't pick up config changes.

2. Reopen Claude Desktop.

3. In a Cowork session, verify each server individually:
   ```
   Check if Hermes is healthy and reachable.
   ```
   ```
   List the files in my home directory.
   ```
   ```
   Run: ps aux | grep hermes
   ```

If any of these fail, check the relevant MCP log — Claude Desktop writes one log file per server under the `logs` subfolder next to `claude_desktop_config.json` (e.g. `mcp-server-hermes.log`, `mcp-server-filesystem.log`, `mcp-server-shell.log`). The actual error text there is almost always more useful than guessing.

---

## Architecture notes

- The Hermes bridge runs as a **stdio** MCP server — Claude Desktop launches it as a subprocess and communicates over stdin/stdout. It is not an HTTP server itself; the HTTP calls to Hermes's own API happen internally.
- Hermes's API endpoints used: `POST /v1/runs` (submit task), `GET /v1/runs/{run_id}` (poll status), `GET /health/detailed` (health check).
- The `filesystem` and `shell` servers are unrelated to Hermes specifically — they're general-purpose MCP servers commonly paired with this bridge so Cowork can inspect logs, verify file state, and run diagnostic commands without round-tripping everything through `hermes_delegate`.

---

## Troubleshooting

Roughly in the order you're likely to hit them during initial setup, then ongoing issues below that.

### Setup-time issues

**`env: 'node': No such file or directory`**
You're running `filesystem` or `shell` via WSL's `wsl.exe` launch instead of Windows-native Node. Use `C:\Program Files\nodejs\npx.cmd` (or wherever `where npx` in PowerShell points) instead. See Part 3.

**`npm error 404` on `@modelcontextprotocol/server-shell`**
That package doesn't exist on npm. Use `bash-mcp` or another real, verified-published package instead.

**`Error accessing directory C:\home\...`**
The filesystem server resolved a Linux-style path (`/home/...`) as a Windows path because it's running under Windows Node. Use the `\\wsl$\<distro>\home\<user>` UNC path form instead.

**Config changes don't seem to take effect**
Claude Desktop caches its config in the running process. Fully quit via the system tray icon — closing the window is not the same as quitting the app.

**Server shows as connected but every tool call errors**
Check that the API key in this bridge's `.env` (`HERMES_API_KEY`) exactly matches `API_SERVER_KEY` in `~/.hermes/.env`. A mismatch here won't stop the stdio connection from establishing, but every actual call to Hermes's API will fail auth.

### Ongoing / operational issues

**404 on every model call inside Hermes, even though it worked before**
The configured model was delisted by its provider. Model catalogs on aggregator platforms (OpenRouter and similar) change over time — a model ID that worked at install time isn't guaranteed to stay live. Run `hermes model` and pick a currently-listed model rather than reusing an old ID from memory, a config file, or documentation.

**Runs stuck on `waiting_for_approval` with no way to approve them**
This happens when Hermes is driven through its API (as this bridge does) rather than the interactive CLI — there's no human present to click approve. Set `approvals.mode: off` in `~/.hermes/config.yaml`. This is appropriate for a trusted single-user setup; it is not appropriate if multiple people can trigger tasks or if you want a human gate before actions execute.

**Tasks fail or hang with no useful error, and the process has been running a very long time**
Long-running gateway or bridge processes can go stale — accumulate memory bloat, hold dead connections, or otherwise degrade — especially across many hours of continuous uptime. If something that used to work suddenly doesn't, and you can't find an obvious cause, kill and restart clean before debugging further:
```bash
pkill -9 -f "hermes gateway"
pkill -9 -f hermes_mcp_server.py
hermes gateway
```
Then fully quit and reopen Claude Desktop too, so it spawns fresh connections rather than reusing anything stale.

**`AGENTS.md` or other context file seems to be getting cut off / Hermes seems to be missing context it should have**
Check the gateway startup logs for a truncation warning. If present, raise `context_file_max_chars` in `~/.hermes/config.yaml` to a value larger than your actual file size.

**A delegated task reports success, but the thing it claims to have built/changed doesn't actually exist**
Don't trust a subprocess's or sub-agent's self-reported summary at face value — independently verify by checking the actual file, running the actual command, or reading the actual log, rather than accepting a narrated "done" as fact. This is a real failure mode with autonomous agents generally, not specific to Hermes.

**Rate limits or provider outages taking down every task**
Configure `fallback_providers` in `~/.hermes/config.yaml` so a single provider's rate limit or downtime doesn't stop all work. See Part 0.

---

## License

MIT — do whatever you want with this.