Skip to main content
Glama
README.md
# imap-mcp

IMAP client MCP. Read mail from a Dovecot / Synology MailPlus mailbox, and
optionally fire webhooks when new matching messages arrive — replaces fragile
Gmail OAuth for automation triggers.

## Tools

| Tool | Description |
|---|---|
| `list_unread(mailbox, limit)` | Unread message summaries |
| `search(criteria, mailbox, limit)` | Raw IMAP SEARCH |
| `fetch(uid, mailbox)` | Full message (headers + text + html) |
| `mark_read(uid, mailbox)` | Set `\Seen` flag |
| `move(uid, source, dest)` | Move to another folder |
| `list_folders()` | All folders |
| `get_status()` | IMAP ping + watcher rules + fire count |

## Environment

| Variable | Required | Description |
|---|---|---|
| `IMAP_HOST` | ✓ | Server (e.g. `192.168.68.61`) |
| `IMAP_PORT` | | Default `993` |
| `IMAP_SSL` | | `true` (default) or `false` |
| `IMAP_USERNAME` | ✓ | Synology account name, not email |
| `IMAP_PASSWORD` | ✓ | |
| `IMAP_DEFAULT_MAILBOX` | | Default `INBOX` |
| `WATCHES_JSON` | | Inline JSON array of watch rules |
| `WATCHES_FILE` | | Path to JSON file (default `/data/watches.json`) |
| `POLL_INTERVAL_SECS` | | Default `30` |
| `PORT` | | MCP server port (default `38105`) |

## Watch rules

```json
[
  {
    "name": "garmin-livetrack",
    "mailbox": "INBOX",
    "criteria": "FROM \"noreply@garmin.com\" SUBJECT \"LiveTrack\" UNSEEN",
    "webhook_url": "http://host.docker.internal:5678/webhook/livetrack-mail",
    "mark_read": true,
    "move_to": null
  }
]
```

On startup the watcher "primes" each rule with existing matching UIDs, so
only *new* mail after boot fires. Every `POLL_INTERVAL_SECS` it re-runs
each rule's criteria and POSTs matching messages (full body) to `webhook_url`
as `{ "rule": "...", "mailbox": "...", "message": {...} }`. After a
successful POST it either moves the message to `move_to`, or marks it read
if `mark_read`.