小岛AI
| ONLINE |

posts/cloudflare-mcp-optional-scopes.md

Cloudflare 给 MCP 加权限勾选,Agent 仍不该拿全权

小岛AI 2026 / 08 / 24

28 个权限。

一个开发工具想连上 Cloudflare,授权页先摊开这么大一包东西。里面有账户与账单,有开发者平台,还有 Workers、KV、R2、路由这些能直接改生产环境的能力。

好家伙,模型还没开始干活,钥匙串先挂满了。

Cloudflare 在 8 月 22 日发了一条不算长的更新。Wrangler 和 Cloudflare API MCP server 的 OAuth 授权页,现在允许用户关掉不需要的可选 scope,也就是权限范围。必需权限仍然保留,当前任务不需要的读写能力可以先不发给客户端。

页面变化很小,不过多了一个「Edit Permissions」。

我觉得这事比不少 Agent 新功能都重要。因为模型会不会调用工具,决定它能干多少活。凭证允许工具碰哪些资源,决定它翻车时能拆多少房子。

OAuth 授权页把 28 个权限分成必需权限与附加权限

Cloudflare 新授权页会展示权限总数,并提供编辑附加权限的入口。

两个工具背后站着 2500 个接口

MCP 是 Model Context Protocol,中文一般叫模型上下文协议。你可以把它理解成一套让 Claude、Codex、Cursor 之类的 Agent 调用外部服务的统一插座。

插座很方便,权限问题也被它放大了。

Cloudflare 的 API MCP server 有个挺有点子牛逼的设计。它没有把两千多个 API 端点全塞成两千多个工具,而是只暴露 search()execute() 两个工具。模型先搜索 OpenAPI 描述,再写一小段 JavaScript 调用目标接口。

官方给出的数字很有冲击力。传统做法如果把 2594 个端点的完整 schema 全部送进上下文,大约要 117 万 token。Code Mode 只要两个工具,常驻成本约 1000 token。

省得非常漂亮。

但这里有个容易被忽略的错觉。工具列表里只有两个名字,不代表权限也只剩两个格子。execute() 后面仍然连着 DNS、Workers、R2、Zero Trust 以及其他 Cloudflare 产品。界面看起来很克制,凭证可能一点都不克制。

这就是 Agent 工程里一个挺麻烦的错位。我们很容易按「工具数量」感受风险,可系统真正的爆炸半径由 token 能访问的资源和动作决定。一个叫 execute 的工具,只要拿着全量权限,能做的事可能比一整页工具都多。

模型也不需要怀有恶意才会出事。需求写得含糊一点,网页里混进一段提示注入,或者重试逻辑把一次写操作执行两遍,都可能把「能做」变成「真做了」。安全边界如果只靠 prompt 里一句「请谨慎操作」,那跟在生产数据库旁边贴张「不要误删」差不多。

Cloudflare 这次改动补的,正是凭证这一层。

可选 scope 不是新概念,愿意让人少给才是进步

MCP 的授权规范 把远程 MCP server 视作 OAuth 资源服务器,客户端代表资源所有者拿 access token 去访问受保护能力。协议把登录、发 token、带 token 请求这些路线接了起来,但具体给多大权限,仍然要靠授权服务器和产品设计。

OAuth 很早就允许部分授权。RFC 6749 的 scope 章节 写得很清楚,授权服务器可以按照策略或资源所有者的选择,只批准客户端请求的一部分 scope,并把实际授予的 scope 告诉客户端。

标准早就留了门,产品不一定给你门把手。

很多授权页过去只有两个选择,要么全给,要么取消。开发者着急把工具跑起来,通常就一路点确认。权限从「这个任务需要什么」悄悄变成「这个应用将来可能用到什么」。可能两个字,养出了多少常驻全能 token,大家心里都有数。

Cloudflare 更新后的授权说明里,所有请求权限默认仍然选中,但用户可以一键只保留只读权限,也可以按分类或单项关闭可选权限。必需权限不能取消,没有配置成可选的客户端也不会出现编辑入口。

棒棒的,至少「全给」终于不再是唯一能继续往下走的按钮。

OAuth 权限编辑器允许在只读、全权限和逐项 scope 之间选择

用户可以按类别或单项关闭可选 scope,必需权限仍会保持选中。

不过,勾选框没有自动解决后面的工程问题。

客户端拿到缩水后的 token,必须知道自己实际得到了什么。如果 Agent 搜索计划时仍能看见一堆无权执行的动作,它就会认真规划一条终点必然撞墙的路线。前面读了日志、改了配置、创建了资源,走到收尾步骤才收到权限错误,留下的不是一个红色报错,而是半套已经生效的系统状态。

权限粒度和任务粒度也不总能对齐。截图里的 Workers Write 描述覆盖 zones、KV、R2、scripts 和 routes。某个任务也许只想更新一段 Worker 脚本,却不得不面对一个跨多类资源的权限包。scope 比全局管理员小很多,但离「只够完成眼前这一步」还可能差一截。

