Metadata-Version: 2.4
Name: netease163-mail-mcp
Version: 1.0.0
Summary: NetEase Mail (163/126/yeah/qiye) MCP Server for WorkBuddy and other MCP clients
Author: 李洋万鲲
License-Expression: MIT
Keywords: mcp,netease,mail,163,imap,smtp
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Communications :: Email
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp<2,>=1.2.0
Dynamic: license-file

# 网易邮箱 MCP 连接器（Netease Mail MCP Server）

一个基于**官方 MCP SDK** 的**标准 MCP Server（stdio 传输）**，让 WorkBuddy / Claude 等
AI 客户端可以直接读写你的网易邮箱（163 / 126 / yeah / 企业邮）。

功能范围：**读写为基础** —— 浏览文件夹与邮件列表、搜索、读正文、下载附件、发送邮件、批量标记已读。

- 语言 / 运行时：**Python 3.10+**（开发环境为 3.13，Windows）
- 传输协议：**stdio**
- 唯一必需依赖：**`mcp`**（邮件协议只用标准库 `imaplib` / `smtplib` / `email` / `ssl`）

---

## 1. 快速开始

### 1.1 安装依赖

```bash
cd netease-mail-mcp
python -m pip install -r requirements.txt
```

> `python-dotenv` 为可选项：未安装时程序会自动跳过 `.env` 加载，不影响运行。
>
> ⚠️ **MCP SDK 版本必须为 1.x（`mcp>=1.2.0,<2`）**：`mcp` 2.x 已移除
> `mcp.server.fastmcp`（`FastMCP` 更名为 `MCPServer`），本项目基于 1.x 的 FastMCP API 实现。
> `requirements.txt` 已锁定上界；若被其它依赖升级到 2.x，`server.py` 会在 import 阶段报
> `ModuleNotFoundError: No module named 'mcp.server.fastmcp'`，请执行
> `python -m pip install "mcp>=1.2.0,<2"` 降级。

### 1.2 获取客户端授权码（关键）

网易邮箱必须使用**客户端授权码**（不是登录密码）作为认证凭据。请先在网页版邮箱开启
IMAP / SMTP 服务并生成授权码：

**个人邮箱（163 / 126 / yeah）**
1. 登录网页版邮箱（如 <https://mail.163.com>）
2. 进入 **设置 → POP3/SMTP/IMAP**
3. 开启 **IMAP/SMTP 服务**
4. 按提示生成 **16 位客户端授权码**（请妥善保存，仅显示一次）

**企业邮箱（qiye.163.com）**
1. 登录企业邮箱网页版
2. 进入 **设置 → 账户与安全 → 客户端设置**
3. 开启 IMAP / SMTP 服务并生成授权码

### 1.3 配置环境变量

复制样例文件并填写：

```bash
cp .env.example .env
```

编辑 `.env`：

```ini
NETEASE_EMAIL=yourname@163.com
NETEASE_AUTH_CODE=abcdnfghijklmnop
NETEASE_MAIL_TYPE=163
```

也可以在 MCP 客户端配置里用 `env` 直接注入（推荐，见下一节）。

### 1.4 连通性自检（可选但推荐）

```bash
python scripts/smoke_test.py
# 如需顺带验证发信：
NETEASE_SMOKE_TO=yourname@163.com python scripts/smoke_test.py
```

---

## 2. 在 MCP 客户端中配置

### WorkBuddy / Claude Desktop（`mcp.json`）

```json
{
  "mcpServers": {
    "netease-mail": {
      "command": "python",
      "args": [
        "C:\\Users\\MECHREU\\WorkBuddy\\2026-09-23-10-21-00\\netease-mail-mcp\\server.py"
      ],
      "env": {
        "NETEASE_EMAIL": "yourname@163.com",
        "NETEASE_AUTH_CODE": "abcdnfghijklmnop",
        "NETEASE_MAIL_TYPE": "163"
      }
    }
  }
}
```

> Windows 下路径请使用双反斜杠或正斜杠。若 `python` 不在 PATH 中，请填写解释器绝对路径
> （例如 `C:\\Python313\\python.exe`）。

