Notmuch

Notmuch

An MCP server that exposes a local notmuch email database to an LLM client such as Claude. It is read-first: searching, reading, and understanding mail is always available; writing anything (drafts, tags, exported files) requires an explicit opt-in flag and is confined to clearly bounded locations.

Category
访问服务器

README

mcp-server-notmuch

<p align="center"> <img src="logo.jpg" alt="mcp-server-notmuch logo" width="200"/> </p>

An MCP server that exposes a local notmuch email database to an LLM client such as Claude. It is read-first: searching, reading, and understanding mail is always available; writing anything (drafts, tags, exported files) requires an explicit opt-in flag and is confined to clearly bounded locations.

It never sends mail. There is no send capability anywhere in this codebase, in any mode, with any flag. Drafts are written to a local maildir for you to review and send yourself in a real mail client.

What it does

  • Searches and reads your mail (threads, single messages, attachments, calendar invites, office documents, images) via the real notmuch CLI.
  • Understands scopes: a named, pre-configured notmuch query (e.g. "personal mail" vs. "mailing lists") that every search is confined to unless you ask otherwise.
  • Answers "what's still unanswered" and "who owes me a reply" (mail_pending), "has this come up before" (mail_related_threads), and gives a token-cheap overview of a long thread before you read all of it (mail_thread_overview).
  • Optionally composes and revises plain-text drafts (--allow-drafts), tags messages (--allow-tags), or exports attachments and a Gource visualization of your mailbox history to a directory you name (--allow-export DIR).

What it does not do

  • It does not send mail. Ever.
  • It does not modify your mail in any way unless you pass --allow-tags (tagging) or --allow-drafts (writing a new file into a drafts maildir). Neither flag lets it touch existing messages' content.
  • It does not read or write outside the notmuch database, the configured drafts maildir, and (only with --allow-export) the configured export directory.
  • It does not require or use the notmuch2 Python bindings, so no compiler is needed to install it.

Install

From PyPI (once published)

$ uvx --prerelease=allow mcp-server-notmuch --help

From source

$ git clone https://github.com/hgn/mcp-server-notmuch
$ cd mcp-server-notmuch
$ uv pip install --prerelease=allow -e .
$ mcp-server-notmuch --help

The --prerelease=allow is required because this project pins a pre-release of the mcp SDK (see SDK version below); it is not optional.

System requirements

  • notmuch (the CLI, not just the library) on PATH or pointed to via notmuch.binary in the config.
  • poppler-utils (pdftotext) to read PDF attachments. Without it, mail_read_attachment on a PDF names the missing package.
  • pandoc or libreoffice to read office documents (doc/docx/odt/rtf). Without either, the error names both options.
  • Optionally, Pillow (pip install 'mcp-server-notmuch[image]') to let oversized image attachments be downscaled instead of refused.
  • Optionally, Gource to actually play back the log mail_export_gource writes.

MCP client configuration

Claude Code

Read-only (the default: search and read tools only):

$ claude mcp add notmuch -- uvx --prerelease=allow mcp-server-notmuch

With drafts enabled (also allows revising/tagging as needed):

$ claude mcp add notmuch -- uvx --prerelease=allow mcp-server-notmuch --allow-drafts

Or by hand in .mcp.json:

{
  "mcpServers": {
    "notmuch": {
      "command": "uvx",
      "args": ["--prerelease=allow", "mcp-server-notmuch", "--allow-drafts"]
    }
  }
}

Claude Desktop

Add to claude_desktop_config.json (read-only default):

{
  "mcpServers": {
    "notmuch": {
      "command": "uvx",
      "args": ["--prerelease=allow", "mcp-server-notmuch"]
    }
  }
}

With drafts enabled:

{
  "mcpServers": {
    "notmuch": {
      "command": "uvx",
      "args": ["--prerelease=allow", "mcp-server-notmuch", "--allow-drafts"]
    }
  }
}

Configuration

The server reads $XDG_CONFIG_HOME/mcp-server-notmuch/config.toml (~/.config/mcp-server-notmuch/config.toml if XDG_CONFIG_HOME is unset), or a path given with --config. A missing file is not an error: the server falls back to the system notmuch binary and a single built-in scope all with an empty query. Once a file exists, [scopes] is authoritative and all is no longer implied.

See config.example.toml for a fully commented reference file. Summary of every key:

Section Key Default Meaning
[notmuch] binary "notmuch" Path or bare name of the notmuch binary.
config notmuch's own default Path passed as NOTMUCH_CONFIG.
[limits] default_limit 20 Rows returned when a tool call omits limit.
max_limit 100 Hard ceiling on limit, whatever is requested.
max_body_chars 40000 Message body truncation point.
max_attachment_chars 100000 Attachment/office/calendar text truncation point.
max_image_bytes 5242880 Image size ceiling; downscaled with Pillow if larger, else refused.
[scopes] default (required once [scopes] exists) Scope used when a tool call omits scope.
[scopes.<name>] query A notmuch query ANDed with every search using this scope.
description "" Shown by mail_list_scopes.
[drafts] maildir unset Root of a maildir (cur/, new/, tmp/) for mail_create_draft.
from unset From: header on every draft.
signature unset Plain-text signature file, appended on request.
wrap_columns 72 Hard-wrap width for drafted plain text.
max_total_attachment_bytes 26214400 (25 MiB) Ceiling on draft attachments' combined size.
[identity] addresses notmuch's user.primary_email/user.other_email Your own address(es); used to exclude yourself from reply-all and to detect mail_pending direction="waiting".

