Skip to main content
Glama
igorolv

mail-mcp-server

README.md
# Mail MCP Server

[![CI](https://github.com/igorolv/mail-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/igorolv/mail-mcp-server/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/igorolv/mail-mcp-server)](https://github.com/igorolv/mail-mcp-server/releases/latest)
[![License](https://img.shields.io/github/license/igorolv/mail-mcp-server)](LICENSE)
[![Java 25](https://img.shields.io/badge/Java-25-blue?logo=openjdk)](https://adoptium.net/)
[![MCP](https://img.shields.io/badge/MCP-server-8A2BE2)](https://modelcontextprotocol.io/)
[![Glama score](https://glama.ai/mcp/servers/igorolv/mail-mcp-server/badges/score.svg)](https://glama.ai/mcp/servers/igorolv/mail-mcp-server)

**Give your AI agent your whole mailbox, including on-premises Exchange, with no way to change it.**

MCP server (stdio) for on-premises Microsoft Exchange over EWS and any IMAP mailbox with password
sign-in, tested on Mail.ru and Gmail. It downloads every message once, converts bodies and
attachments to Markdown on your machine, builds a Lucene search index, and answers the agent from
that copy, so text inside PDF, Office and ZIP attachments is searchable in Russian and English.

**Status:** version `0.1.0` is published. Checked end to end on Exchange 2013 (about 8
thousand messages), Mail.ru (about 34 thousand) and Gmail with an app password. Developed and
tested on Windows.

**Contents:** [Features](#features) ·
[How it works](#how-it-works) ·
[Typical Scenarios](#typical-scenarios) ·
[Quickstart](#quickstart) ·
[MCP Tools](#mcp-tools) ·
[Commands](#commands) ·
[Build](#build) ·
[Stack](#stack) ·
[Documentation](#documentation) ·
[License](#license)

## Features

- **Works with on-premises Exchange.** EWS with NTLM and your normal domain password, tested on
  Exchange 2013. No Microsoft Graph needed. Other mailboxes work over IMAP, tested on Mail.ru and Gmail.
- **Attachments are searchable too.** Text from PDF, Word, Excel, PowerPoint, ZIP archives,
  attached emails, Outlook `.msg` and `winmail.dat` is extracted and indexed, so the agent can
  find the contract inside the attachment, not just the message that carried it.
- **Search that understands word forms.** Russian and English, across subject, body, addresses
  and attachments. Copies of one message appear once, and conversations are followed across
  folders ([search](docs/search.md)).
- **Cannot change your mailbox.** Read-only is enforced at the protocol level: write commands are
  rejected before they are sent, and downloading a message does not mark it as read
  ([security](docs/security.md)).
- **Careful with your account.** One sign-in per sync and no retries. After a rejected password,
  it stops trying until you fix it, which reduces the risk of a domain lockout.
- **Fast, and works offline.** The agent reads a local copy, so answers don't depend on the mail
  server. Sync runs only when you ask for it.
- **Your mail stays on your machine.** No cloud service of its own: conversion and indexing are
  local, and the local copy is unfiltered. Your AI client still sees what the tools return, and an
  optional [secret filter](docs/security.md#secret-filter) masks passwords and codes in responses.
- **Several mailboxes, one server.** Passwords can stay out of the config, in Windows Credential
  Manager, a password manager or an environment variable ([connections](docs/connections.md)).

## How it works

1. **Download all mail.** The `sync` tool copies the included folders of each mailbox as original `.eml` files, then only changes on later runs.
2. **Convert it locally to Markdown.** Bodies and attachments — PDF, Office documents, archives,
   attached messages — are converted with pandoc (required), PDFBox and Apache POI.
3. **Index it.** A Lucene index per mailbox with Russian and English word forms.
4. **Use it from MCP.** Tools search and read the local copy only; they never contact a mail
   server.

```text
 MCP client (Claude Code, Codex, OpenCode, VS Code, ...)
      |  JSON-RPC over stdin / stdout
      v
 mail-mcp-server --- search, read ---> Lucene index + <data-dir>/<connection>/messages/
      |                                       ^
      +--- sync (on request) --- EWS / IMAP --+--> .eml -> Markdown -> index
```

## Typical Scenarios

The agent picks the tools itself; these are the chains it usually follows.

| Scenario | Example request | Tools |
|---|---|---|
| Find mail on a topic | "Find the letters about the supply contract in `work` and show the attachments." | `search` → `getMessage` → `getAttachment` |
| Mail of a period | "What came to my work mail yesterday?" | `search` with `since`/`until` and no query → `getMessage` |
| Follow a conversation | "Show the whole discussion around this letter." | `search` → `getThread` → `getMessage` |
| Read an attachment | "What does the price list in the last letter from the supplier say?" | `search` (`from`, `attachmentName`) → `getAttachment` |
| Bring mail up to date | "Update my work mail and tell me what is new." | `sync` → `syncStatus` → `search` with `since` |
| Browse a folder | "List the latest messages in my inbox." | `listFolders` → `listMessages` |
| Organize a mailbox | "Who sends the most mail to my inbox, and where is it filed?" | `listFolders` → `summarizeSenders` → `search` |

## Quickstart

**1. Install** JDK 25 and [pandoc 3.x](https://pandoc.org/installing.html). Pandoc is required
and must be on `PATH`. Check in a new terminal:

```powershell
java -version
pandoc --version
```

**2. Get the jar:** download `mail-mcp-server.jar` from the
[latest release](https://github.com/igorolv/mail-mcp-server/releases/latest), or build it yourself:

```powershell
.\gradlew.bat bootJar      # -> build\libs\mail-mcp-server.jar
```

No local JDK or pandoc? Use the Docker image `ghcr.io/igorolv/mail-mcp-server` instead, with fewer
ways to store passwords: see [Docker](docs/clients.md#docker).

**3. Describe your mailboxes** in `%USERPROFILE%\.mail-mcp-server\connections.json`
(`~/.mail-mcp-server/connections.json` elsewhere), one entry per mailbox. Keep only the ones you
need and replace the example addresses:

```json
{
  "connections": {
    "work": {
      "type": "ews",
      "url": "https://webmail.example.com/EWS/Exchange.asmx",
      "auth": "ntlm",
      "username": "user",
      "windowsCredential": "mail-mcp/work",
      "description": "Work mail, Exchange 2013"
    },
    "mailru": {
      "type": "imap",
      "host": "imap.mail.ru",
      "port": 993,
      "tls": true,
      "auth": "app-password",
      "username": "user@mail.ru",
      "passwordCommand": ["security", "find-generic-password", "-s", "mail-mcp", "-a", "mailru", "-w"],
      "description": "Personal Mail.ru"
    },
    "gmail": {
      "type": "imap",
      "host": "imap.gmail.com",
      "port": 993,
      "tls": true,
      "auth": "app-password",
      "username": "user@gmail.com",
      "password": "${MAIL_GMAIL_PASSWORD}",
      "description": "Personal Gmail"
    }
  }
}
```

Each entry shows a different password source: `work` reads Windows Credential Manager, `mailru`
runs a command that prints the password (here the macOS Keychain; 1Password, `pass` and others
work the same way), and `gmail` reads an environment variable, which must be set wherever the
server starts. A literal password works too. Which password each provider needs (domain password, Mail.ru or Gmail
app password, Gmail OAuth2) and how to store it: [docs/connections.md](docs/connections.md).

**4. Register the server** with your MCP client, with an absolute path to the jar. Claude Code:

```powershell
claude mcp add --scope user mail -- java -jar C:/path/to/mail-mcp-server.jar
```

Other clients and environment variables: [docs/clients.md](docs/clients.md). If you installed
pandoc or changed `PATH` while the client was running, fully restart the client.

**5. Download your mail.** Ask the agent: *"Sync my `work` mailbox and tell me when it's done."*

The first sync downloads the whole mailbox and can take a long time, so keep the client open. If it
stops, ask again and it continues. A password manager must be unlocked or ask in its own window,
because the server has no terminal to ask in. For a large mailbox, or to see progress in a
terminal, use [`mail-sync`](docs/commands.md#mail-sync).

**6. Try it.** Ask the agent: *"Which mailboxes can you read?"* or *"Find the letters about the
supply contract in `work` from last week."*

Something went wrong: [docs/troubleshooting.md](docs/troubleshooting.md).

## MCP Tools

Every tool except `listConnections` takes a required `connection` argument.

| Tool | What it does |
|---|---|
| `listConnections` | Configured mailboxes with type, sign-in method, description and configuration errors |
| `sync` | Starts a background synchronization and returns at once |
| `syncStatus` | Progress and result of the current or last run |
| `listFolders` | Local folders with message counts |
| `listMessages` | Message cards of one folder, newest first, in pages |
| `getMessage` | Headers, the body in chunks, attachments with file paths |
| `getAttachment` | The text of one attachment in chunks, and the file path |
| `search` | Full-text search with Russian and English word forms, fields, folder and date filters |
| `summarizeSenders` | Message counts by sender address or domain, with dates and folders |
| `getThread` | The conversation of a message across folders, oldest first |

Query syntax and results: [docs/search.md](docs/search.md). Arguments and limits:
[docs/tools-agent.md](docs/tools-agent.md).

## Commands

Offline maintenance, sign-in recovery and Gmail OAuth2 consent run in a terminal:
[docs/commands.md](docs/commands.md).

## Build

```powershell
.\gradlew.bat build        # compile, all tests, bootJar -> build\libs\mail-mcp-server.jar
```

The build needs a JDK 25 toolchain; Gradle can download one. Tests never contact a mail server:
they use loopback fakes and the synthetic data sets in `testdata/`. Tests that need pandoc are
skipped when it is not installed.

## Stack

- Java 25, Gradle 9 with a version catalog
- Spring Boot `4.2.0-RC1`, Spring AI `2.1.0-RC1` MCP server (stdio)
- Apache HttpClient 5.6.5 (EWS with NTLM), StAX for SOAP
- Eclipse Angus Mail 2.0.5 (IMAP and MIME parsing)
- Apache Lucene 10.5.2 (index, Russian and English analysis, highlighting)
- pandoc 3.x (external process), Apache PDFBox 3.0.5, Apache POI 5.4.1

## Documentation

| Document | Contents |
|---|---|
| [docs/connections.md](docs/connections.md) | The connections file, passwords, Exchange, Mail.ru and Gmail setup |
| [docs/clients.md](docs/clients.md) | Registering the server with MCP clients, environment variables |
| [docs/commands.md](docs/commands.md) | User-run commands, their output and exit codes |
| [docs/search.md](docs/search.md) | Search syntax, dates, copies, conversations, word forms |
| [docs/security.md](docs/security.md) | Read-only protection, sign-in policy, credentials, local data, secret filter |
| [docs/troubleshooting.md](docs/troubleshooting.md) | Common problems and what to check first |
| [AGENTS.md](AGENTS.md) | Rules for AI agents changing this repository, and the map of engineering documents (`docs/*-agent.md`) |
| [docs/plan.md](docs/plan.md), [docs/decisions.md](docs/decisions.md), [docs/results.md](docs/results.md) | Plan, decisions and real-mailbox results |

## License

This project is licensed under the [Apache License, Version 2.0](LICENSE). Dependencies keep their
own licenses: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).

TDQS

A4.5/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a clearly distinct role: connection discovery, folder browsing, message browsing/reading, search, thread reading, attachment reading, sync, and sync status. The descriptions explicitly disambiguate close pairs like listMessages vs. search and getMessage vs. getThread.

Naming Consistency4/5

Most tools follow a consistent camelCase verb_noun pattern such as listConnections, getMessage, listFolders, and getThread. The bare verbs search and sync plus syncStatus are minor deviations but still readable and predictable.

Tool Count5/5

Nine tools is well-scoped for a local mail access server. Each tool earns its place by covering a specific part of the browse/read/search/sync workflow.

Completeness4/5

The set covers the core read-only mail workflow: connections, folders, messages, search, threads, attachments, syncing, and sync status. It lacks compose/send or message mutation operations, but those may be outside the apparent local-copy purpose.

Maintenance

ActivityMaintained
ResponsivenessResponsive