用途
mcp-server-notmuch 是一个 MCP 服务器,把本地 notmuch 邮件数据库暴露给 Claude 等 LLM 客户端。它的定位是“只读优先”:搜索、阅读和理解邮件始终可用;任何写入操作(草稿、标签、导出文件)都需要显式开启标志,并限制在明确边界内。
它从不发送邮件。代码库中没有任何发送能力,任何模式、任何标志下都没有。草稿写入本地 maildir,由用户自行在真实邮件客户端中审阅并发送。
主要能力
- 通过真实的 notmuch CLI 搜索和阅读邮件,包括线程、单条消息、附件、日历邀请、办公文档和图片。
- 理解作用域:一个命名的、预配置的 notmuch 查询(例如“个人邮件”与“邮件列表”),除非另有要求,每次搜索都限定在该作用域内。
- 回答“哪些还没回复”和“谁欠我回复”(mail_pending)、“这件事以前出现过吗”(mail_related_threads),并在阅读整个长线程之前提供低 token 的概览(mail_thread_overview)。
- 可选地撰写和修改纯文本草稿(--allow-drafts)、给邮件打标签(--allow-tags),或把附件和邮箱历史的 Gource 可视化导出到指定目录(--allow-export DIR)。
它不做什么
- 它不发送邮件,永远不。
- 除非传入 --allow-tags(打标签)或 --allow-drafts(向草稿 maildir 写入新文件),它不以任何方式修改邮件。这两个标志都不允许它触碰现有邮件的内容。
- 它不在 notmuch 数据库、配置的草稿 maildir 以及(仅在 --allow-export 时)配置的导出目录之外读写。
- 它不要求也不使用 notmuch2 Python 绑定,因此安装不需要编译器。
安装与系统要求
从 PyPI 安装(发布后):需要 --prerelease=allow,因为项目固定了 mcp SDK 的预发布版本,这不是可选项。
系统要求:
- notmuch(CLI,不只是库)在 PATH 中,或通过配置中的 notmuch.binary 指向。
- poppler-utils(pdftotext)用于读取 PDF 附件。没有它时,对 PDF 调用 mail_read_attachment 会提示缺失的包。
- pandoc 或 libreoffice 用于读取办公文档(doc/docx/odt/rtf)。两者都没有时,错误会同时列出两个选项。
- 可选:Pillow(pip install 'mcp-server-notmuch[image]')让过大的图片附件被缩小而不是拒绝。
- 可选:Gource 用于实际播放 mail_export_gource 写出的日志。
使用方式
MCP 客户端配置:
- 只读(默认:仅搜索和阅读工具)。
- 启用草稿(也允许按需修改/打标签)。
- 或在 .mcp.json 中手动配置。
- 添加到 claude_desktop_config.json(只读默认)。
- 启用草稿。
服务器读取 $XDG_CONFIG_HOME/mcp-server-notmuch/config.toml(未设置 XDG_CONFIG_HOME 时为 ~/.config/mcp-server-notmuch/config.toml),或通过 --config 给出的路径。文件缺失不是错误:服务器回退到系统 notmuch 二进制和一个内置作用域 all,查询为空。一旦文件存在,[scopes] 就是权威的,all 不再被隐含。
参见 config.example.toml 获取完整注释的参考文件。mail_create_draft / mail_update_draft 除非同时设置 drafts.maildir 和 drafts.from,否则拒绝运行。mail_pending direction="waiting" 需要 [identity] addresses(或可读的 notmuch user.primary_email)来知道哪个地址是“你”。
默认不截断任何内容。这是本地快速邮箱,不是限速 API:mail_search、mail_pending、mail_related_threads、mail_list_addresses、mail_find_attachments 和 mail_export_gource 都返回每一行匹配结果,除非工具调用传入显式 limit;消息正文/附件文本完整返回,除非设置 limits.max_body_chars / limits.max_attachment_chars。每当显式 limit 或字符上限确实截断内容时,输出会说明:“Showing N of TOTAL ...”并提示省略 limit 以查看全部。服务器中没有任何静默的、不可见的上限。
作用域解析:工具的 scope 参数命名一个已配置作用域;其查询与调用者的查询做 AND 组合,两侧都加括号((scope_query) and (user_query)),因此任一侧的 or 都无法泄漏到另一侧。未知作用域名称会报错并列出已配置作用域;scope="all" 永远不会被静默地不加过滤,除非你定义了一个字面名为 all 的作用域。
层级与工具
四个层级。读取层级始终注册。其他三个仅在传入对应标志时注册——不存在“已注册但被拒绝”的状态,未授权的工具根本不会出现在客户端看到的工具列表中。
- 读取层级(始终开启)
- 草稿层级(--allow-drafts)
- 标签层级(--allow-tags)
- 导出层级(--allow-export DIR)
mail_export_gource 每条消息写一行,格式为 timestamp|username|type|path|colour,按最旧优先排序(Gource 要求如此)。path 是 folder/normalized-subject,因此整个回复链落在树中的同一个点;colour 是文件夹名称的稳定哈希,因此文件夹在重复导出中保持颜色。用以下命令播放:
默认包含所有匹配消息;当查询宽泛时 limit 很重要,因为把收到的每封邮件列表消息都喂给 Gource 会产生无法观看的动画,所以先限定查询或显式设置 limit。
安全与限制
这是交给 LLM 的邮件服务器;消息内容不像你自己的指令那样可信。
- 提示注入。消息正文、附件文本、日历摘要和线程概览行是第三方内容,不是指令。render.py 把每一个都包裹在显式的 -----BEGIN/END UNTRUSTED EMAIL CONTENT----- 标记中,并附有说明:内部任何内容都不应被视为命令,因此没有单个工具会忘记这样做。
- 路径限制。草稿 maildir(compose.py)和导出目录(export.py)各自解析目标路径,并在之后验证它仍在配置的根目录内。这能捕获字面 .. 和指向根目录外的符号链接(Path.resolve() 会跟随符号链接),并且 mail_create_draft / mail_update_draft 无法通过任何参数组合被诱使写入配置 maildir 之外。
- 永不使用 shell。每个子进程调用都是 subprocess.run([...], shell=False) 加 argv 列表;查询作为单个 argv 元素传递,绝不插值到 shell 字符串或超出正常 AND/OR 组合的 notmuch 查询字符串中。
- 诊断中无内容。错误和进度输出到 stderr,且不含内容(字节数或文件路径,绝不是消息正文)。
- 永不静默地不加过滤。与查询做 AND 组合的 scope 参数始终显式;除非配置了字面名为 all 的作用域,否则没有隐藏的“搜索一切”回退。
- 永不静默截断。这是本地快速邮箱:结果行、消息正文和附件文本完整返回,除非工具调用传入显式 limit 或配置设置显式 limits.max_body_chars / limits.max_attachment_chars。每当这些确实截断内容时,输出会说明真实总数以及如何查看其余部分,绝不静默截断。
notmuch 查询语法
query / scope 参数接受完整的 notmuch 搜索语法:from:、to:、subject:、tag:、date: 范围、布尔 and / or / not 等。完整参考见 notmuch-search-terms(7)(man notmuch-search-terms)。
版本与测试
目标 MCP 规范 2026-07-28,并固定 mcp==2.0.0b2,这是为该规范构建的 Python SDK 预发布版。一旦规范和匹配的稳定 SDK 发布,该固定将移到稳定版;在此之前,每次安装(uv pip install、uvx)都需要 --prerelease=allow。
测试构建一个小型手工 maildir 并对其运行真实 notmuch 命令;不触碰你的真实邮件。CI 在 Python 3.11、3.12 和 3.13 上运行,notmuch 通过 apt 安装。
MIT 许可,见 LICENSE。