Skip to main content
Glama
FaisalFehad

IMAP Mail MCP

by FaisalFehad
README.md
# IMAP Mail MCP

IMAP Mail MCP is an MCP (Model Context Protocol) server that lets LLM clients read email through IMAP tools.

It is designed for:
- local AI workflows (Cursor, ollmcp, other MCP hosts)
- mailbox exploration/search/summarization
- extension by developers who want to add mail tools

Current implementation is read-only (list/search/fetch/status/thread context/attachment metadata).

## What You Get

- 10 MCP tools for common mail workflows
- consistent sorting and cursor pagination support
- guardrails for result size and snippet size
- deterministic tests plus CI
- TypeScript codebase that is easy to extend

## Quick Start

### 1. Install

```bash
git clone https://github.com/FaisalFehad/imap-mail-mcp.git
cd imap-mail-mcp
cp .env.example .env
npm install
npm run build
```

### 2. Configure `.env`

Set IMAP credentials in `.env`:

| Variable | Required | Description | Typical Example |
|---|---|---|---|
| `IMAP_HOST` | yes | IMAP host | `127.0.0.1` |
| `IMAP_PORT` | yes | IMAP port | `1143` |
| `IMAP_SECURE` | yes | `true` for TLS, `false` for plain | `false` |
| `IMAP_USER` | yes | IMAP username | `you@proton.me` |
| `IMAP_PASS` | yes | IMAP password | `...` |
| `IMAP_TLS_REJECT_UNAUTHORIZED` | no | validate TLS cert chain | `false` for local self-signed |
| `MAIL_MAX_BODY_LENGTH` | no | max body chars in `mail_get_message` | `50000` |
| `MAIL_MAX_RESULTS` | no | global cap for list/search limits | `200` |
| `MAIL_SNIPPET_LENGTH` | no | max snippet chars when enabled | `400` |

Proton Bridge users usually run with `IMAP_HOST=127.0.0.1`, `IMAP_PORT=1143`, `IMAP_SECURE=false`.

### 3. Run

```bash
node dist/index.js
```

Or via package bin:

```bash
npx imap-mail-mcp
```

Compatibility alias still works:

```bash
npx proton-bridge-mcp
```

## MCP Client Setup

### Cursor

Add server config (Settings -> MCP):

```json
{
  "mcpServers": {
    "imap-mail": {
      "command": "node",
      "args": ["/absolute/path/to/imap-mail-mcp/dist/index.js"],
      "env": {
        "IMAP_HOST": "127.0.0.1",
        "IMAP_PORT": "1143",
        "IMAP_SECURE": "false",
        "IMAP_USER": "your@proton.me",
        "IMAP_PASS": "your-bridge-password"
      }
    }
  }
}
```

### ollmcp

1. Create `~/.config/ollmcp/mcp-servers/servers.json`:

```json
{
  "mcpServers": {
    "imap-mail": {
      "command": "node",
      "args": ["/absolute/path/to/imap-mail-mcp/dist/index.js"],
      "env": {
        "IMAP_HOST": "127.0.0.1",
        "IMAP_PORT": "1143",
        "IMAP_SECURE": "false",
        "IMAP_USER": "your@proton.me",
        "IMAP_PASS": "your-bridge-password"
      },
      "disabled": false
    }
  }
}
```

2. Run:

```bash
ollmcp -j ~/.config/ollmcp/mcp-servers/servers.json
```

If `ollmcp` is not in PATH, use `~/.local/bin/ollmcp`.

## Tool Reference

Use `mail_search_advanced` as the default search entry point.

| Tool | Use For | Notes |
|---|---|---|
| `mail_list_folders` | list mailboxes/folders | start here |
| `mail_list_messages` | list messages in one mailbox | supports `limit`, `sort`, `cursor`, `includeSnippet`, `returnPage` |
| `mail_get_message` | full message body by UID | returns envelope + body text |
| `mail_search` | basic filter search | convenience wrapper |
| `mail_search_advanced` | keyword/sender/receiver/subject/body/date/sent-date/read-state/message-id | primary search tool |
| `mail_get_mailbox_status` | counters for one mailbox | messages, unseen, recent, UID metadata |
| `mail_list_unread` | unread messages in a mailbox | same pagination/sort options as list/search |
| `mail_list_attachments` | attachment metadata by UID | no binary download |
| `mail_query_by_folder` | free text query by selected fields | convenience wrapper |
| `mail_get_thread_context` | related messages around a UID | thread continuity for summarization/reply |