配置完成后重启客户端，即可看到 `netease-mail` 提供的工具。

---

## 3. 环境变量说明

| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| `NETEASE_EMAIL` | ✅ | — | 登录邮箱完整地址，同时作为发信人 From |
| `NETEASE_AUTH_CODE` | ✅ | — | 客户端授权码（16 位），**非登录密码** |
| `NETEASE_MAIL_TYPE` | ❌ | `163` | `163` \| `126` \| `yeah` \| `qiye` |
| `NETEASE_IMAP_HOST` | ❌ | 自动映射 | 手动覆盖 IMAP 地址 |
| `NETEASE_IMAP_PORT` | ❌ | `993` | IMAP 端口（SSL） |
| `NETEASE_SMTP_HOST` | ❌ | 自动映射 | 手动覆盖 SMTP 地址 |
| `NETEASE_SMTP_PORT` | ❌ | `465` | SMTP 端口（SSL） |
| `NETEASE_DEFAULT_FOLDER` | ❌ | `INBOX` | 默认邮箱文件夹 |
| `NETEASE_ATTACHMENT_DIR` | ❌ | `./downloads` | 附件默认下载目录 |
| `NETEASE_TIMEOUT` | ❌ | `30` | 网络操作超时（秒） |

服务器地址自动映射：

| 类型 | IMAP Host | SMTP Host |
|---|---|---|
| `163`（默认） | imap.163.com | smtp.163.com |
| `126` | imap.126.com | smtp.126.com |
| `yeah` | imap.yeah.net | smtp.yeah.net |
| `qiye` | imap.qiye.163.com | smtp.qiye.163.com |

> **重要**：缺少 `NETEASE_EMAIL` / `NETEASE_AUTH_CODE` 时，服务**仍可正常启动**，
> 只有在实际调用工具时才会返回「请先配置 …」的提示。这样可避免在客户端里因配置
> 未就绪而导致服务启动失败。

---

## 4. 可用工具（7 个）

| 工具 | 说明 | 主要参数 |
|---|---|---|
| `list_folders` | 列出全部文件夹（名称已解码为可读中文） | 无 |
| `list_messages` | 邮件摘要列表（时间倒序） | `folder="INBOX"`, `limit=20`, `offset=0`, `unread_only=False` |
| `search_messages` | 条件检索（各条件为「与」） | `query`, `folder`, `limit`, `unread_only`, `since`, `before`, `from_addr`, `subject` |
| `get_message` | 读取单封邮件正文 + 附件清单 | `uid`, `folder="INBOX"`, `include_html=False` |
| `download_attachments` | 下载附件到磁盘 | `uid`, `folder`, `save_dir`, `filenames` |
| `send_message` | 发送邮件（纯文本 / HTML / 附件） | `to`, `subject`, `body`, `cc`, `bcc`, `html`, `attachments` |
| `mark_messages` | 批量标记已读 / 未读 | `uids`, `folder`, `read=True` |

要点：

- 所有列表类工具返回 **UID**（非 message sequence number），避免序号变化导致误操作。
- `list_messages` / `search_messages` 中 `from` / `to` 为结构化地址数组
  `[{"name": ..., "address": ...}]`。
- `get_message` 正文最长返回 **100000 字符**，超出会截断并置 `truncated: true`。
- `since` / `before` 使用 `YYYY-MM-DD` 字符串，内部会转换为 IMAP 的 `DD-Mon-YYYY`。
- `download_attachments` 会对文件名做安全清洗（去除路径分隔符与 `..`），同名文件自动加序号。

---

## 5. 关键实现说明（网易特有坑）

本项目针对网易 IMAP 服务做了三项特殊处理，这是能否正常工作的关键：

1. **登录后发送 `ID` 命令**
   网易 IMAP 要求客户端上报身份信息，否则后续命令会报
   `Unsafe Login. Please contact kefu@188.com for help` 或直接断开。
   实现位于 `netease_mail/imap_client.py::_send_id`，在 `login()` 成功后、
   任何其他命令之前执行，并**消费掉 untagged 响应**以避免污染后续命令队列。

