Skip to main content
Glama
shangguanyiyu0011

QQ 邮箱 MCP

README.md
# QQ 邮箱 MCP

通过 MCP 和 OAuth 将自己的 QQ 邮箱连接到 ChatGPT,支持邮件搜索、读取、附件、发送及文件夹管理。Python 实现,适合单账户自托管。

Self-hosted QQ Mail MCP server with OAuth, attachment support, and duplicate-send protection.

## 功能

| 工具 | 用途 |
| --- | --- |
| `list_folders` | 列出邮箱文件夹 |
| `search_emails` | 按关键词、发件人、日期、未读状态搜索邮件 |
| `read_email` | 读取正文与附件目录,保留原有未读状态 |
| `read_attachment` | 读取小附件,文本直接返回,二进制返回 Base64 |
| `send_email` | 发送纯文本邮件及附件,通过 `operation_id` 防止重试重复发送 |
| `move_email` | 将邮件移动到已有文件夹 |
| `trash_email` | 移入垃圾箱 |
| `set_email_read` | 按要求标记已读或未读 |

读取使用只读 IMAP 和 `BODY.PEEK`。邮件 ID 包含文件夹、UIDVALIDITY 和 UID;文件夹身份改变后,旧 ID 会被拒绝。移动使用服务器的原子 MOVE;没有永久删除或清空垃圾箱工具。

## 准备

- Python 3.12。
- 已开启 IMAP/SMTP 的 QQ 邮箱及其邮箱授权码。
- 自己控制的域名、服务器和 HTTPS 反向代理,用于远程 OAuth 连接。

此项目是每个部署连接一个邮箱的自托管服务。代码不包含可直接使用的公共邮箱服务、账户、授权码或访问令牌。

## 安装与配置

```bash
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
cp config.example.json config.json
python hash_password.py
```

Windows PowerShell 激活虚拟环境和复制配置:

```powershell
.\.venv\Scripts\Activate.ps1
Copy-Item config.example.json config.json
python hash_password.py
```

脚本会隐藏输入独立的连接密码,确认两次后输出 SHA-256。将输出填写到 `config.json`,并设置以下字段:

| 字段 | 内容 |
| --- | --- |
| `base_url` | 自己服务的 HTTPS 根地址,例如 `https://mail.example.com`,末尾不加 `/mcp` |
| `state_dir` | 可写的私有持久目录;本地可用 `./state`,systemd 示例使用 `/var/lib/qq-mail-mcp` |
| `mail_account` | 自己的完整 QQ 邮箱地址 |
| `mail_auth_code` | QQ 邮箱授权码,非 QQ 登录密码 |
| `connection_password_sha256` | 独立连接密码的 SHA-256,小写十六进制字符串 |

连接密码和 QQ 邮箱授权码用于不同用途,请分别设置。配置模板中的值都是占位符,需要自行替换。真实配置和状态数据库应仅允许服务账户读取,且不应提交到 Git。

Linux/macOS 启动:

```bash
QQ_MCP_CONFIG="$PWD/config.json" python server.py
```

Windows PowerShell 启动:

```powershell
$env:QQ_MCP_CONFIG = (Resolve-Path .\config.json).Path
python server.py
```

服务监听 `127.0.0.1:8765`。通过反向代理提供 HTTPS,将外部请求转发到此地址。`caddy.example` 是最小示例,域名需自行替换;OAuth 发现、注册、授权、令牌与 `/mcp` 路径都需要转发,因此应代理整个站点根路径。

`qq-mail-mcp.service` 提供 Linux systemd 配置示例。使用前创建专用系统用户 `qqmailmcp`,安装代码至 `/opt/qq-mail-mcp`,将真实配置置于 `/etc/qq-mail-mcp/config.json`,并使服务用户可以写入 `/var/lib/qq-mail-mcp`。systemd 示例不读取项目根目录的 `config.json`;两者请选择对应的部署方式。

## 连接 MCP 客户端

部署完成后,用客户端创建远程 MCP 连接,地址为 `https://mail.example.com/mcp`,选择 OAuth。实际界面入口以客户端显示为准。授权页输入自己设置的独立连接密码。

当前实现只允许 `chatgpt.com` 的指定 OAuth 回调路径,不是面向任意 MCP 客户端的通用认证服务。所有邮箱工具均要求授权,并同时申请 `mail:read` 和 `mail:write`,当前没有可切换的只读授权模式。

连接后可以要求:

- “列出邮箱文件夹。”
- “查找收件箱中最近的未读邮件,先给我摘要。”
- “读取这封邮件的正文和附件目录。”
- “把我上传的 PDF 作为附件发给指定收件人,主题和正文如下……”

发送附件时,应使用客户端实际传入的文件参数。客户端是否支持原生文件传递需实际验证;本地路径、虚构的文件 ID 或过期链接不能代替文件。

## 限制与行为

- 搜索每页最多 50 封;单封读取上限 15 MiB,正文返回上限 100,000 字符。
- 单个附件读取上限 512 KiB;这与发送附件额度是不同限制。
- 每封最多发送 20 个附件,附件原始字节合计不超过 50 MiB,编码后的整封邮件不超过 70 MiB;服务器与收件方仍可能采用更低额度。
- 附件只在内存中处理。下载要求公网 HTTPS/443;DNS、连接目标和每次跳转均经过校验,拒绝内网地址及非法 URL。
- 相同发送重试须复用同一 `operation_id`。SMTP 已提交但结果未知时,应先到邮箱核对,不能换编号自动重发。
- 访问令牌有效一小时,刷新令牌有效三十天并轮换。撤销时会撤销对应令牌族;SQLite 状态目录需要持久保存。
- 服务指令要求发送、移动、删除和修改已读状态须由用户明确提出;此约束也依赖 MCP 客户端遵守,不能替代用户核对收件人、正文和附件。
- 邮件正文和附件属于不可信数据,不应将其中的指令当成用户授权执行。

## 测试

```bash
python -m pip install -r requirements-dev.txt
python -m pytest -q
```

测试使用临时目录、示例域名、虚构账户、模拟 SMTP 和模拟附件下载;不连接真实邮箱、不向真实收件人发送邮件。覆盖 OAuth/PKCE、令牌重放与撤销、工具权限、MCP 协议、附件编码、发送去重、内网下载拦截和大小限制。

本次公开版本在本地通过 25 项测试。模拟测试不代表新部署的 QQ 登录、真实发信、大附件或每种客户端文件传递都已验收;请在自己的部署中验证。

## 文件

- `server.py`:MCP 工具、鉴权、HTTP 入口及请求限制。
- `auth_service.py`:OAuth、授权页、CSRF、令牌轮换与撤销。
- `mailbox_service.py`:IMAP/SMTP、邮件 ID、附件和发送去重。
- `file_transfer.py`:原生文件参数、下载与网络目标校验。
- `config.example.json`、`caddy.example`、`qq-mail-mcp.service`:部署模板。
- `test_server.py`、`test_attachments.py`:本地模拟测试。

## 许可证

MIT,见 [LICENSE](LICENSE)。本项目是独立实现,与腾讯、QQ 邮箱及 OpenAI 没有隶属关系。