还有重新授权。Cloudflare 的规则是,如果命令需要此前拒绝的 scope,客户端就得重新走授权。短任务里多点一次还能接受。一个跑了四十分钟、已经产生副作用的长任务被卡在这里,怎么暂停、怎么恢复、会不会把前半段再跑一次,都是运行时要回答的问题。

所以,可选 scope 是好消息,却不是可以把权限设计从待办里划掉的理由。

权限不足要在计划阶段报,不要在生产阶段报

假设一个常见场景,Agent 要发布预览环境。它需要读取项目配置,上传 Worker,创建一个 KV namespace,再绑定路由。需求里没有改 DNS,也没有碰账单。

如果直接给全量 token,任务大概率很顺。代价是任何错误指令都能顺手越过项目边界。

如果人工凭感觉砍权限,另一种麻烦会冒出来。Agent 前三步都成功了,绑定路由时才发现缺少写权限。Worker 和 KV 已经存在,路由没接上。用户看到「失败」,云端看到一堆孤儿资源。

嘶,这就不是补一个 scope 再重试那么简单了。

更稳的做法,是先把任务翻译成权限清单,再开始产生副作用。下面只是示意,不是 Cloudflare 的真实配置格式。

task: deploy-preview
allow:
  - workers.read
  - workers.write
  - kv.write
approval_required:
  - routes.write
deny:
  - dns.write
  - billing.write
token_ttl: 30m

Agent 先输出计划,运行时根据计划算出所需权限,再和 token 实际拿到的 scope 做差集。缺权限就停在零副作用阶段,告诉人类「还需要 routes.write,用十分钟,完成后撤销」。权限齐了再执行。

这一步很像编译。自然语言需求是源代码,工具计划是中间表示,scope 清单是类型检查。类型没过,程序别跑。

坦率讲,这比让模型记住一百条安全提示靠谱得多。模型可能漏看 prompt,资源服务器不会因为它语气诚恳就放过一个缺 scope 的 token。

工具发现也要跟着实际权限收缩。无权调用的工具,要么不出现在搜索结果里,要么明确标成不可用,并在规划时给出缺少的 scope。别让 Agent 花十分钟写出一套漂亮方案,执行器再冷冷回一句 403。权限错误越早,修复成本越低。

写操作还应该有独立确认。读日志、列资源可以在只读 token 下自动跑。更新 Worker、修改 route、删资源这种动作,进入执行前展示目标资源、预期 diff 和回滚动作,让人确认一次。确认记录要绑定 run_idtool_call_id 和具体资源,不能拿一句模糊的「允许本次操作」给后面所有写请求当通行证。

再往下是幂等和审计。一次写请求因为网络超时没有返回,Agent 不知道服务端到底成功没成功。它若原样重试,可能创建第二份资源。每次写入都该有幂等键,审计日志至少要记下调用身份、实际 scope、目标资源、请求摘要、结果和回滚状态。Cloudflare 本身也提供专门的 Audit Logs MCP server,拿它做事后查询挺方便,但真正的运行记录仍然要能从一次 Agent 任务追到每个 API 调用。

短期 token 是另一层保险。授权只覆盖任务预计时长,到期后自动失效。长任务需要续期时,不要静默换成更大的凭证,更不能失败后偷偷回退到环境变量里的全局 API key。不是哥们,前面费劲砍掉权限,异常分支又把万能钥匙掏出来,那前面是在表演安保吗。。

一把 token 不够描述一次任务

OAuth scope 描述的是客户端被允许触碰的能力,它并不知道 Agent 此刻为什么要碰,也不知道这次任务结束后还该不该继续碰。

这两个问题看起来接近,放到生产里差得很远。

一个 token 可以只带 Workers Write,不算宽。可如果十个 Agent 运行共享它,任何一条运行都能代表另外九条运行去写。如果它能活九十天,今天批准的预览发布,三个月后还可以拿同一把钥匙改生产。如果它覆盖整个账户,任务只想动一个 Worker,却仍然可能碰到旁边二十个项目。

scope 变少了,身份、资源和时间边界仍然可能很大。

我更愿意把一次授权判断看成六个字段。谁在调用,代表哪次任务,要操作哪个资源,准备做什么动作,权限能活多久,哪位人类批准了哪一步。

下面同样只是运行时记录的示意。

principal: agent-run-8f21
task_id: deploy-preview-174
resource: workers/my-preview
action: update-script
granted_scope: workers.write
expires_at: 2026-08-24T15:30:00+08:00
approval_ref: approve-6c02

这份记录里,token 只回答「能不能」。任务策略还要回答「该不该」。

