关于 MCP Write Gate
MCP Write Gate 是位于 AI Agent 与真实 MCP 服务之间的本地写入控制层,面向连接 CRM、邮件和协作系统的 GTM 与运营团队。它让读取请求通过,并按列表、只读模式和审批规则拦截写入,同时保管密钥并记录调用。
主要功能
- 01按目标列表检查写入
- 02支持强制只读模式
- 03未命中名单时可要求审批
- 04配置联系频率上限
- 05保护 API 密钥并记录调用
适用场景
- 01限制 Agent 修改 CRM 客户记录
- 02阻止向客户或未联系对象发送邮件
- 03测试规则而不实际发送请求
MCP 相关
MCP 客户端连接此服务而非后端 MCP 服务;它转发获准的读取和写入工具调用,并拒绝不符合规则的请求。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
mcp-write-gate
mcp-write-gate 位于 AI Agent 与其通过 MCP 使用的工具之间,例如电子邮件、CRM、Smartlead 或 Slack。Agent 可以自由读取,但每次写入都会在发送前经过检查;任何面向客户、正在进行的交易或你列表中其他对象的操作,都会在到达工具之前被拒绝。
我为 GTM 和运营团队构建了它:这些团队会将 Claude Code 之类的 Agent 连接到真实系统,并希望制定一条 Agent 无法辩驳绕过的规则。最常见的设置是只读模式:你使用完整权限范围的 API 密钥连接 Smartlead、HubSpot 或 Salesforce,Agent 可以查看所有内容,但不能更改任何内容。
+---------------+ +------------------+ +-------------------+
| AI agent | tool call | mcp-write-gate | allowed calls | Real MCP server |
| (Claude Code, | ------------> | | ---------------> | (HubSpot, Gmail, |
| Cursor, ...) | <------------ | | <--------------- | Smartlead, ...) |
+---------------+ the result, +--------+---------+ results +-------------------+
or why it |
was refused v
+-----------------------------+
| read -> forward |
| write, safe -> forward |
| write, listed -> refuse |
| write, unsure -> hold |
| read-only -> refuse |
| every call -> log |
+-----------------------------+
工作原理
Agent 连接到 mcp-write-gate,而不是实际服务,并看到相同的工具、相同的名称和输入。调用工具时,由网关决定如何处理。读取操作会直接通过。写入操作会根据你的列表进行检查;如果涉及客户、正在进行的交易或任何标记为“禁止联系”的对象,就会被拒绝,并告知 Agent 原因。任何内容都不会到达工具。默认情况下,写入不在任何列表中的对象会通过;你也可以选择让它等待人工批准。你还可以限制联系同一个人的频率,例如每周最多发送一封电子邮件;在只读服务上,无论密钥允许什么,所有写入都会被拒绝。
Agent 始终不会持有真实 API 密钥。密钥保存在网关自己的文件夹中,网关也会从服务返回的任何内容中移除密钥。每次调用,无论通过还是被拒绝,都会记录在日志中;如果有人之后编辑日志,你也能看出来。
快速开始
你需要 Python 3.10 或更高版本。
pip install mcp-write-gate
mcp-write-gate init
mcp-write-gate add mail -- npx -y your-mail-mcp-server
init 会在你的用户文件夹中创建网关文件夹,与项目隔开。文件夹中包含存放规则和服务的 gate.json、起始列表 lists/off-limits.csv、用于存放真实服务 API 密钥的 keys.env,以及日志。
add 会启动一次真实服务,读取其工具,并判断哪些工具用于读取、哪些用于写入。它会打印每项判断,以及供 Agent 使用的 MCP 配置块。检查这些判断,然后将该配置块粘贴到原来真实服务配置所在的位置。在 Claude Code 中只需运行一条命令:
claude mcp add mail -- mcp-write-gate serve mail --config "<gate folder>/gate.json"
远程服务通过 URL 添加,其密钥保存在 keys.env 中,并以 ${NAME} 的形式引用。使用 OAuth 的服务需要由人工登录一次,之后网关会自行刷新令牌:
mcp-write-gate add crm --url https://mcp.example.com/mcp --header 'Authorization: Bearer ${CRM_TOKEN}'
mcp-write-gate add crm --url https://mcp.example.com/mcp --oauth
mcp-write-gate login crm
在依赖某条规则之前,你可以先测试,而不会发送任何内容:
mcp-write-gate check mail send_email --args '{"to": ["[email protected]"]}'
只读模式
mcp-write-gate add smartlead --preset read-only --url 'https://mcp.smartlead.ai/sse?user_api_key=${SMARTLEAD_KEY}'
mcp-write-gate reads smartlead
mcp-write-gate confirm smartlead get_campaigns get_campaign_stats
在只读模式下,所有写入都会被拒绝,任何列表、模式或审批都无法改变这一点。只有在以下情况下,读取操作才会通过:服务将工具标记为只读,且工具名称看起来像读取操作;或者人工使用 mcp-write-gate confirm 确认了该工具。mcp-write-gate reads 会列出每项读取操作以及确认者。
网关不会对服务提供的信息照单全收。名为 delete_campaign、但被服务标记为只读的工具仍会被视为写入操作。如果服务在你确认后更改了某个工具,该工具会被拒绝,直到你再次确认。试图通过参数更改内容的读取操作也会被拒绝,例如 HTTP DELETE、执行写入的 SQL 语句或 GraphQL mutation。
因此,只要满足以下四项条件,将完整权限范围的密钥放在网关后面就是安全的:
- 网关是 Agent 访问该工具的唯一途径。
- Agent 无法读取网关文件夹,也无法以网关用户的身份运行代码,否则它可能找到密钥并直接调用 API。docs/security.md 介绍了三种防范方法,其中最强的是使用独立容器。
- 确认每项读取操作前都已检查。对于
call_api或run_query这类通用工具,需要格外谨慎,因为供应商 API 可能会在 GET 请求中执行删除操作,看起来却像读取操作。 - 密钥仍应采用供应商提供的最小权限范围。网关是第二道锁,所以第一道锁也要保留。
列表
列表是 lists/ 文件夹中的纯 CSV、TXT 或 JSON 文件,因此你可以从 CRM 导出,也可以手动维护。每个文件都会被读取,文件更改会在下一次调用时生效。
target,status,note
example.com,customer,paying account
[email protected],open_deal,in a live deal
[email protected],allow,partner contact inside a customer
#sales-internal,blocked,chat channel the agent must not post in
*/contracts/*,blocked,signed contracts stay untouched
域名规则涵盖其所有子域名;对于邮箱地址,使用 +标签 或 Gmail 点号写法的同一邮箱也适用。当两行都匹配时,更具体的一行优先,因此 [email protected],allow 可以位于某个客户的域名规则之下。门禁无法识别的状态会被视为拒绝,因此即使写错也不会放行。如果真实数据在 CRM 而非文件中,门禁可以针对未列入任何列表的对象运行查询命令,详见 docs/configuration.md。
使用真实服务测试
| 服务 | 测试内容 |
|---|---|
| Smartlead | 将全部 170 个工具分类为读取和写入。回复潜在客户、启动营销活动和转发电子邮件的操作均遭拒绝,且未发送;已确认读取操作可以通过,API 密钥也从未传到 Agent。 |
| HubSpot | 读取操作通过并到达 HubSpot。创建记录和记录互动的操作均遭拒绝,且未发送。 |
| Gmail | 将全部 23 个工具分类。草稿、标签、垃圾邮件和废纸篓操作均视为写入。 |
| Salesforce | 将全部 74 个工具分类。部署、权限更改和 Apex 测试均属于写入,查询属于读取。 |
| Notion | 将全部 24 个工具分类。创建、编辑、移动和删除页面均属于写入,获取页面属于读取。 |
| MCP filesystem 和 everything servers | 将文件路径作为目标、资源、提示词、补全内容和长时间运行的调用进行测试。 |
我还使用 Claude Code 作为 Agent 进行了测试。不启用门禁时,Claude 会发送被要求发送的电子邮件。启用只读模式的门禁后,电子邮件未被发送,日志中也显示了拒绝记录。
它无法防护的情况
门禁会检查调用涉及的对象和目标,但不会检查消息内容,因此不会拦截邮件正文中提到某个客户的名字、却发给其他人的情况。它无法查看加密载荷、自定义二进制格式或类似 contact 123 这样的裸记录 ID 中隐藏的目标。对于 ID,你可以添加查询命令;对于其他情况,可以将相应工具设置为拒绝任何不在列表中的对象。它无法阻止以与门禁相同的 OS 用户身份运行代码的 Agent,因此 Dockerfile 会在独立容器中运行门禁。此外,它只能控制 Agent 向服务发送的内容,因此只应将你信任的服务置于其后。
了解更多
docs/configuration.md 介绍了 gate.json 中的所有设置:目标、预设、速率限制、查询命令、暂存调用和审批页面、通过 HTTP 提供服务,以及完整的命令列表。docs/security.md 说明了如何防止 Agent 接触门禁、机密信息的存放位置、日志如何显示篡改记录,以及攻击测试覆盖的内容。
运行测试
pip install -e ".[dev]"
pytest
测试会在门禁后启动一个真实的 MCP 服务,并通过官方 MCP 客户端与其通信,支持 stdio 和 HTTP。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。