Gmail MCP Gateway
Gmail MCP 网关
一个 MCP 服务器,让 AI 代理能够完整读取和管理多个 Gmail 账户——并且无法发送、丢弃或删除邮件。
Gmail MCP Gateway
ALLOWED FORBIDDEN
─────── ─────────
Search Send
Read messages Send draft
Read threads Trash
Read attachments Delete
Create drafts Mark spam
Edit drafts Gmail settings
Archive Forwarding rules
Read / unread Arbitrary API calls
Labels该保证通过应用程序代码强制执行,而非通过向客户端下达指令实现。即使 MCP 客户端存在缺陷、被攻破或遭受提示注入,也无法通过此网关发送电子邮件,因为没有任何代码路径允许这样做。
目录
Related MCP server: imap-mcp
快速开始
需要 Python 3.11+。五步,大约十分钟,大部分时间在 Google 控制台内。
1. 安装
git clone <this-repo> gmail-mcp-gateway
cd gmail-mcp-gateway
uv sync # or: python -m venv .venv && .venv/bin/pip install -e .
.venv/bin/gmail-mcp-gateway --version可选:将其放入 PATH 中,以便下面的示例更自然:
export PATH="$PWD/.venv/bin:$PATH"2. 创建 Google OAuth 客户端
一次性操作,免费,并且你之后添加的每个账户都可以共享。
在 https://console.cloud.google.com/ 创建一个项目。
APIs & Services → Library → 启用 Gmail API。
APIs & Services → OAuth consent screen → External,填写必填字段,在 Test users 下添加你自己的 Google 账户。
发布应用(如果你仍然是唯一用户,则无需验证审核)。跳过此步骤会使应用保持在“测试”状态,Google 会在 7 天 后过期刷新令牌,你将需要每周重新授权。
Credentials → Create credentials → OAuth client ID → Desktop app → 下载 JSON。
这里不需要选择作用域。网关在授权时仅请求所需权限,并拒绝请求 Gmail 之外的任何内容。
3. 安装 OAuth 客户端
install -Dm600 ~/Downloads/client_secret_*.json \
~/.local/share/gmail-mcp-gateway/secrets/oauth_client.json这是你唯一需要手动放置的文件。加密密钥会在首次运行时自动生成。
4. 授权账户
gmail-mcp-gateway accounts add personal浏览器会打开;批准请求的权限,勾选 所有 复选框(如果权限被拒绝,网关会大声报错而不是部分工作)。刷新令牌会加密存储,之后网关可以无人值守运行。
可以添加任意数量的账户——每个账户都有自己的授权、刷新令牌、加密密钥、速率限制桶和审计轨迹:
gmail-mcp-gateway accounts add work
gmail-mcp-gateway accounts add newsletters --read-only # Google itself refuses writes5. 验证
gmail-mcp-gateway health # exit 0 = ready, 2 = something is wrong[ok ] directories config=/home/you/.config/gmail-mcp-gateway ...
[ok ] database /home/you/.local/share/gmail-mcp-gateway/gateway.db
[ok ] master_key loaded
[ok ] oauth_client configured
[ok ] accounts 1/1 authorized
gmail-mcp-gateway 1.0.0: healthy然后,将你的 MCP 客户端指向它——参见 连接 MCP 客户端——或者先进行测试:
uv run python scripts/try-it.py --account personal环境变量
对于普通的本地安装,你不需要设置任何环境变量。 上面的快速开始没有设置任何环境变量。默认配置位于 ~/.config,数据和密钥位于 ~/.local/share,加密密钥会自动生成。
这些变量适用于容器、systemd 单元和密钥管理器等场景——在这些地方,磁盘文件不是合适的机制。
变量 | 必需? | 默认值 | 用途 |
| 否¹ | — | Google OAuth 客户端 ID |
| 否¹ | — | Google OAuth 客户端密钥 |
| 否² | 自动生成 | base64 编码的 32 字节凭据加密密钥 |
| 否 |
|
|
| 否 |
|
|
| 否 |
| 密钥、OAuth 客户端、凭据 |
| 否 |
| HTTP 绑定地址 |
| 否 |
| HTTP 绑定端口 |
| 否 |
| 通过配置启用 HTTP 传输 |
| 否 |
| 允许非回环绑定 |
| 否 |
|
|
¹ 替代 secrets/oauth_client.json。提供文件 或 这对变量。
² 未设置时,网关会在首次运行时创建 secrets/master.key(模式 0600)。
环境变量覆盖 config.toml,而 config.toml 覆盖默认值。
生成每个值
OAuth 客户端 ID 和密钥 —— 来自快速开始步骤 2 中下载的 JSON。如果要用环境变量而不是文件:
jq -r '.installed.client_id' ~/Downloads/client_secret_*.json
jq -r '.installed.client_secret' ~/Downloads/client_secret_*.json主密钥 —— 32 个随机字节,base64 编码:
openssl rand -base64 32
# or, without openssl:
python3 -c "import base64,secrets; print(base64.b64encode(secrets.token_bytes(32)).decode())"此密钥用于解密你存储的刷新令牌。如果在添加账户后更改密钥,凭据将变得不可读,每个账户都需要执行
accounts reauth。请将密钥备份到数据目录的备份位置。
网关承载令牌 —— 不是环境变量。这是 MCP 客户端通过 HTTP 传输发送的内容,与任何 Google 凭据无关。CLI 生成它,并仅存储 SHA-256 哈希:
gmail-mcp-gateway token create my-agent明文令牌只打印一次,并放入 客户端 的配置中。
使用 env 文件
网关不会自动读取 .env —— 安全工具不应悄无声息地从启动目录吸收密钥。复制 .env.example,其中记录了每个变量,并显式加载:
cp .env.example .env # already covered by .gitignore
$EDITOR .env
set -a && source .env && set +a
gmail-mcp-gateway healthsystemd 使用 EnvironmentFile=;Docker Compose 使用 env_file:。
运行方式
stdio —— 通常的选择
客户端将网关作为子进程启动,并通过管道进行通信。没有端口,没有令牌,没有网络暴露。Google 凭据始终保留在网关进程内;客户端仅看到工具调用。
gmail-mcp-gateway serve --transport stdio手动运行时,它看起来会挂起——这是正常的,因为它正在等待 stdin 上的 JSON-RPC。通常你的 MCP 客户端会为你启动它。
可流式 HTTP —— 独立服务
适用于长期运行的服务,或无法生成进程的客户端。
gmail-mcp-gateway token create my-agent # once; save the printed token
gmail-mcp-gateway serve --transport http --host 127.0.0.1 --port 8765端点绑定到回环地址,需要承载令牌,并启用了 DNS 重新绑定保护。GET /healthz 无需认证,仅报告存活状态。
绑定非回环地址需要 GMAIL_MCP_ALLOW_REMOTE_BIND=true,即便如此,互联网可路由地址也会被拒绝。对于远程客户端,请使用隧道:
ssh -L 8765:127.0.0.1:8765 gateway-hostsystemd
deploy/gmail-mcp-gateway.service 以专用系统用户身份运行,在强化沙箱中——ProtectSystem=strict,空的 CapabilityBoundingSet,seccomp 过滤器,NoExecPaths 覆盖数据目录。安装步骤位于单元文件的头部。在启动之前,以服务用户身份以交互方式一次性授权账户。
Docker
deploy/Dockerfile 和 deploy/docker-compose.yml 以非 root 和只读方式运行,放弃所有能力,仅将端口发布到回环地址。镜像中不包含凭据;它们位于 /secrets 卷中。
docker compose -f deploy/docker-compose.yml up -d一次性授权序列——放置 OAuth 客户端、运行授权流程(发布重定向端口)、生成令牌——在 compose 文件的头部注释中有说明。
连接 MCP 客户端
stdio
{
"mcpServers": {
"gmail": {
"command": "/absolute/path/to/gmail-mcp-gateway/.venv/bin/gmail-mcp-gateway",
"args": ["serve", "--transport", "stdio"]
}
}
}Claude Code:
claude mcp add gmail -- /absolute/path/to/.venv/bin/gmail-mcp-gateway serve --transport stdioHTTP
{
"mcpServers": {
"gmail": {
"type": "http",
"url": "http://127.0.0.1:8765/mcp",
"headers": { "Authorization": "Bearer <token from `token create`>" }
}
}
}客户端永远不会挂载或以其他方式访问 Google 凭据文件。在 stdio 模式下,客户端与管道通信;在 HTTP 模式下,它持有一个与任何 Google 凭据无关的网关令牌。
工具参考
每个工具都接受一个 account 别名——没有默认账户。可变操作工具可接受一个可选的 client_request_id 用于幂等性:使用相同的 ID 和参数重复调用将返回第一次的结果,而不是执行两次操作。
工具 | 作用 |
| 别名、地址、状态、授予的权限。不含凭据。 |
| 每个账户的实时授权检查,以及邮箱总数。 |
| Gmail 搜索语法; |
| 一条消息:发件人、收件人、抄送、密送、主题、时间戳、标签、已读状态、正文、附件清单。 |
| 按顺序的整个对话,包含参与者。 |
| 附件清单。不下载任何内容。 |
| 获取字节:小附件内联 base64,否则写入网关自己的目录。 |
| 所有标签及其计数,以及网关是否会修改每个标签。 |
| 按 ID 或名称应用标签。拒绝 |
| 按 ID 或名称移除标签。拒绝 |
| 移除 |
| 移除 |
| 添加 |
| 已保存的草稿,包含收件人、主题、片段。 |
| 完整的一封草稿。 |
| 新建纯文本草稿。保存,从不发送。 |
| 在现有线程中创建回复草稿,包含正确的 |
| 编辑草稿;省略的字段保留其值,线程信息保持不变。 |
变更操作适用于单个消息或线程,也适用于批处理(默认上限 100 个 ID)。标签可以以 ID(Label_7)或显示名称(Receipts)形式提供。
在关键地方,读写处理不对称:gmail_search 可以愉快地过滤 TRASH 或设置 include_spam_trash,因为检查已存在的内容是读取操作。而应用这些标签会被拒绝,因为那会将邮件移入垃圾桶或报告为垃圾邮件。
错误
失败以 MCP 工具错误形式返回,带有 isError: true 和结构化负载,同时出现在文本块和结构化内容中:
{"error": {
"code": "forbidden_label",
"message": "refusing to add label 'TRASH': moving messages to Trash is a forbidden capability of this gateway",
"retryable": false
}}错误码包括 invalid_input、unknown_account、not_found、too_large、batch_too_large、rate_limited、forbidden_operation、forbidden_label、account_read_only、needs_reauth、upstream_rate_limited、upstream_unavailable、network_error、timeout 和 internal_error。内部异常记录在服务器端,并报告为裸露的 internal_error——客户端永远不会收到回溯或内部路径。
管理
gmail-mcp-gateway accounts list
gmail-mcp-gateway accounts status # live Gmail check per account
gmail-mcp-gateway accounts auth <alias>
gmail-mcp-gateway accounts reauth <alias> # after a revoked or expired grant
gmail-mcp-gateway accounts remove <alias> --yes # revokes at Google, deletes locally
gmail-mcp-gateway token create <name>
gmail-mcp-gateway token list
gmail-mcp-gateway token revoke <name>
gmail-mcp-gateway audit --limit 50 # recent state-changing operations
gmail-mcp-gateway audit --account work --since-hours 24
gmail-mcp-gateway audit --outcome denied --json
gmail-mcp-gateway prune # expired audit rows, dedup keys, attachments
gmail-mcp-gateway health --json在无头主机上,通过转发重定向端口进行授权:
# on the server
gmail-mcp-gateway accounts add work --no-browser --port 8899
# on your laptop
ssh -L 8899:127.0.0.1:8899 server
# then open the printed URL locally审计日志记录账户、时间戳、操作、受影响ID、结果、错误码、持续时间和调用主体——包括成功、失败和拒绝。它绝不记录令牌、消息体、主题或附件内容。管理仅限CLI:被入侵的MCP客户端无法添加账户、触发同意流程、生成令牌或读取审计日志。
目录布局
配置、数据和秘密相互分离,且各自可覆盖,因此每个部分可以有不同的后端存储:
角色 | 变量 | 默认值 | 内容 |
配置 |
|
|
|
数据 |
|
|
|
秘密 |
|
|
|
config.toml 是可选的;所有键及其默认值请参见
deploy/config.example.toml —— 包括批量上限、页面大小、正文和附件预算、速率限制、重试策略以及幂等窗口。
边界如何强制执行
四个独立层。每一层单独即可阻止发送;只有全部四层都失效,消息才会离开。
1. 工具表面。 共有十八个工具。没有 gmail_send、没有 gmail_trash、没有 gmail_raw_request,也没有任何接受URL、路径、HTTP方法或端点名称的工具。通用Gmail代理不是客户端可以达到的,因为它根本不存在。
mcpsrv/server.py
2. 端点白名单。 每个对Gmail的HTTP请求必须指定一个十四个 Endpoint 常量之一。users.messages.send、users.drafts.send、users.messages.trash、users.messages.delete 以及 users.settings 下的所有内容均被排除。路径参数使用严格ID模式验证,并以空安全集进行百分号编码,因此任何值都无法引入/从而到达不同端点。在请求发出前,黑名单会再次检查解析后的方法和路径,独立于其构建方式。DELETE 和 PATCH 完全无法发出。
gmail/allowlist.py
3. 标签策略。 这关闭了白名单留下的后门。users.messages.modify 是允许的——它是归档和已读状态的工作方式——但Gmail将 TRASH 和 SPAM 视为普通标签,因此应用其中一个标签会删除邮件或将其报告为垃圾邮件。变异中的每个标签ID都会进行双向、不区分大小写的检查,并且在传输前还会再次检查组装后的请求体。
gmail/labels.py
4. OAuth 范围。 账户使用 gmail.modify 授权,别无其他。该范围无法永久删除邮件(messages.delete 需要 https://mail.google.com/),也无法触碰任何Gmail设置,因此转发规则、过滤器、POP/IMAP配置和永久删除在Google的授权层即被阻止,而不仅仅是在此处被拦截。Google没有发布任何允许创建草稿但不允许发送的范围,因此发送由第1-2层阻止。使用 --read-only 添加的账户获得 gmail.readonly,Google本身会拒绝所有写入操作。
安全模型
邮件内容不可信。 正文、主题、发件人名称和附件文件名由第三方编写,可能包含针对读取邮件的模型的指令。网关将每个读取结果标记为 content_is_untrusted: true,并且服务器指令告诉客户端将邮件视为数据而非指令。更有用的是,注入指令可能请求的能力并不存在。
HTML 从不执行,也从不作为标记返回。 <script>、<style>、<iframe> 及类似元素及其内容会被丢弃;所有其他标签被剥离。结果为纯文本。
不可见 Unicode 被剥离。 零宽字符、双向覆盖和Unicode标签字符可让攻击者向人类展示一个内容,而LLM读取另一个内容。这些字符被移除,移除数量记录为 removed_hidden_characters。
附件被存储,但从不打开。 网关不解析、渲染或执行附件内容。客户端可以建议文件名,但绝不能建议路径:目标始终为 <attachments_dir>/<account>/<message_id>/<sanitized-name>,经过解析并重新检查是否包含在内,以 O_NOFOLLOW 模式 0600 写入。
凭据永远不会到达客户端。 刷新令牌、访问令牌和OAuth客户端密钥仅存在于网关进程内部。每个账户的凭据使用 AES-256-GCM 在按账户派生的密钥(HKDF-SHA256(master, "…account:<id>"))下加密,账户ID作为关联数据——因此一个账户的密钥无法打开另一个账户的凭据,并且移动到不同账户的凭据文件将解密失败。文件权限为 0600,位于 0700 目录中;网关拒绝读取组或世界可读的密钥。
诚实的作用域: 静态加密保护备份、散落副本和磁盘镜像。它无法防御已经以网关用户身份执行代码的攻击者——该攻击者可以读取主密钥。文件系统权限仍然是主要边界。
日志不会泄露秘密。 每条日志记录都会经过一个过滤重写,任何看起来像Google访问或刷新令牌、客户端密钥、Bearer头、JWT或凭据名称字段的内容——在消息、参数和异常文本中——都会被重写。在 stdio 下,日志发送到 stderr,因为 stdout 是 MCP 线路。
输入经过验证。 草稿收件人必须是符合严格模式的裸地址;标头值中的任何 CR、LF 或 NUL 都被视为标头注入尝试而被拒绝。草稿从类型化字段组装——网关从不接受来自客户端的原始 RFC 5322。批量、页面大小、正文长度、附件大小和收件人数均有上限,并且每个账户的令牌桶在失败时快速返回带有 retry_after_seconds 提示,而不是排队。
这不能防范什么
能够以网关用户身份执行代码的操作员。
客户端合法使用允许的功能但使用不当——例如批量归档或编写误导性草稿。归档和标记是可逆且可审计的;草稿仍然需要人工发送。
Google 端被入侵或恶意的 OAuth 客户端配置。
如果在没有 TLS 的情况下暴露 HTTP 传输,则可能被流量拦截。请将其保留在回环接口上,或在其前面放置一个 TLS 终止代理。
可靠性
令牌刷新:自动,每个账户有锁,因此并发调用只会刷新一次。
401触发恰好一次刷新和重试。刷新失败:
invalid_grant将账户标记为needs_reauth,并返回结构化错误,指出修复该问题的 CLI 命令。速率限制和 5xx:指数退避,完全抖动,遵循
Retry-After,最多max_attempts次。网络故障和超时:重试,然后报告为
network_error或timeout,不包含内部细节。分页:
next_page_token返回给客户端,因此服务器中不保存游标状态。重复请求:
client_request_id在 24 小时内抑制重复请求。并发重复请求在进程内序列化;使用不同参数重用 ID 是错误,而不是静默返回错误答案。背压:Gmail 调用共享可配置的并发信号量,每个账户有独立的令牌桶。因此,大型搜索或繁忙的账户不会产生无限制的上游并发。
扩展和部署拓扑
对于给定的数据和秘密目录,运行一个网关进程。SQLite 状态、凭据文件、令牌刷新锁和进行中的幂等协调有意保持本地;将多个副本指向同一卷不会提供安全的主主操作。
对于更大的安装,将账户分片到独立的网关实例中,每个实例有自己的配置、数据、秘密、Bearer 令牌和回环端口。这使故障、速率限制、审计轨迹和凭据保持隔离,同时允许每个实例服务并发客户端。仅在观察 Gmail 配额使用和主机容量后增加 limits.max_concurrency;默认值 8 是保守的。如果客户端需要一个共享的网络地址,在其前面放置一个 TLS 认证的路由层,并将每个账户别名路由到其自己的实例。
同一账户的主主副本需要将 SQLite 和本地凭据/幂等状态替换为协调的外部存储。这超出了当前网关的安全模型;不要仅仅通过增加工作节点或共享卷来扩展它。
测试
uv sync --all-extras
uv run pytest -q # 334 tests, no Google account needed
uv run pytest tests/test_security_boundary.py -v # just the guaranteetest_security_boundary.py 通过模拟 Gmail 驱动每个支持的操作,如果请求了禁止的 URL 则失败,然后尝试通过每个可用路由进行删除、垃圾邮件和发送。
一旦账户授权,用真实邮箱进行测试。该脚本像 MCP 客户端一样通过 stdio 连接,执行只读遍历,然后确认禁止的操作被拒绝:
uv run python scripts/try-it.py --account personal
uv run python scripts/try-it.py --account personal --draft # also drafts a reply
uv run python scripts/try-it.py --account personal --archive # archive round trip除非传递了变异标志,否则为只读,并且它所做的每个变异都是可逆的。它创建的草稿必须由您删除——网关无法删除。
要交互式点击:
npx @modelcontextprotocol/inspector .venv/bin/gmail-mcp-gateway serve --transport stdio布局
src/gmail_mcp_gateway/
├── mcpsrv/server.py the tool surface — the complete client-facing API
├── mcpsrv/http.py Streamable HTTP transport, bearer auth, bind safety
├── service.py the supported operations, and nothing else
├── gmail/allowlist.py the endpoint allowlist ← security boundary
├── gmail/labels.py label policy (blocks TRASH/SPAM) ← security boundary
├── gmail/client.py the only code that talks to Gmail
├── gmail/parse.py MIME → structured data, sanitization
├── gmail/compose.py draft assembly from typed fields
├── security/ validation, rate limiting, path confinement
├── auth/oauth.py OAuth 2.0 + PKCE, refresh, revoke
├── accounts.py account registry
├── crypto.py envelope encryption for credentials
├── audit.py audit log
└── cli.py administration添加一个支持的操作意味着:allowlist.py 中的 Endpoint,service.py 中的方法,mcpsrv/server.py 中的工具,EXPOSED_TOOLS 中的条目,以及测试。EXPOSED_TOOLS 和 FORBIDDEN_TOOLS 根据运行中的服务器进行断言,因此添加一个未声明的工具——或添加一个禁止的工具——会导致测试失败。保持网关专注于 Gmail;不同的 Google 产品应属于单独的 MCP 服务,而不是在此处使用更宽的范围。
故障排除
no OAuth client configured —— 快速入门第 3 步。放置 secrets/oauth_client.json(模式 0600)或设置 GMAIL_MCP_OAUTH_CLIENT_ID 和 GMAIL_MCP_OAUTH_CLIENT_SECRET。
Google did not return a refresh token —— 您之前已授权此应用。在 https://myaccount.google.com/permissions 移除其访问权限,然后重新运行 accounts auth <alias>。
consent screen did not grant every required permission —— 权限框未勾选。重新运行授权并保持所有框勾选。网关在此处故意失败,而不是留下一个半功能账户。
账户每周进入 needs_reauth 状态 —— OAuth 应用仍处于“测试”状态,Google 会在 7 天后过期刷新令牌。请发布它(快速入门第 2.4 步)。
stored credential failed authentication —— GMAIL_MCP_MASTER_KEY 已更改,或密钥文件已被替换。恢复原始密钥,或对每个账户运行 accounts reauth <alias>。
<file> is accessible to other users —— 网关拒绝读取组或世界可读的秘密。对该文件执行 chmod 600。
refusing to bind …: it is not a loopback address —— 有意如此。绑定 127.0.0.1 并使用 SSH 隧道,或者如果确实是受信任的私有接口,则设置 GMAIL_MCP_ALLOW_REMOTE_BIND=true。互联网可路由地址无论何种情况都会被拒绝。
serve --transport stdio 看起来挂起 —— 正确;它在等待 stdin 上的 JSON-RPC。让您的 MCP 客户端启动它,或使用 scripts/try-it.py。
有人更改了邮箱,我想知道具体发生了什么——
gmail-mcp-gateway audit --limit 50。每一次状态变更都记录在那里,拒绝操作也包含在内。
许可证
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityAmaintenanceAn open-source MCP server that provides AI agents with secure access to read, search, and manage emails via Microsoft 365 and Gmail. It features security-first defaults like recipient allowlists and markdown content conversion to facilitate safe agent interaction with mailboxes.4Apache 2.0
- Alicense-qualityBmaintenanceRead-only MCP server for IMAP email access, enabling AI agents to read, search, and monitor email without sending or deleting messages.47MIT
- AlicenseCqualityCmaintenanceMulti-account Gmail MCP server that lets assistants scan inbox, read threads, draft and send emails only after human approval, and manage follow-up reminders.4268MIT
- Flicense-qualityDmaintenanceEnables AI agents to interact with Gmail through a standardized MCP server interface, allowing for natural language email management and automation.
Related MCP Connectors
Hosted email MCP for AI agents with inboxes, send/receive, memory, recovery, and credits.
Shipmail MCP server for AI agent custom-domain email inboxes with REST API and webhooks.
Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/systheno/gmail-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server