身份要细到一次 Agent run,而不是笼统记成某位员工登录。员工完成 OAuth 同意后,运行时可以为当前任务派生一个短生命周期会话身份,并把它和 run_id 绑在一起。两个并发任务即使来自同一个人,也不该在审计日志里糊成同一个调用者。

资源边界要尽量贴近目标对象。授权服务器如果只能给到产品级 scope,执行层还得再加账户、zone、Worker 名称或资源标签的 allowlist。scope 说你能写 Workers,不代表这次任务能写账户里所有 Workers。

动作也不能只分读和写。创建、更新、删除的风险完全不同。读配置可以自动,生成 diff 可以自动,应用 diff 要确认,删除资源除了确认还应要求可恢复方案。把它们全塞进一个 Write,省了策略代码,出事时会把省下的代码连本带利还回来。

时间边界不是简单填一个过期时间。运行时要在开始长步骤之前检查 token 剩余寿命。预计上传加部署需要十二分钟,token 还有两分钟,就该在产生副作用前续期。续期只能延长原有范围,不能顺手把拒绝过的 scope 补回来。

人类确认也得可验证。确认页展示的资源、动作和 diff,要生成不可混用的 approval reference。执行器收到确认后再检查计划哈希是否变化。模型如果在确认后改了目标资源,旧确认立即失效,重新展示。

这些字段看着有点像给一次工具调用办签证,麻烦吗,确实麻烦一点。可 Agent 一旦能操作云资源,模糊的「用户已经授权」已经不够用了。它只能证明门开过,证明不了进门的人现在应该去哪个房间。

还有恢复问题。

每个写步骤执行前,系统要保存检查点和补偿动作。创建 KV 成功,就记下资源 ID 和删除方案。更新 Worker 成功,就保存上一版引用。绑定路由失败,运行时可以停在明确边界,补权限后从未完成步骤继续,或者按补偿动作退回去。

这不一定要做成重量级分布式事务。很多场景里,一个稳定的幂等键、一份步骤状态表、写前版本号和清晰的补偿函数已经够用。关键是别让重新授权等于整条任务重新跑。

真正该验收的是拒绝之后会发生什么

权限系统最容易在顺风局里显得很安全。只读 token 执行只读任务,当然一切都好。要测,就得故意拒绝它。

我会把下面几种情况放进上线门禁。

  1. 只读 token 收到写请求时,任务要在计划或执行前检查阶段停止,不能先改一半资源。

  2. 用户关掉某个可选 scope 后,Agent 的工具搜索与计划必须反映实际授权,不能继续假装那项能力可用。

  3. 运行中需要补权限时,系统要从检查点恢复,只执行尚未完成的步骤,不能把前面的写操作整段重放。

  4. token 被用户撤销或过期后,所有调用都要失败关闭,并留下清晰审计记录,不能回退到另一把更大的钥匙。

  5. 工具返回内容里出现诱导指令时,模型可以被骗,token 不能被扩大。授权边界必须在模型之外。

  6. 写操作的确认要绑定具体资源和 diff。把确认范围从「允许 Agent」缩到「允许这次改这个对象」。

这些测试不花哨,也不会在演示视频里让人惊呼厉害了。可它们决定了 Agent 是一个能进生产的执行系统,还是一个拿着管理员 token 的聊天窗口。

验收时还可以顺手记四个数字。一次任务授予了多少 scope,真正使用了多少。权限拒绝发生在计划阶段还是产生副作用之后。补权限后有多少任务能从检查点恢复。失败任务留下了多少孤儿资源。

这几项会比「授权成功率」诚实很多。授权成功率高,可能只是大家都拿了全量权限。真正健康的系统,应该让未使用权限越来越少,让权限错误越来越早,让重新授权越来越不需要人工收拾残局。

也别迷信把权限利用率做到百分之百。某些只读 scope 会作为诊断备用能力保留,硬追满分反而逼着团队拆出一堆难维护的 token。指标的作用是暴露异常。一个只改 Worker 脚本的任务,最终用了两个权限却拿了二十八个,这种落差值得追。一个故障处理任务预先声明会读取日志、回滚版本,实际都用上了,多拿一两项只读权限未必需要大动干戈。

权限治理不是比谁的 scope 最少,而是每一项多出来的能力都能解释,每一次扩大范围都有人知道,每一次任务结束都真的把钥匙收回去。

Cloudflare 的 Wrangler 登录流程已经把授权、设备码与权限选择放到用户能看见的位置,Cloudflare One 的 MCP 安全接入文档也在处理身份和受保护资源之间的连接。下一步更值得期待的是,客户端和 Agent 运行时把「实际获得了哪些 scope」变成规划、工具发现、暂停恢复和审计里的第一等数据。

我对这次更新的评价很直接。方向对,而且早该做。

但 28 个权限不该只是一页你懒得看的授权说明。它们应该变成任务可编译、运行可观测、失败可恢复的一份合同。

勾选框只是门把手,门后还得有锁。