postbus-mcp
postbus-mcp
一个自托管的 MCP 服务器,让你和你身边的几个人可以通过 Claude 或其他任何 MCP 客户端处理你的邮箱:搜索、阅读完整会话,以及发送邮件。
适用于任何 IMAP/SMTP 提供商——Gmail、Outlook、Fastmail,甚至你自己的邮件服务器——只需一个普通的应用密码。无需 Google Cloud 项目,无需 OAuth 验证,也没有测试用户限制。
一个实例可服务多个用户。每个人都有自己的 API 令牌,并且只能看到自己的邮箱。由你托管,由你分发令版;没有开放注册。
Claude / MCP client
│ Authorization: Bearer <token>
▼
POST /mcp ──► postbus-mcp ──► SQLite (users + encrypted app passwords)
│
├──► IMAP (imapflow) search, read, threads
└──► SMTP (nodemailer) sending目录
Related MCP server: simple-email-mcp
工作原理
多租户,但体积小。 一个 SQLite 文件包含两张表:users(id 以及 API 令牌的哈希)和 mail_accounts(每个用户的邮箱,应用密码已加密)。无需单独运行数据库服务。
隔离体现在查询中,而不是事后的检查中。 每个 MCP 会话恰好属于一个用户,由 Bearer 令牌决定。MCP 服务器针对每个请求围绕该用户构建,并且每个数据库查询都在其 WHERE 中携带 user_id。别人的别名在你的会话中根本不存在。
提供商接口。 工具层与通用的 MailProvider 交互,不关心 IMAP 或 Gmail。ImapSmtpProvider 是主要实现,可选配 GmailApiProvider。添加第三个提供商无需改动工具层——参见 添加提供商。
快速开始
使用 Docker(推荐)
git clone https://github.com/HalloSouf/postbus-mcp.git
cd postbus-mcp
cp .env.example .env
openssl rand -hex 32 # put the result in .env as MASTER_KEY
docker compose up -d --build
docker compose exec postbus node dist/cli/add-user.js "Soufiane"最后一条命令会恰好打印一次 API 令牌。请立即保存。
在本地使用 Node(22 或更高版本)
npm install
cp .env.example .env
openssl rand -hex 32 # put the result in .env as MASTER_KEY
npm run build
npm run add-user -- "Soufiane"
npm start服务器监听 http://localhost:3000/mcp。GET /health 返回 {"status":"ok"},方便做存活检查。
用户和令牌
令牌由你自己分发;没有自助注册。
命令 | 作用 |
| 创建用户并打印令版(仅一次) |
| 显示用户、邮箱数量和状态 |
| 生成新令版;旧令版立即失效 |
| 删除该用户及其所有邮箱 |
在 Docker 中,以 node dist/cli/<script>.js 方式运行相同的脚本:
docker compose exec postbus node dist/cli/list-users.js
docker compose exec postbus node dist/cli/rotate-token.js WvDnhafdM5yQ每个令版仅存储 SHA-256 哈希,因此丢失的令版无法找回——请改为轮换令版。
连接你的客户端
Claude Desktop
Claude Desktop 使用 stdio,因此需要在中间加上 mcp-remote。在 claude_desktop_config.json 中:
{
"mcpServers": {
"postbus": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.example.com/mcp",
"--header",
"Authorization: Bearer pb_YOUR_TOKEN_HERE"
]
}
}
}该文件在 macOS 上位于 ~/Library/Application Support/Claude/claude_desktop_config.json,在 Windows 上位于 %APPDATA%\Claude\claude_desktop_config.json。编辑后请重启 Claude Desktop。
Claude Code
claude mcp add --transport http postbus https://mcp.example.com/mcp \
--header "Authorization: Bearer pb_YOUR_TOKEN_HERE"其他客户端
任何支持 Streamable HTTP 的客户端都可以工作:端点 POST /mcp,令版作为 Authorization: Bearer <token>。服务器是无状态的——没有会话 ID,也没有服务器端流——因此 GET /mcp 会刻意返回 405。
关联邮箱
你在对话中用自己的令版完成此操作,无需终端:
把我的 Gmail 关联为“个人”,地址 souf@gmail.com,应用密码 abcd efgh ijkl mnop
Claude 随后会调用 add_mail_account。首先会测试连接(IMAP 和 SMTP 都测试);只有两者都成功才会存储任何信息。
创建应用密码
提供商 | 位置 | 说明 |
Gmail / Workspace | 需要账号开启两步验证 | |
Outook / Microsoft 365 | 需要两步验证;管理员可能阻止 IMAP | |
Fastmail | 设置 → 隐私与安全 → 应用密码 | 选择“邮件(IMAP/SMTP)” |
iCloud | https://account.apple.com → 应用专用密码 | 需要两步验证 |
自有服务器 | 不适用 | 你的邮件密码,或一个专用账号 |
当提供商提供应用密码时,切勿使用你的常规密码。
主机和端口
对于已知提供商,postbus-mcp 会自动填写这些信息——你只需提供别名、邮箱和应用密码:
Gmail、Google Workspace、Outlook、Hotmail、Microsoft 365、Fastmail、iCloud、Yahoo、Zoho、Proton(通过 Bridge)。
对于其他提供商,请自己传入这些信息:
imap_host: imap.yourdomain.com imap_port: 993 (TLS)
smtp_host: smtp.yourdomain.com smtp_port: 465 (TLS) or 587 (STARTTLS)端口 993 和 465 从一开始就使用 TLS;在其他端口上,如果服务器支持,则使用 STARTTLS。如果该假设不适用于你的服务器,请显式传入 imap_secure 或 smtp_secure。
可用工具
工具 | 作用 |
| 列出你的邮箱,包含别名和邮件地址 |
| 关联一个 IMAP/SMTP 邮箱和应用密码(先测试连接) |
| 解除邮箱关联并清除已存储的应用密码 |
| 使用 Gmail 风格语法搜索;每条消息返回一个 |
| 获取单条消息的完整内容:头部、正文、附件元数据 |
| 获取会话中的所有消息,按时间从旧到新 |
| 立即发送新消息(支持 cc、bcc、reply-to、html) |
每个工具都只会访问令牌所属用户的邮箱。
搜索语法
search_emails 使用 Gmail 风格的语法。对于 Gmail 邮箱,你的查询会原样发送给 Gmail(通过 X-GM-RAW),因此 Gmail 搜索栏中可用的任何语法在这里都可用。对于其他 IMAP 服务器,查询会被转换:
术语 | Gmail | 其他 IMAP |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅(事后过滤) |
| ✅ | ✅(通过 SPECIAL-USE) |
| ✅ | ✅ |
| ✅ | ⚠️ 合并为一个文本条件 |
| ✅ | ❌ 忽略 |
示例:
from:boss@company.com is:unread newer_than:7d
subject:"march invoice" has:attachment
in:sent to:client@example.com older_than:1m空查询会返回收件箱中最新的消息。
会话线程
每个 search_emails 结果都带有 threadId,get_thread 使用它来拉取整个会话——按时间顺序排列,包含每条消息的发件人、主题、日期和正文。
根据服务器可用的能力,有两种方式:
Gmail(
X-GM-THRID)和 RFC 8474 服务器(OBJECTID) 会自行提供稳定的线程 ID。我们直接使用它,threadId看起来像srv:1829384756。其他所有 IMAP 服务器 没有线程概念。在这种情况下,我们根据标准的
Message-ID、In-Reply-To和References头重建会话:该链中的第一个 id 是线程的根。这些threadId值以ref:开头。
获取时,如果服务器有“所有邮件”文件夹,我们会从那里查找;否则会在收件箱、已发送和归档中查找——这样你自己的回复也会出现在会话中。
在 Traefik 后面部署
此仓库中的 docker-compose.yml 是一个可运行的示例。其核心内容:
services:
postbus:
build: .
restart: unless-stopped
environment:
MASTER_KEY: ${MASTER_KEY:?set MASTER_KEY in .env}
DATABASE_PATH: /data/postbus.db
TRUST_PROXY: "true"
volumes:
- postbus-data:/data
networks: [proxy]
labels:
traefik.enable: "true"
traefik.docker.network: proxy
traefik.http.routers.postbus.rule: Host(`${PUBLIC_HOST:-mcp.example.com}`)
traefik.http.routers.postbus.entrypoints: websecure
traefik.http.routers.postbus.tls.certresolver: letsencrypt
traefik.http.services.postbus.loadbalancer.server.port: "3000"需要注意的事项:
在
.env中将PUBLIC_HOST设置为你自己的主机名;这是域名唯一出现的位置,因此 compose 文件本身无需改动。proxy网络必须存在(docker network create proxy),并且 Traefik 必须连接到此网络。容器本身不发布任何端口:只有 Traefik 可以访问它。
TRUST_PROXY=true让 Express 信任X-Forwarded-*请求头。在 Traefik 处终止 TLS。令牌以 Bearer 凭证形式传输;没有 HTTPS 就会明文传输。
postbus-data卷保存着包含所有加密应用密码的数据库。请将其与MASTER_KEY一同备份——但分开存放。
安全性
MASTER_KEY。 应用密码和刷新令牌使用 AES-256-GCM 存储,每条都有独立的 IV。没有该密钥,服务器拒绝启动。如果丢失,所有人都需要重新关联邮箱,因此请将其与数据库备份分开保存。
令牌。 仅存储 SHA-256 哈希。通过你信任的渠道分享它们,有疑虑时进行轮换(npm run rotate-token)。
隔离。 对 mail_accounts 的每个查询都按 user_id 过滤,并且 MCP 服务器为每个请求围绕单个用户构建,因此不存在可能混淆用户的会话存储。
这不包括什么。 没有速率限制、没有审计日志、没有细粒度权限。这是为你认识的几个人在 TLS 后面使用的。不要将其开放给未知受众。
添加提供商
工具层只与 src/types.ts 中的 MailProvider 交互:
interface MailProvider<A extends MailAccount = MailAccount> {
readonly id: ProviderId;
verify(account: A): Promise<void>;
search(account: A, query: string, maxResults: number): Promise<MessageSummary[]>;
getMessage(account: A, messageId: string): Promise<MessageDetail>;
getThread(account: A, threadId: string): Promise<MessageDetail[]>;
send(
account: A,
to: string,
subject: string,
body: string,
options?: SendOptions,
): Promise<string>;
}提供商收到的是完全解析后的账户,凭据已解密。别名查找发生在工具层,因此提供商无法访问会话用户以外的数据。
要添加一个提供商:
扩展
src/types.ts中的ProviderId和MailAccount联合类型。编写
src/providers/<name>/provider.ts,其中包含一个实现该接口的类。在
src/providers/registry.ts的映射表中添加一行。确保该类型的账户可以访问数据库:在
src/db/accounts.ts中添加save<Name>Account()(机密通过encryptSecret处理),并提供关联方式——在add_mail_account旁边添加一个额外工具,或提供一个 CLI 脚本。
现有工具(search_emails、get_message、get_thread、send_email)无需改动。另请参阅 CONTRIBUTING.md。
可选:通过 API 而非 IMAP 使用 Gmail
在 https://console.cloud.google.com 创建项目。
API 和服务 → 库 → 搜索 "Gmail API" → 启用。
API 和服务 → OAuth 同意屏幕 → 类型选为 外部 → 填写名称和支持邮箱。
在 测试用户 下添加要关联的地址。
凭据 → 创建凭据 → OAuth 客户端 ID → 类型选为 桌面应用。
将客户端 ID 和客户端密钥放入
.env:GOOGLE_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx OAUTH_CALLBACK_PORT=53682关联一个邮箱。此操作在管理员的机器上运行,因为 Google 会将回调发送到
localhost:npm run list-users # look up the user id npm run link-gmail -- <user-id> work
使用的范围:gmail.readonly、gmail.send、gmail.compose、gmail.labels。
注意: 当 OAuth 同意屏幕设置为 测试 时,刷新令牌会在 7 天后过期,你必须重新关联。这种情况只有在同意屏幕变为 已发布 后才会停止,而对这些范围而言,发布需要 Google 验证。这正是 IMAP 搭配应用专用密码成为主要路径的原因。
开发
npm install
npm run dev # server with hot reload (tsx watch)
npm test # unit tests (vitest)
npm run typecheck # src + tests
npm run format # prettier across the repo
npm run build # into dist/tests/ 中的测试在半秒内运行完毕,并且不会触及进程之外的任何内容:SQLite 在内存中运行,没有任何连接离开机器。它们覆盖了那些可能悄然出错的逻辑——搜索查询转换、消息和线程 ID 的编码、MIME 的解析与组装、加密存储、用户之间的隔离,以及 bearer 中间件。
它们不覆盖的是与真实邮件服务器的通信。为此,请在本地运行 GreenMail:
docker run -d --rm --name greenmail -p 3143:3143 -p 3025:3025 \
-e GREENMAIL_OPTS='-Dgreenmail.setup.test.imap -Dgreenmail.setup.test.smtp -Dgreenmail.users=souf:secret@postbus.test -Dgreenmail.hostname=0.0.0.0' \
greenmail/standalone:2.1.0然后用 imap_host: 127.0.0.1、imap_port: 3143、smtp_host: 127.0.0.1、smtp_port: 3025、username: souf、app_password: secret 关联一个邮箱。
GreenMail 不支持 Gmail 扩展。使用
X-GM-RAW和X-GM-THRID的代码分支只能针对真实的 Gmail 邮箱进行测试。
GitHub Actions 会在每次 push 和 pull request 上运行同样的检查:格式化、类型、对生产依赖执行 npm audit、测试,以及一个 docker 构建——它会启动容器,并验证 /health 有响应,且在没有令牌的情况下 /mcp 返回 401。CI 和容器都运行 Node 24,即当前的 LTS。
项目结构
src/
├── index.ts startup: check MASTER_KEY, open the db, listen
├── config.ts environment configuration
├── crypto.ts AES-256-GCM for secrets, hashing for tokens
├── types.ts MailProvider plus every shared type
├── db/ SQLite: migrations, users, mail_accounts
├── http/ Express app, bearer auth, MCP transport per request
├── providers/
│ ├── registry.ts account -> provider
│ ├── imap/ IMAP/SMTP: connections, search, threading, sending
│ └── gmail/ optional Gmail API provider (OAuth)
├── tools/ the MCP tools (they know no provider)
└── cli/ admin scripts: users and tokens
tests/ unit tests (vitest), mirroring the layout of src/许可证
MIT — 参见 LICENSE。
This server cannot be deployed
Maintenance
Related MCP Connectors
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Email inboxes for AI agents: send, receive, reply, search, and manage threaded email over MCP.
Hosted email MCP for your own Gmail, Outlook.com, Microsoft 365, iCloud or IMAP inbox: read, search, draft, reply in thread, forward and file mail. It moves or flags up to 500 messages in one call, and a send leaves exactly one copy in Sent. A calendar is a separate connection, and connecting one adds diary and scheduling tools.
Hosted MCP server for your Gmail, Outlook and IMAP mail, with mailboxes and scopes set per agent.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables reading and sending emails via IMAP and SMTP through the MCP protocol. Supports multiple email accounts and configuration via UI or environment variables.BSD 3-Clause
- AlicenseBqualityBmaintenanceEnables users to manage email accounts via IMAP/SMTP, including reading, searching, sending emails with attachments and calendar invites, all through natural language interactions with MCP-compatible clients.132 PyPI4MIT
- AlicenseBqualityAmaintenanceConnects MCP clients to any IMAP/SMTP email account, enabling email search, reading, sending, replying, forwarding, flagging, moving, and folder management via natural language.17MIT
- AlicenseBqualityCmaintenanceEnables email integration through MCP, including sending, receiving, searching, replying to, and forwarding emails, plus folder management and multi-account support with encrypted credential storage.401,013 npmMIT