2. **modified UTF-7 编解码**
   中文等非 ASCII 文件夹名在 `SELECT` / `SEARCH` 前会被编码为 modified UTF-7，
   返回给用户时再解码为可读中文。自实现于 `netease_mail/imap_utf7.py`，无第三方依赖。

3. **发信人地址与登录账号一致**
   163 SMTP 会校验 `From`，不一致将被拒信（553），因此 `send_message` 的 From
   固定使用配置的邮箱地址。

其他健壮性设计：

- 复用一条 IMAP 连接，用 `threading.Lock` 串行化访问（`imaplib` 非线程安全）；
  遇到 `imaplib.IMAP4.abort` / socket 异常时**自动重连一次**再抛错。
- 读取邮件统一使用 `BODY.PEEK[...]`，不会把邮件置为已读。
- 字符集处理健壮：`decode_header` 优先按声明字符集解码，失败回退
  `utf-8` / `gb18030` / `big5`，最终 `errors="replace"`，**绝不因单封邮件异常而中断整个列表**。

---

## 6. 目录结构

```
netease-mail-mcp/
├── server.py                  # MCP 入口：FastMCP 实例、注册全部工具、main() 启动 stdio
├── requirements.txt
├── .env.example
├── README.md
├── netease_mail/
│   ├── __init__.py
│   ├── config.py              # 环境变量解析 + 邮箱类型→服务器地址映射 + 校验
│   ├── errors.py              # 异常体系
│   ├── imap_utf7.py           # modified UTF-7 编解码
│   ├── mime.py                # 头部解码、正文提取、附件枚举、地址解析
│   ├── imap_client.py         # IMAP 连接管理（ID 命令、自动重连、线程锁）
│   ├── smtp_client.py         # SMTP 发信（纯文本 + HTML + 附件）
│   └── service.py             # 领域服务层：给工具层提供干净的 Python 方法
├── tests/
│   ├── test_imap_utf7.py
│   ├── test_config.py
│   ├── test_mime.py
│   └── test_service_helpers.py
└── scripts/
    └── smoke_test.py          # 真实连通性自检
```

分层调用链：`server.py`（工具层）→ `service.py`（领域层）→ `imap_client.py` / `smtp_client.py` → `mime.py` / `imap_utf7.py`。
工具层**不拼接任何 IMAP/SMTP 命令**。

---

## 7. 运行测试

```bash
# 从项目根目录执行
python -m unittest discover -s tests -v
```

测试均为纯离线单测，不建立网络连接。

---

## 8. 常见问题排查

| 现象 | 原因与解决 |
|---|---|
| 调用工具返回「请先配置 NETEASE_EMAIL 与 NETEASE_AUTH_CODE」 | 未配置必填环境变量；在客户端 `env` 或 `.env` 中补充 |
| `IMAP 登录失败 …` | ① 用了登录密码而非授权码；② 未开启 IMAP 服务；③ 邮箱地址不完整 |
| `Unsafe Login. Please contact kefu@188.com for help` | ID 命令未成功发送；确认 `imap_client.py` 中的 `_send_id` 未被跳过，并检查日志 |
| `SMTP 登录失败 …` | 未开启 SMTP 服务，或授权码错误 |
| 发信被拒（553） | From 与登录账号不一致；本连接器已固定 From，若自定义需保持一致 |
| 中文文件夹打不开 | 确认使用 `list_folders` 返回的名称（已解码），不要手工拼写 modified UTF-8 |
| 搜索中文关键字无结果 | 非 ASCII 关键词会以 UTF-8 字面量发送；若仍无结果，请改用 `list_messages` 拉取后在本地筛选 |
| 连接超时 | 检查网络 / 代理；可通过 `NETEASE_TIMEOUT` 适当增大超时 |
| 附件下载位置 | 默认 `./downloads`，可用 `NETEASE_ATTACHMENT_DIR` 或工具参数 `save_dir` 指定 |

---

## 9. 安全提示

- `.env` 已被 `.gitignore` 忽略，请勿将授权码提交到版本库。
- 授权码泄漏等同于邮箱密码泄漏；如怀疑泄漏，请在网页版邮箱重置授权码。
- `download_attachments` 的 `save_dir` 请使用可信目录。