mail_create_draft/mail_update_draft refuse to run unless both drafts.maildir and drafts.from are set. mail_pending direction="waiting" needs [identity] addresses (or a readable notmuch user.primary_email) to know which address is "you".

Scope resolution: a tool's scope argument names a configured scope; its query is ANDed with the caller's query, both sides parenthesized ((scope_query) and (user_query)), so an or on either side cannot leak past the other. An unknown scope name is an error listing the configured scopes; scope="all" is never silently unfiltered unless you define a scope literally named all.

Tiers and tools

Four tiers. The read tier is always registered. The other three are registered only when their flag is passed — there is no "registered but refused" state, an unauthorized tool is simply absent from the tool list a client sees.

Flag Registers
(none) Read tier: search, read, list, prepare — nothing is written.
--allow-drafts Draft tier: compose and revise local plain-text drafts.
--allow-tags Tag tier: add/remove tags on existing messages.
--allow-export DIR Export tier: write attachments/a Gource log into DIR.

Read tier (always on)

Tool Purpose
mail_search Search threads or messages, paged, with a truncation notice.
mail_read_thread Every message in a thread, oldest first.
mail_thread_overview One line per message (date/size/from), tree or flat layout, before reading a long thread in full.
mail_related_threads Heuristic "has this come up before" (subject + participant overlap).
mail_pending Threads you owe a reply on, or threads you're waiting on a reply to.
mail_read_message A single message's headers and body.
mail_count Cheap message/thread count for a query.
mail_list_addresses Resolve a name to the real address(es) behind it.
mail_list_attachments List one message's attachments.
mail_read_attachment Read one attachment: text, PDF, image, office document, or calendar invite.
mail_find_attachments Find attachments across a whole search (e.g. "all PDFs from 2025").
mail_list_scopes List the configured scopes.
mail_prepare_reply Derive reply/reply-all/forward headers and quoted/forwarded body; writes nothing.

Draft tier (--allow-drafts)

Tool Purpose
mail_create_draft Compose a plain-text draft (optionally with attachments) into the configured maildir.
mail_update_draft Revise an existing draft in place; only the given fields change.

Tag tier (--allow-tags)

Tool Purpose
mail_tag Add/remove tags on every message matching a query.

Export tier (--allow-export DIR)

Tool Purpose
mail_save_attachment Save one attachment's raw bytes into DIR.
mail_export_gource Write a Gource custom log of mailbox history into DIR.

mail_export_gource writes one line per message, timestamp\|username\|type\|path\|colour, sorted oldest first (Gource requires this). path is folder/normalized-subject, so a whole reply chain lands at one point in the tree; colour is a stable hash of the folder name, so a folder keeps its colour across repeated exports. Play it back with:

$ gource --log-format custom mail.gource -s 0.5 --key

limit (default 100000) matters: feeding Gource every mailing-list message you've ever received produces an unwatchable animation, so scope the query first.

Security model

This is a mail server handed to an LLM; message content is not trusted the way your own instructions are.

  1. Prompt injection. Message bodies, attachment text, calendar summaries, and thread overview lines are third-party content, not instructions. render.py wraps every one of them in explicit -----BEGIN/END UNTRUSTED EMAIL CONTENT----- markers with a notice that nothing inside should be treated as a command, so no individual tool can forget to do this.
  2. Path confinement. The drafts maildir (compose.py) and the export directory (export.py) each resolve the target path and verify it is still inside the configured root afterward. This catches a literal .. and a symlink pointing outside the root (Path.resolve() follows symlinks), and mail_create_draft/mail_update_draft cannot be made to write outside the configured maildir with any combination of arguments.
  3. No shell, ever. Every subprocess call is subprocess.run([...], shell=False) with an argv list; queries are passed as a single argv element, never interpolated into a shell string or a notmuch query string beyond normal AND/OR composition.
  4. No content in diagnostics. Errors and progress go to stderr and are content-free (a byte count or a file path, never a message body).
  5. Never silently unfiltered. A scope argument that AND-composes with a query is always explicit; there is no hidden "search everything" fallback unless a scope literally named all is configured.

notmuch query syntax

query/scope arguments accept full notmuch search syntax: from:, to:, subject:, tag:, date: ranges, boolean and/or/not, and more. See notmuch-search-terms(7) (man notmuch-search-terms) for the complete reference.

SDK version

Targets MCP spec 2026-07-28 and pins mcp==2.0.0b2, a pre-release of the Python SDK built for that spec. Once the spec and a matching stable SDK release ship, this pin moves to the stable release; until then, every install (uv pip install, uvx) needs --prerelease=allow.

Development

$ make          # fmt + lint + test
$ make test     # pytest (skips cleanly if notmuch is not installed)
$ make lint     # ruff format --check + ruff check
$ make help     # list all targets

Tests build a small crafted maildir and run real notmuch commands against it; nothing touches your real mail. CI runs on Python 3.11, 3.12 and 3.13 with notmuch installed via apt.

License

MIT, see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选