### Common List/Search Options

Supported by list/search tools:

- `limit`: requested size (clamped by `MAIL_MAX_RESULTS`)
- `sort`: `asc` or `desc` (default `desc`)
- `cursor`: opaque cursor for next page
- `includeSnippet`: include snippet text in envelope results
- `returnPage`: return `{ items, nextCursor }` instead of only array

Envelope result fields are stable:

```json
{
  "uid": 123,
  "subject": "string",
  "from": "comma-separated addresses",
  "to": "comma-separated addresses",
  "date": "ISO-8601 string",
  "messageId": "optional string",
  "snippet": "optional string"
}
```

## Example Workflows

### Find unread billing mail in INBOX

1. `mail_search_advanced` with `mailbox=INBOX`, `keyword=bill`, `unseen=true`, `limit=20`
2. `mail_get_message` on the most relevant UID

### Summarize a conversation before drafting a reply

1. `mail_get_thread_context` with target UID and `limit`
2. fetch one or two full messages with `mail_get_message`
3. summarize using context

### Scan a large folder in pages

1. `mail_list_messages` with `returnPage=true`
2. pass returned `nextCursor` to next call until absent

## Developer Guide (Use This Codebase)

### Project Layout

```text
src/index.ts   MCP server, tool schemas, handlers
src/imap.ts    IMAP operations and query behavior
src/query.ts   sorting/pagination/cursor/snippet helpers
src/config.ts  environment parsing and defaults
tests/*.test.mjs deterministic tests
```

### Add a New Tool

1. Add schema in `src/index.ts` tool list.
2. Add handler branch in `src/index.ts` call handler.
3. Implement IMAP logic in `src/imap.ts`.
4. Add deterministic tests in `tests/`.
5. Update this README tool table.

### Keep LLM Behavior Predictable

- keep envelope field names stable
- prefer additive changes (avoid breaking existing tool args)
- keep default ordering deterministic
- keep limits clamped
- avoid expensive full-mailbox scans where possible

## Testing

Run deterministic tests (no live mailbox required):

```bash
npm test
```

Run live smoke test (requires real IMAP credentials):

```bash
node scripts/test-mcp.mjs
```

CI runs:

- `npm ci`
- `npx tsc --noEmit`
- `npm test`

## Troubleshooting

### `Error: Command failed no such user (NO)`

Usually bad `IMAP_USER`/`IMAP_PASS` or temporary server lockout after failed attempts.

Check:
- `IMAP_USER` is your IMAP login identity, not host/IP
- `IMAP_PASS` is correct for that IMAP account
- for Proton Bridge, use the Bridge-generated password
- wait a few minutes after repeated failed login attempts

### TLS errors (`wrong version number`, `ssl3_get_record`)

You are likely using TLS against a plain IMAP port.

Fix:
- set `IMAP_SECURE=false` for local plain IMAP endpoints (common with Bridge)
- verify port matches secure/non-secure mode

### Self-signed cert errors

If your IMAP endpoint uses self-signed certs:

- set `IMAP_TLS_REJECT_UNAUTHORIZED=false`

### `ollmcp: command not found`

Install via pip/pipx/uvx, or run with full path:

```bash
~/.local/bin/ollmcp -j ~/.config/ollmcp/mcp-servers/servers.json
```

## Security

- never commit `.env` or credential files
- keep IMAP credentials in MCP client env or local `.env`
- this implementation does not send or modify mail

## License

MIT

TDQS

A3.5/5.0

Scored across 10 tools

Disambiguation4/5

Most tools have distinct purposes, but the three search tools (mail_query_by_folder, mail_search, mail_search_advanced) have overlapping functionality, which could lead to confusion despite the descriptions advising preference for mail_search_advanced.

Naming Consistency3/5

The naming pattern is mostly 'mail_verb_noun' but uses inconsistent verbs (get vs list vs query) and mixes conventions between retrieval actions, creating slight inconsistency.

Tool Count5/5

With 10 tools, the set is well-scoped for an IMAP-focused server, covering the essential operations for reading and searching emails without being excessive.

Completeness3/5

The server covers reading, searching, and thread context well, but notable gaps exist for common IMAP actions like marking messages as read/unread, deleting, or moving messages.

Maintenance

ActivityInactive
ResponsivenessNo issues