Skip to main content
Glama
OR13

ietf-imap-mcp

by OR13
README.md
# ietf-imap-mcp

An [MCP](https://modelcontextprotocol.io) server for navigating **IETF
mailing-list archives** over the IETF IMAP service
(`imap.ietf.org:993`). It lets an MCP-capable assistant search, page through,
and read list traffic — handy when chairing a working group or BoF and you need
to track what the list is actually saying.

> Built to be a **good citizen** of shared IETF infrastructure. See
> [Respecting IETF resources](#respecting-ietf-resources).

## Tools

| Tool | What it does |
|------|--------------|
| `list_mailing_lists(pattern, limit)` | Discover list mailboxes (IMAP `LIST`). |
| `search_list(list_name, subject, from_addr, text, since, before, limit)` | Search a list; returns message summaries, newest first. |
| `page_list(list_name, offset, limit)` | Page through a list's messages, newest first. |
| `get_message(list_name, uid, include_body, max_body_chars)` | Fetch one message (headers + plain-text body) by UID. |

## Configuration

All configuration is via environment variables — **no credentials are ever
stored in the code or the repo**.

| Variable | Purpose | Default |
|----------|---------|---------|
| `IETF_IMAP_USER` | `anonymous`, or your Datatracker username | `anonymous` |
| `IETF_IMAP_EMAIL` | Email used as the password for **anonymous** access | — |
| `IETF_IMAP_PASSWORD` | Datatracker password (authenticated access) — plaintext | — |
| `IETF_IMAP_PASSWORD_CMD` | Shell command whose stdout is the password (e.g. `op read …`) | — |
| `IETF_IMAP_PASSWORD_FILE` | Path to a file containing the password | — |
| `IETF_IMAP_MIN_INTERVAL` | Minimum seconds between IMAP commands | `1.0` |
| `IETF_IMAP_MAILBOX_PREFIX` | Prefix for bare list names | `Shared Folders/` |

See [`.env.example`](.env.example) for a copy-paste starting point.

## Authentication

Per the [IETF lists page](https://www.ietf.org/participate/lists/), the IMAP
service supports two modes.

### Anonymous (recommended for public lists)

IMAP anonymous login uses your **email address in the password field** — this is
not a secret. This is all you need for public list archives:

```json
{
  "mcpServers": {
    "ietf-imap": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/ietf-imap-mcp", "python", "-m", "ietf_imap_mcp"],
      "env": { "IETF_IMAP_USER": "anonymous", "IETF_IMAP_EMAIL": "you@example.org" }
    }
  }
}
```

### Authenticated (Datatracker — only when anonymous is not possible)

Set `IETF_IMAP_USER` to your Datatracker username and supply the password.
The password is resolved in this order of preference:
`IETF_IMAP_PASSWORD` → `IETF_IMAP_PASSWORD_CMD` → `IETF_IMAP_PASSWORD_FILE`.

> ⚠️ **Keep the secret out of the config file.** Values in an MCP client's
> `env` block sit in **plaintext on disk** (and often sync to the cloud). Prefer
> `IETF_IMAP_PASSWORD_CMD` (resolve from a secret manager at launch) or
> `IETF_IMAP_PASSWORD_FILE` (a `chmod 600` file). Avoid `IETF_IMAP_PASSWORD`
> except for throwaway local testing, and never put a real password in a file
> you commit.

**Recommended — resolve from a secret manager (no secret on disk):**

```json
{
  "mcpServers": {
    "ietf-imap": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/ietf-imap-mcp", "python", "-m", "ietf_imap_mcp"],
      "env": {
        "IETF_IMAP_USER": "your-datatracker-username",
        "IETF_IMAP_PASSWORD_CMD": "op read \"op://Private/IETF Datatracker/password\""
      }
    }
  }
}
```

(`op` is the 1Password CLI; substitute `pass`, `gopass`, `security find-generic-password`,
`vault kv get`, etc.)

**Alternative — a permission-restricted file:**

```bash
install -m 600 /dev/null ~/.config/ietf-imap/datatracker.pw
printf '%s' 'your-password' > ~/.config/ietf-imap/datatracker.pw
```

```json
"env": {
  "IETF_IMAP_USER": "your-datatracker-username",
  "IETF_IMAP_PASSWORD_FILE": "~/.config/ietf-imap/datatracker.pw"
}
```

## Run

```bash
uv run ietf-imap-mcp          # stdio MCP server
# or
uv run python -m ietf_imap_mcp
```

## Respecting IETF resources

The IMAP server is shared infrastructure. This server:

- **Read-only** — mailboxes are opened with `EXAMINE`, never `SELECT`; it cannot
  flag, move, or delete anything.
- **Rate-limited** — a minimum interval (default 1s) is enforced between IMAP
  commands.
- **Bounded** — result counts are hard-capped (≤ 100 per call) so one call can't
  pull thousands of messages.
- **Connection-reusing** — one TLS connection is kept open rather than
  reconnecting per call.

Please don't lower the interval or remove the caps to hammer the service. For
bulk needs, use [rsync archive downloads](https://www.ietf.org/participate/lists/)
instead.

## Development

```bash
uv run pytest        # tests are fully mocked — no network, no live IMAP
```

## License

[MIT](LICENSE) © 2026 Orie Steele

TDQS

A4.3/5.0

Scored across 4 tools

Disambiguation4/5

The tools are mostly distinct: list_mailing_lists discovers lists, search_list filters by criteria, page_list browses without filters, and get_message fetches a specific message. However, search_list with no criteria could overlap with page_list, though descriptions clarify the intended use.

Naming Consistency5/5

All four tool names follow a consistent verb_noun pattern (list_mailing_lists, search_list, page_list, get_message) with underscore separation, making the naming predictable and easy to navigate.

Tool Count5/5

With only 4 tools, the server is well-scoped for a read-only IMAP mailing list client. Each tool serves a clear purpose and the count is appropriate for the domain.

Completeness4/5

The core workflows are covered: discovering lists, searching or browsing messages, and fetching a full message. Minor gaps exist, such as no direct message count or raw header retrieval, but these are not critical for typical usage.

Maintenance

ActivityInactive
ResponsivenessNo issues