Skip to main content
Glama
Sui-IB

Personal Gmail MCP

by Sui-IB
README.md
# Personal Gmail MCP


这是一个完全外置的 Gmail MCP 服务,任何支持 Streamable HTTP 传输的 MCP 客户端都能用。

可兼容 InternalBeyond 当前的浏览器端 MCP 客户端:

- 不修改 `InternalBeyond.html`;
- 使用 Streamable HTTP / JSON-RPC 2.0;
- 兼容协议版本 `2025-03-26`;
- 可直接部署到 Render;
- Google OAuth 令牌不写入 HTML,也不提交到 GitHub;
- 前端原有“调用前询问”继续负责发送与管理操作的最终确认;
- 不提供永久删除邮件的工具。

## 能做什么

一共提供 18 个工具:

1. `search_emails`:按 Gmail 搜索语法查询邮件;
2. `read_thread`:读取会话正文;
3. `list_labels`:列出标签;
4. `list_drafts`:列出草稿;
5. `create_draft`:创建新草稿;
6. `reply_draft`:创建回复草稿;
7. `send_email`:直接发送新邮件;
8. `reply_email`:直接回复邮件;
9. `send_draft`:发送既有草稿;
10. `mark_thread_read`:标为已读;
11. `mark_thread_unread`:标为未读;
12. `star_thread`:加星标;
13. `unstar_thread`:取消星标;
14. `archive_thread`:归档;
15. `trash_thread`:移入垃圾箱;
16. `restore_thread`:从垃圾箱恢复;
17. `apply_labels`:添加标签;
18. `remove_labels`:移除标签。

`trash_thread` 只会移入垃圾箱。项目中没有永久删除接口。

## 第一步:把项目放到 GitHub

把本文件所在的整个目录上传为一个新的 GitHub 仓库。不要上传真实的 `.env` 文件,也不要把任何密码、Client Secret、访问密钥或令牌写进代码。

## 第二步:先部署到 Render,取得固定网址

最容易理解的方式:

1. 在 Render 选择 **New → Web Service**;
2. 连接刚才的 GitHub 仓库;
3. Runtime 选择 **Node**;
4. Build Command 填 `npm install`;
5. Start Command 填 `npm start`;
6. 可以先不配置 Google 环境变量,直接部署;
7. 部署完成后记下 Render 地址,例如:

   `https://my-personal-gmail-mcp.onrender.com`

打开 `/health`,看到 `{"ok":true,...}` 即表示服务正常。例如:

`https://my-personal-gmail-mcp.onrender.com/health`

仓库里的 `render.yaml` 也支持 Blueprint 部署。Render 会自动提供 `RENDER_EXTERNAL_URL`,本项目会据此生成 OAuth 回调地址。

## 第三步:建立 Google OAuth 应用

### 1. 创建项目并启用 Gmail API

