Skip to main content
Glama
cyril-belin

HermodMail MCP Server

by cyril-belin
README.md
# HermodMail

**A mailbox where humans and AI agents work together.**

๐Ÿ“Œ **Official website:** [cyril-belin.github.io/HermodMail](https://cyril-belin.github.io/HermodMail/)
(English and French โ€” this README remains the complete technical reference.)

HermodMail is an open-source webmail with a **native MCP server**: your existing
IMAP/SMTP mailbox, one shared business engine, and permission rules that apply
identically to the person reading mail in the browser and the agent working
through the API.

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   HTTP/cookies   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Vue web UI โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚  Fastify API                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ”‚        โ”‚                                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   Bearer token   โ”‚        โ–ผ                                 โ”‚
โ”‚ MCP agents  โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚  MailEngine (auth+scopes enforced here)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ”‚   โ”œโ”€โ”€ IMAP  (imapflow)   SQLite cache    โ”‚
                                 โ”‚   โ”œโ”€โ”€ SMTP  (nodemailer)  (better-       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   OpenAI-compat  โ”‚   โ””โ”€โ”€ AI    (Ollama / any)  sqlite3)     โ”‚
โ”‚ Ollama etc. โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

## Why HermodMail exists

For decades, email clients were designed for one kind of user: a human sitting
in front of a screen. That made sense โ€” until it didn't. Today, AI agents can
read, prepare, organize and send messages. Yet connecting one to a real mailbox
usually means a pile of scripts, separate IMAP credentials with far too much
power, and authorization logic you have to reinvent for every project.

HermodMail started from a simple question: **why not give humans and agents one
common email environment, with a single business engine and controlled
permissions?**

The human uses a classic web interface. The agent uses a native MCP endpoint.
Both work on the same accounts and folders โ€” but each with exactly the rights
that were granted to them. No parallel credential store, no out-of-band sync,
no "the agent has my full mailbox password" problem.

## How humans and agents collaborate

Every request โ€” from the browser or from an agent โ€” goes through **the same
engine**. Scopes and ownership are enforced there, never in the adapters. That
one design decision is what makes the collaboration safe:

- A **scoped API token** defines what an agent may do: read mail, write mail,
  send, delete, manage folders, use the AI assistant. Grant or revoke scopes
  at any time โ€” a running agent session picks the change up immediately.
- Agents **never send on their own initiative unless you let them**. Their
  strongest default move is writing to the *real* IMAP Drafts folder โ€” visible
  and editable in HermodMail and in every other mail client.
- Tokens cannot manage mail accounts at all. A leaked read-only token cannot
  enroll new credentials; adding a mailbox requires an interactive session.
- One HermodMail instance is **multi-user**: each person adds their own mail
  accounts and issues their own tokens, with isolation enforced in the engine.

## A concrete workflow

Imagine an entrepreneur on a train.

They ask their agent to find 50 prospects. The agent does its research, writes
50 personalized messages, and saves them through MCP โ€” straight into the
**IMAP Drafts folder**. It sends nothing.

Later, the entrepreneur opens HermodMail, finds the 50 drafts waiting, reads
each one, edits what needs editing, and sends.

The agent did the work. The human kept the decision.

That is one illustration of HermodMail's philosophy โ€” not its only use. Search,
summarize, triage, folder management, reply drafting: whatever your agent does,
it happens inside the same permissions model as the human.

## What makes it different

- **Native MCP, not a bolt-on.** `/mcp` is a route of the same server as the
  web UI, speaking full streamable HTTP (SSE responses to POST, a
  server-initiated stream per session, 15 s heartbeats). Seventeen tools:
  `list_accounts`, `list_folders`, `sync_folder`, `list_messages`,
  `search_messages`, `get_message`, `mark_message`, `move_message`,
  `delete_message`, `send_message`, `create_draft`, `summarize_message`,
  `draft_reply`, `export_markdown`, `create_folder`, `rename_folder`,
  `delete_folder`.
- **Granular permissions.** Tokens carry explicit scopes (`mail:read`,
  `mail:write`, `mail:send`, `mail:delete`, `folder:write`, `folder:delete`,
  `ai:use`) โ€” editable at any time, effective immediately, stored as SHA-256
  hashes only.
- **Shared drafts, real ones.** `create_draft` appends a genuine IMAP draft
  (`\Draft` flag) to the server's Drafts folder. Nothing is ever sent by a
  draft save. The human reviews it in the UI โ€” or in Thunderbird, or Gmail.
- **One engine, not two codepaths.** The web API and MCP share the same mail
  engine, so a fix or a guardrail applies to both at once.
- **Honest SMTP.** A failure after DATA transmission leaves the outcome
  *undetermined*: HermodMail classifies definitive rejections versus uncertain
  failures and never auto-retries a send. No duplicate bombs.
- **Guardrails on folders.** INBOX and system folders can't be renamed or
  deleted; deleting a non-empty folder requires an explicit `confirm` โ€” an
  agent can never wipe mail in one shot.

## A complete webmail first

HermodMail is a fully usable webmail even with **zero AI configured**:

- Real IMAP/SMTP โ€” connect any mailbox; folders, messages, flags, moves,
  sent-folder filing, Trash-aware deletion.
- Local SQLite cache (WAL) with FTS5 full-text search across subject, people
  and bodies.
- Safe HTML rendering โ€” all email HTML sanitized server-side; attachment
  downloads controlled.
- Smart address book โ€” correspondents harvested automatically from your mail
  (including existing IMAP history), powering recipient autocomplete; editable
  manually in Settings.
- Real IMAP folder management from the UI, with the same INBOX/system-folder
  protections as the agent API.
- Multilingual UI (French, English, Spanish) with on-demand message
  translation, dark theme, Markdown export of any message.
- Multi-user with admin-created accounts and strict per-user isolation.

## AI: optional and provider-agnostic

**HermodMail is not tied to any AI vendor.**

- The built-in assistant (message summaries, reply drafting, translation) is
  **entirely optional** โ€” the webmail works without any AI provider configured.
- It talks to **any OpenAI-compatible API**, so you can pick among many cloud
  providers and models; **Ollama** gives you fully local, offline models.
- We used **Gemini through its OpenAI-compatible endpoint** during real-world
  testing โ€” it's an example of the compatible family, not an integration
  HermodMail depends on. We don't claim every compatible provider has been
  individually tested.
- Agents connected over MCP bring **their own LLM**. What HermodMail's
  configured provider does is separate; the agent's intelligence never routes
  through HermodMail.
- No single provider is imposed โ€” swap anytime in Settings.

Email content and headers sent to models are always fenced as untrusted data
(`<<<UNTRUSTED_EMAIL_CONTENT>>>`, control characters stripped) with an explicit
system rule to ignore instructions inside email content โ€” a prompt-injection
guard, not a hope. And AI can never send mail by itself: only `send_message`
with the `mail:send` scope does.

## Screenshots

| The webmail | Drafts prepared by an agent |
| --- | --- |
| ![The HermodMail webmail interface](docs/screenshots/webmail.png) | ![Agent-prepared drafts in the Drafts folder](docs/screenshots/agent-drafts.png) |

| Composing (contact autocomplete, save draft) | API tokens for MCP agents |
| --- | --- |
| ![Compose window with contact autocomplete and save draft](docs/screenshots/compose-draft.png) | ![Scoped API tokens for MCP agents](docs/screenshots/mcp-tokens.png) |

_Dark mode:_

![HermodMail in dark mode](docs/screenshots/dark-theme.png)

## Installation

### From source (dev)

```bash
npm install
npm run dev          # API on :8667
npm run dev:web      # Vite dev server on :5667 (proxies /api and /mcp)
```

Open http://localhost:5667 โ€” the first visit asks you to create the admin
account, then add your IMAP/SMTP account in Settings.

### Production build

```bash
npm run build
PORT=8667 HOST=0.0.0.0 HERMODMAIL_WEB_DIST=apps/web/dist node apps/server/dist/index.js
```

Requires Node โ‰ฅ 22.

### Docker / Podman

```bash
cp .env.example .env            # then set HERMODMAIL_SECRET
docker compose up -d            # or: podman-compose up -d
```

The app listens on port 8667 and stores everything in the `hermodmail-data`
volume.

## Configuration (env)

| Variable | Default | Meaning |
| --- | --- | --- |
| `PORT` / `HOST` | `8667` / `127.0.0.1` | HTTP listen address |
| `HERMODMAIL_DATA_DIR` | `./data` | SQLite DB + secret key location |
| `HERMODMAIL_SECRET` | auto-generated file | Master key for credential encryption (โ‰ฅ 32 chars). **Set it in production** or backups are not portable. |
| `HERMODMAIL_WEB_DIST` | โ€“ | Built web client to serve |
| `HERMODMAIL_COOKIE_SECURE` | off | Set `Secure` on session cookies โ€” enable **only** behind HTTPS |
| `TRUST_PROXY` | off | Set `1` behind a reverse proxy so rate limiting uses the real client IP |
| `SESSION_TTL_HOURS` | `336` (14 d) | Web session lifetime |
| `INITIAL_SYNC_LIMIT` | `200` | Messages fetched on first folder sync |
| `BODY_INDEX_LIMIT` | `300` | Recent messages body-indexed for search |
| `MAX_ATTACHMENT_SIZE` | `5242880` | Largest attachment cached (bytes) |
| `LOG_LEVEL` | `info` | Fastify log level |

## MCP usage for agents

1. In HermodMail: **Settings โ†’ API tokens (MCP)** โ†’ create a token with only
   the scopes the agent needs.
2. Point any MCP client at the streamable-HTTP endpoint:

```json
{
  "mcpServers": {
    "hermodmail": {
      "type": "http",
      "url": "http://localhost:8667/mcp",
      "headers": { "Authorization": "Bearer hmt_..." }
    }
  }
}
```

`create_folder` / `rename_folder` (scope `folder:write`) and `delete_folder`
(scope `folder:delete`) operate on the **real IMAP server**, with the folder
guardrails described above. `create_draft` saves a **real IMAP draft** โ€”
recipients, subject, body and optional conversation references
(`in_reply_to` / `references`) โ€” without ever sending anything; `draft_reply`
is different: it only returns an AI text proposal. Drafts saved by agents are
visible and editable in the web UI (Drafts folder โ†’ "edit draft") and in any
other mail client.

Every call runs through the same engine as the UI: the agent sees only the
token owner's accounts, and destructive/sending tools fail without the
corresponding scope.

Wherever HermodMail runs, MCP lives at `http://<host>:8667/mcp`.

## Deploying on a VPS (HTTPS + remote agents)

| | Local machine | VPS |
| --- | --- | --- |
| Web UI | `http://localhost:8667/` | `https://mail.example.com/` |
| MCP | `http://localhost:8667/mcp` | `https://mail.example.com/mcp` |
| Agent auth | `Bearer hmt_โ€ฆ` | identical |

### 1. Run the container

```bash
git clone <repo> && cd HermodMail
echo "HERMODMAIL_SECRET=$(openssl rand -base64 48)" > .env
docker compose up -d --build
```

`HERMODMAIL_SECRET` is the key that encrypts stored IMAP/SMTP credentials โ€”
back it up and never rotate it casually (rotating without re-entering the mail
passwords makes them undecryptable).

### 2. Put TLS in front (strongly recommended)

Over plain HTTP, agent tokens and session cookies travel unencrypted. The
simplest front is Caddy with automatic certificates:

```
# Caddyfile
mail.example.com {
    reverse_proxy 127.0.0.1:8667
}
```

Then keep the app port private and tell it it lives behind a proxy โ€” in
`docker-compose.yml`:

```yaml
    ports:
      - "127.0.0.1:8667:8667"   # only the proxy reaches the app
    environment:
      - HERMODMAIL_SECRET=${HERMODMAIL_SECRET}
      - HERMODMAIL_COOKIE_SECURE=1   # cookies over HTTPS only
      - TRUST_PROXY=1                # rate-limit on the real client IP
```

SSE responses carry `X-Accel-Buffering: no` and comment heartbeats every 15 s
so reverse proxies don't buffer or drop idle MCP streams.

### 3. Multi-user on one VPS

The first account created becomes admin; every user then adds **their own**
mail accounts and creates **their own** MCP tokens. Isolation is enforced in
the engine and verified end to end: an agent holding user A's token sees only
A's mailboxes, and a read-only token cannot send.

## Security model

- Passwords: scrypt. Sessions: hashed tokens, HttpOnly SameSite cookie.
  Agents: hashed Bearer tokens with explicit scopes, editable and revocable at
  any time (effective immediately, including inside an open MCP session).
- Mail credentials: AES-256-GCM, key = HMAC-derived subkey of
  `HERMODMAIL_SECRET`. Secrets never leave the server except to the mail
  provider itself.
- Email HTML is sanitized server-side before storage (defense in depth; the
  client never renders raw email HTML). Attachment filenames are re-sanitized
  before being echoed into HTTP headers.
- CSP on the web client, `no-store` on API responses, same-origin enforcement
  on cookie-authenticated mutations (login CSRF included), login rate-limiting,
  MCP sessions bound to their token's owner.
- **SMTP honesty** and **prompt-injection fencing** as described above.

## Testing

- **194 automated tests** (server + web): crypto, auth, sanitization, mail
  engine, contacts & drafts with real MIME parsing/sending, AI guardrails,
  MCP over HTTP (streamable, SSE), folder protections, reliability/chaos
  (killed IMAP connections, SMTP failures, concurrent syncs), property-based
  checks, and a real local IMAP/SMTP fixture server.
- **Beyond the suite**, we have run real IMAP/SMTP sessions against a live
  LWS-hosted mailbox, exercised end to end from an MCP agent โ€” folder cycles,
  draft saving, real SMTP sending.

## Development

```bash
npm run typecheck   # server + web strict TypeScript
npm test            # 194 vitest tests
```

Layout: `apps/server/src/core` (engine: auth, mail, ai, contacts โ€” no transport
knowledge), `apps/server/src/http` (thin REST adapter), `apps/server/src/mcp`
(thin MCP adapter), `apps/web` (Vue 3 + Tailwind 4 + reka-ui components).

The engine talks to mail servers only through the `MailTransport`/`SmtpSender`
interfaces โ€” production uses imapflow/nodemailer; tests use an in-memory
transport with real MIME parsing.

## Roadmap

- IMAP IDLE push notifications ยท sieve-like rules ยท calendar attachments ยท
  multi-select batch actions ยท per-folder AI auto-classification ยท CardDAV
  contacts.

## Website

The official product site lives in [`site/`](site/) โ€” a static
[Astro](https://astro.build) project, fully isolated from the app workspaces
(this monorepo's `npm` scripts never touch it). It is published automatically
to <https://cyril-belin.github.io/HermodMail/> by
[`.github/workflows/deploy-site.yml`](.github/workflows/deploy-site.yml) when
files under `site/` change on `main` (manual runs are also available from the
Actions tab). English is the default language; the French version lives under
`/fr/`.

To work on the site locally:

```bash
cd site
npm install
npm run dev       # dev server (URL printed in the terminal)
npm run build     # static output in site/dist/ (what Pages publishes)
```

Screenshots shown on the site come from `docs/screenshots/` โ€” replace them
there and copy into `site/src/assets/` to refresh the site. No backend, no
secrets, and no mail infrastructure are involved in serving the site.

## License

HermodMail is licensed under the **GNU Affero General Public License v3.0 or
later** โ€” see [LICENSE](LICENSE).