一个最小可用的 163 邮箱 MCP 服务,使用 Go + Rod 通过真实浏览器自动化访问 https://mail.163.com。
当前实现了 4 个核心工具:
logincheck_login_statussend_mailsend_mail_with_big_attachment
打开 163 邮箱登录页,并优先尝试使用环境变量自动登录。
- 如果配置了环境变量
MAIL163_USERNAME和MAIL163_PASSWORD,会优先自动登录 - 如果自动登录失败,会回退到手动登录
- 如果没有配置环境变量,会直接等待用户手动登录
检查当前浏览器页面中是否存在 #dvNavContainer:
- 存在:表示已登录
- 不存在:表示未登录
发送邮件,参数要求:
to:收件人,不能为空subject:主题,不能为空content:正文,不能为空- 支持多个收件人,多个邮箱用分号
;分隔 - 会校验邮箱格式
发送流程:
- 打开 163 邮箱页面
- 检查是否已登录
- 如果未登录,优先尝试环境变量自动登录
- 点击“写信”
- 填写收件人、主题、正文
- 点击“发送”
- 等待
#sucAnimIcon.suc-anim-icon.had-finish-anim - 返回发送成功信息
发送带本地大附件的邮件,参数要求:
to:收件人,不能为空subject:主题,不能为空content:正文,不能为空attachment_paths:本地附件路径数组,不能为空- 最多支持 5 个附件
- 单个附件大小不能超过 500MB
发送流程:
- 打开 163 邮箱页面
- 检查是否已登录
- 如果未登录,优先尝试环境变量自动登录
- 点击“写信”
- 填写收件人、主题、正文
- 向页面中的
input[type="file"]注入本地附件路径 - 持续等待附件上传和扫描完成
- 点击“发送”
- 返回发送成功信息
说明:
attachment_paths建议传绝对路径- 大附件上传和网易侧扫描可能持续较长时间,服务端会在上传阶段持续轮询等待
- 服务内部会为附件创建 ASCII 临时上传文件名,以避免中文文件名或特殊字符影响浏览器上传
- 收件人看到的附件名会是服务内部生成的安全临时名,而不是原始中文文件名
- 如果任一附件不存在、不可访问、或路径是目录,会在打开浏览器前直接报错,不再继续尝试发送
当前登录策略分为两层:
如果设置了以下环境变量:
MAIL163_USERNAMEMAIL163_PASSWORD
服务会优先尝试自动登录。
其中 MAIL163_USERNAME 支持以下规则:
- 如果值是
xxx@163.com,登录时会自动裁剪成xxx - 如果不是
@163.com结尾,则原样使用
自动登录时会尝试勾选“30天免登录”复选框:
input#un-login[name="un-login"]
如果自动登录失败,或者没有配置环境变量,用户可以直接在浏览器中手动登录。
服务会复用固定浏览器 profile,因此手动登录成功后,后续重启 MCP server 时通常仍然可以保留登录状态。
在 macOS 下,默认优先使用:
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
如果没有找到这个路径,则回退到 Rod 默认浏览器。
默认浏览器 profile 目录:
~/.163mail-mcp/browser-profile
这意味着:
- 手动登录成功后,登录态通常会被保存在这个目录里
- 重启 MCP server 后,大概率不需要重新登录
你也可以通过参数自定义 profile:
-user-data-dir=/path/to/profile- macOS
- Windows
- Linux / Docker
go mod tidygo run . -addr :16161| 参数 | 含义 | 是否必填 | 默认值 |
|---|---|---|---|
-addr |
HTTP 监听地址 | 否 | :16161 |
-headless |
是否以无头模式运行浏览器 | 否 | false |
-browser-bin |
自定义浏览器可执行文件路径 | 否 | macOS 下优先尝试系统 Chrome,否则回退到 Rod 默认浏览器 |
-user-data-dir |
浏览器 profile 目录 | 否 | ~/.163mail-mcp/browser-profile |
| 环境变量 | 含义 | 是否必填 | 默认值 |
|---|---|---|---|
MAIL163_USERNAME |
163 邮箱账号,用于自动登录和默认调试回执收件人 | 自动登录场景下必填 | 空 |
MAIL163_PASSWORD |
163 邮箱密码,用于自动登录 | 自动登录场景下必填 | 空 |
MAIL163_DEBUG_REPORT_ENABLED |
是否开启“发送成功后自动回寄调试回执” | 否 | false |
MAIL163_DEBUG_REPORT_RECIPIENT |
调试回执收件人邮箱 | 否 | 默认使用 MAIL163_USERNAME |
说明:
- 如果未设置
MAIL163_USERNAME和MAIL163_PASSWORD,服务仍可启动,但需要手动登录 - 如果
MAIL163_DEBUG_REPORT_ENABLED=false,则不会发送调试回执 MAIL163_DEBUG_REPORT_RECIPIENT仅在开启调试回执时生效
项目现在可以直接构建为 Docker image,并在容器里运行。
- 构建镜像
- 准备宿主机挂载目录
- 启动容器
- 使用
/healthz检查服务状态
docker build -t 163mail-mcp:latest .如果你准备把镜像部署到 Windows 宿主机上的 Linux 容器环境,建议显式构建 linux/amd64:
docker build --platform linux/amd64 -t 163mail-mcp:latest .如果你想显式指定 Go 大版本,也可以:
docker build --build-arg GO_VERSION=1.26 -t 163mail-mcp:latest .mkdir -p $(pwd)/data $(pwd)/attachments
docker run -d \
--name 163mail-mcp \
--shm-size=1g \
-p 16161:16161 \
-e MAIL163_USERNAME='your_account@163.com' \
-e MAIL163_PASSWORD='your_password' \
-e MAIL163_DEBUG_REPORT_ENABLED='true' \
-v 163mail-browser-profile:/data/browser-profile \
-v $(pwd)/attachments:/attachments \
163mail-mcp:latest| 配置项 | 含义 | 是否必填 | 默认值 |
|---|---|---|---|
-p 16161:16161 |
将宿主机 16161 端口映射到容器内服务端口 |
建议填写 | 无 |
MAIL163_USERNAME |
163 邮箱账号,用于自动登录 | 自动登录场景下必填 | 空 |
MAIL163_PASSWORD |
163 邮箱密码,用于自动登录 | 自动登录场景下必填 | 空 |
MAIL163_DEBUG_REPORT_ENABLED |
是否开启发信后自动发送调试回执 | 否 | false |
MAIL163_DEBUG_REPORT_RECIPIENT |
调试回执收件人 | 否 | 默认使用 MAIL163_USERNAME |
--shm-size=1g |
为容器内 Chromium 提供更稳定的共享内存空间 | 强烈建议 | Docker 默认值 |
-v 163mail-browser-profile:/data/browser-profile |
用 Docker named volume 持久化浏览器 profile | 强烈建议 | 无 |
-v $(pwd)/attachments:/attachments |
挂载附件目录,供 send_mail_with_big_attachment 使用 |
带附件发信场景下建议填写 | 无 |
说明:
- 容器默认会以
-headless=true启动 Chromium - 容器内浏览器路径固定为
/usr/bin/chromium - 浏览器 profile 持久化目录为
/data/browser-profile - 推荐把浏览器 profile 单独放进 Docker named volume,而不是直接映射到 Windows 宿主机目录
- 如果要发送本地大附件,建议把宿主机目录挂载到容器内
/attachments - 调用
send_mail_with_big_attachment时,attachment_paths应传容器内路径,例如/attachments/demo.pdf - 如果设置
MAIL163_DEBUG_REPORT_ENABLED=true,每次发送成功后会自动向回执邮箱再发送一封调试回执 - 默认回执邮箱取
MAIL163_USERNAME;也可以单独设置MAIL163_DEBUG_REPORT_RECIPIENT - 这个调试回执开关是服务端环境配置,不是 MCP tool 参数,因此调用方不能在单次请求里随意打开它
- 调试回执邮件内部不会再次触发调试回执,因此不会形成死循环
curl http://127.0.0.1:16161/healthzdocker logs -f 163mail-mcpDocker 默认使用无头浏览器,因此更推荐配置:
MAIL163_USERNAMEMAIL163_PASSWORD
也就是优先走自动登录。
如果你确实想在容器里做“手动登录”,通常还需要额外接入可视化桌面、VNC 或远程调试方案,这不是当前 Docker 默认用法。
项目根目录已经提供了 docker-compose.yml。
先设置环境变量:
export MAIL163_USERNAME='your_account@163.com'
export MAIL163_PASSWORD='your_password'
export MAIL163_DEBUG_REPORT_ENABLED='true'
# 可选:不填时默认回寄到 MAIL163_USERNAME
# export MAIL163_DEBUG_REPORT_RECIPIENT='your_account@163.com'然后启动:
docker compose up -d --build如果你想看配置内容,当前 compose 文件等价于:
services:
mail163-mcp:
build: .
container_name: 163mail-mcp
ports:
- "16161:16161"
environment:
MAIL163_USERNAME: your_account@163.com
MAIL163_PASSWORD: your_password
MAIL163_DEBUG_REPORT_ENABLED: "true"
MAIL163_DEBUG_REPORT_RECIPIENT: your_account@163.com
shm_size: "1gb"
volumes:
- mail163_browser_profile:/data/browser-profile
- ./attachments:/attachments
restart: unless-stopped
volumes:
mail163_browser_profile:如果容器启动时报类似错误:
The profile appears to be in use by another Chromium process
通常说明上一次 Chromium 没有正常退出,/data/browser-profile 下残留了锁文件。
推荐修复方式:
- 先停止并删除旧容器
docker rm -f 163mail-mcp- 如果使用的是宿主机目录挂载,删除 profile 锁文件
cd /path/to/163mail-mcp
find data/browser-profile -maxdepth 1 \
\( -name 'Singleton*' -o -name 'SingletonLock' -o -name 'SingletonSocket' -o -name 'SingletonCookie' \) \
-print -delete- 重新启动容器
docker run -d \
--name 163mail-mcp \
-p 16161:16161 \
-e MAIL163_USERNAME='your_account@163.com' \
-e MAIL163_PASSWORD='your_password' \
-e MAIL163_DEBUG_REPORT_ENABLED='true' \
-v $(pwd)/data:/data \
-v $(pwd)/attachments:/attachments \
163mail-mcp:latest说明:
- 不建议直接删除整个
data/browser-profile - 删除整个 profile 会丢失登录态,通常需要重新登录 163 邮箱
- 只有在锁文件清理后仍然无法恢复,或者 profile 已损坏时,才建议删除整个
browser-profile - 如果使用的是 Docker named volume,更简单的做法通常是删除旧 volume 后重新启动,再重新登录一次 163 邮箱
如果你的宿主机是 Windows,容器里又需要运行 Chromium,这里有几个高概率问题值得优先排查。
这通常说明当前运行的镜像架构和生产环境不匹配,例如把错误架构的镜像直接拷到了 Windows 机器上,导致 Chromium 在模拟层里运行。
推荐做法:
- 在构建机上显式构建
linux/amd64:
docker build --platform linux/amd64 -t 163mail-mcp:latest .- 构建后检查镜像架构:
docker image inspect 163mail-mcp:latest --format '{{.Architecture}}/{{.Os}}'预期输出:
amd64/linux
- 在
docker compose中给服务补上:
platform: linux/amd64这通常是上一次 Chromium 异常退出后,浏览器 profile 遗留了锁文件或损坏状态。
推荐做法:
- 优先把浏览器 profile 放在 Docker named volume 中,而不是 Windows 宿主机目录
- 如果仍然报错,删除旧的
mail163_browser_profilevolume 后重新启动容器 - 删除旧 profile 后,通常需要重新登录一次 163 邮箱
这类错误通常说明 Rod 与 Chromium 的调试连接已经断开。当前服务已经增加了浏览器会话断连后的自动重建逻辑,但如果错误在每次启动后都稳定复现,通常仍要优先排查底层 Chromium 运行环境:
- 镜像架构是否正确
- 是否配置了
shm_size: "1gb"或--shm-size=1g - 浏览器 profile 是否放在 Docker named volume 中
- 是否存在上一次异常退出遗留的锁文件或损坏 profile
如果日志里卡在 chromium_*.deb 下载失败、Connection failed、502 Bad Gateway 之类错误,通常不是代码问题,而是构建时 APT 源网络不稳定。当前 Dockerfile 已经做了这些处理:
- 默认切换到国内 Debian 镜像源
- 为
apt-get update增加重试和超时 - 将
chromium的安装拆成单独重试
建议:
- 优先在网络更稳定的机器上构建出镜像,再导出为 tar 交给生产机
docker load - 如果构建成功,部署前再次确认镜像架构是
amd64/linux
services:
mail163-mcp:
image: 163mail-mcp:latest
platform: linux/amd64
container_name: 163mail-mcp
ports:
- "16161:16161"
environment:
MAIL163_USERNAME: your_account@163.com
MAIL163_PASSWORD: your_password
MAIL163_DEBUG_REPORT_ENABLED: "true"
MAIL163_DEBUG_REPORT_RECIPIENT: your_account@163.com
shm_size: "1gb"
volumes:
- mail163_browser_profile:/data/browser-profile
- ./attachments:/attachments
restart: unless-stopped
volumes:
mail163_browser_profile:如果你在国内网络环境,建议这样启动:
cd /path/to/163mail-mcp
GOMODCACHE=$(pwd)/.gomodcache \
GOCACHE=$(pwd)/.gocache \
GOPROXY=https://goproxy.cn,direct \
GOSUMDB=off \
/usr/local/go/bin/go run . -addr :16161先设置环境变量:
export MAIL163_USERNAME='your_account@163.com'
export MAIL163_PASSWORD='your_password'再启动服务:
cd /path/to/163mail-mcp
GOMODCACHE=$(pwd)/.gomodcache \
GOCACHE=$(pwd)/.gocache \
GOPROXY=https://goproxy.cn,direct \
GOSUMDB=off \
/usr/local/go/bin/go run . -addr :16161如果你不想设置环境变量,也可以直接启动服务:
cd /path/to/163mail-mcp
GOMODCACHE=$(pwd)/.gomodcache \
GOCACHE=$(pwd)/.gocache \
GOPROXY=https://goproxy.cn,direct \
GOSUMDB=off \
/usr/local/go/bin/go run . -addr :16161然后调用 login,在浏览器中手动完成登录。
服务默认提供:
POST /mcpGET /healthz
curl http://127.0.0.1:16161/healthz{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "send_mail_with_big_attachment",
"arguments": {
"to": "foo@example.com;bar@example.com",
"subject": "带附件测试",
"content": "这是正文",
"attachment_paths": [
"/absolute/path/report.pdf",
"/absolute/path/archive.tar"
]
}
}
}{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "login",
"arguments": {}
}
}{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "check_login_status",
"arguments": {}
}
}{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "send_mail",
"arguments": {
"to": "foo@example.com;bar@example.com",
"subject": "测试主题",
"content": "测试正文"
}
}
}- 启动服务
- 调
login - 在浏览器中手动登录
- 调
check_login_status - 调
send_mail
- 设置
MAIL163_USERNAME和MAIL163_PASSWORD - 启动服务
- 直接调
send_mail
服务端会输出关键步骤日志,便于排查问题,例如:
- 浏览器路径
- profile 目录
- 登录检查
- 自动登录流程
- 写信步骤
- 正文填写方式
- 登录失败提示
- 163 邮箱页面结构如果变化,定位器可能需要调整
- 自动登录依赖当前登录页 DOM,如果 163 登录页变更,可能需要重新适配
- 某些情况下如果出现验证码、风控、滑块校验,仍需要手动登录
- 正文编辑器位于 iframe 中,当前实现会优先定位
iframe.APP-editor-iframe - 当前实现是最小版本,没有附件、抄送、草稿、收件箱查询等能力