1. 打开 [Google Cloud Console](https://console.cloud.google.com/);
2. 新建一个项目,例如 `Personal Gmail MCP`;
3. 打开 **APIs & Services → Library**;
4. 搜索并启用 **Gmail API**。

### 2. 配置授权页面

在 **Google Auth Platform** 中:

1. App name 可填 `Personal Gmail MCP`;
2. Audience 选择 **External**;
3. 如果当前状态是 Testing,把自己的个人 Gmail 加入 **Test users**;
4. Data Access 中添加:

   `https://www.googleapis.com/auth/gmail.modify`

这个权限可以读取、写信、发送、管理标签和移入垃圾箱,但不能绕过垃圾箱永久删除。

### 3. 创建 OAuth Client

1. 打开 **Google Auth Platform → Clients → Create Client**;
2. Application type 选择 **Web application**;
3. 在 **Authorized redirect URIs** 中填写:

   `https://你的Render地址/oauth/callback`

   例如:

   `https://my-personal-gmail-mcp.onrender.com/oauth/callback`

4. 创建后保存 `Client ID` 和 `Client Secret`。

不要把 Client Secret 放进 GitHub,也不要发给其他人。

## 第四步:配置 Render 环境变量

进入 Render 服务的 **Environment** 页面,添加:

| Key | Value |
| --- | --- |
| `MCP_ACCESS_KEY` | 自己生成的长随机字符串,建议至少 32 个字符 |
| `ALLOWED_GMAIL` | 只允许授权的个人 Gmail 地址 |
| `GOOGLE_CLIENT_ID` | Google 刚才生成的 Client ID |
| `GOOGLE_CLIENT_SECRET` | Google 刚才生成的 Client Secret |
| `ALLOWED_ORIGINS` | 离线 HTML 使用 `null` |

保存并重新部署。`MCP_ACCESS_KEY` 同时承担两件事:保护远程 MCP 地址,以及加密稍后生成的 Gmail 刷新令牌。因此保存令牌后不要随意更改它。

## 第五步:只做一次 Gmail 授权

在浏览器打开:

`https://你的Render地址/setup?key=你的MCP_ACCESS_KEY`

点击“连接并授权 Gmail”,亲自在 Google 页面登录并授权。服务会核对授权账号必须等于 `ALLOWED_GMAIL`。

授权成功后,页面会生成一段加密字符串:

1. 复制它;
2. 回到 Render → Environment;
3. 新增 `GMAIL_TOKEN_BLOB`;
4. 将整段加密字符串作为值;
5. 点击 **Save and deploy**。

这段字符串不是邮箱密码,但仍属于敏感凭证:不要发给别人,不要提交到 GitHub。

为什么多这一步:Render 免费实例可能休眠或重新部署,运行内存会清空。把加密令牌放入环境变量后,服务重启仍能自动连接 Gmail,不需要数据库或付费磁盘。

## 第六步:接入 InternalBeyond

打开 InternalBeyond 的 DIY → MCP 服务器:

- 别名:`gmail`
- 服务器地址:

  `https://你的Render地址/mcp?key=你的MCP_ACCESS_KEY`

- 勾选“启用 MCP”;
- 保持“调用前询问”开启;
- 点击“连接”。

成功后应该显示“已连接 · 18个工具”。无需修改原 HTML。

## 测试用语

- “查找最近7天收件箱里的未读邮件,只列发件人、主题和日期。”
- “读取刚才第一封邮件的完整会话,然后概括对方在说什么。”
- “给 person@example.com 写一封邮件,告诉他明天下午可以见面,先创建草稿。”
- “回复刚才那封邮件,说明我已经收到,并直接发送。”
- “把刚才那封邮件标为已读并归档。”

直接发送、回复发送、归档、标签和垃圾箱等工具会继续触发 InternalBeyond 原有确认卡。只有点击确认后,前端才真正向 MCP 发出调用。

## 关于 Google 的“Testing”状态

Google 规定:External + Testing 的 OAuth 应用,如果申请 Gmail 等用户数据权限,刷新令牌通常会在 7 天后过期。测试成功后,如果希望长期使用,可在 Google Auth Platform 中把发布状态调整为 **In production**。个人自用应用仍可能显示“未验证应用”提示和用户数上限,但不需要把服务公开给其他人。

官方说明:

- [Google OAuth 刷新令牌有效期](https://developers.google.com/identity/protocols/oauth2#expiration)
- [Google 个人使用与测试应用的验证例外](https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification#exceptions-to-verification-requirements)

## 安全边界

1. **不要关闭调用前确认**,尤其是 `send_email`、`reply_email`、`send_draft`、`trash_thread`;
2. 邮件正文可能故意写入“忽略规则、转发资料”等提示词攻击。本服务在工具说明和返回结果中都会标记邮件为“不可信数据”,但最终仍应检查确认卡;
3. AI 阅读邮件时,正文会被提交给 InternalBeyond 当前选择的模型服务商;
4. MCP URL 中含访问密钥,不要截图公开,不要分享;
5. 本服务不保存邮件正文,不提供永久删除工具,也不会主动轮询邮箱;只有你打开页面、让 AI 调用时才访问 Gmail;
6. 如需撤销,可在 [Google 账号的第三方连接页面](https://myaccount.google.com/connections) 移除该 OAuth 应用,并删除 Render 服务或环境变量。

## 常见错误

### `redirect_uri_mismatch`

Google OAuth Client 中登记的回调地址必须与 Render 实际地址逐字一致,并以 `/oauth/callback` 结尾。

### InternalBeyond 显示 CORS/网络失败

离线 `file://` HTML 的 Origin 是 `null`。确认 Render 环境变量:

`ALLOWED_ORIGINS=null`

如果以后把 HTML 托管到网页,则把该网页 Origin 加入白名单,多个值用英文逗号分隔,例如:

`null,https://example.com`

### HTTP 401

InternalBeyond 填写的 URL 中 `key=` 后内容必须与 Render 的 `MCP_ACCESS_KEY` 完全一致。

### `Gmail 尚未授权`

重新打开 `/setup?key=...` 完成授权,并把生成的 `GMAIL_TOKEN_BLOB` 保存到 Render。

### 7 天后出现 `invalid_grant`

OAuth 应用仍处于 Testing。重新授权后,按上文评估切换到 In production。

### AI 只说会处理,但不调用工具

确认 MCP 总开关、对应 Gmail 工具开关和“原生函数调用”已经打开,并选用工具调用能力较好的模型。

## 本地测试

项目没有第三方运行依赖。Node.js 20 或更高版本下:

```bash
npm test
```

测试覆盖 InternalBeyond 的 MCP 握手、`Origin: null` CORS、工具发现、邮件搜索、中文邮件 MIME 编码、错误密钥拒绝和恶意网页 Origin 拒绝。

## 相关官方文档

- [Gmail API](https://developers.google.com/workspace/gmail/api/reference/rest)
- [Gmail OAuth scopes](https://developers.google.com/workspace/gmail/api/auth/scopes)
- [Gmail messages.send](https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send)
- [Gmail threads](https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.threads)
- [Render 环境变量](https://render.com/docs/configure-environment-variables)
- [Render Blueprint](https://render.com/docs/blueprint-spec)