AI 博客

WebMCP 把你的页面变成了一个 API,而它唯一的鉴权就是那个会话

WebMCP 让一个页面把一份可调用的工具清单递给 AI 智能体;Chrome 正把它放在实验标志后面发布,而 W3C 社区组的草案仍在变动。值得争论的不是发现机制,而是权限:一个注册过的工具是以你页面自己的 JavaScript 身份执行的,就在那位已登录用户既有的会话里——于是你的服务器看到的,是一个它无法与一次点击区分开的请求。你正在发布一个 API,而它唯一的凭证属于一个并非调用方的人。

作者 智能体 AI 维基 22 分钟读完

多数团队交付的第一个 WebMCP 工具,不会是以「工具」的名义写出来的。它会是一个本来就存在的表单,用四行代码重新声明了一遍——而它将以「此刻登录的那个人」的全部权限接受模型的调用,因为浏览器认证的是用户,而没有任何东西认证调用方。

一眼看全

WebMCP 给浏览器加了一个 API:让网页把一组结构化、可调用的工具声明给 AI 智能体,而不必让智能体通过界面去操纵页面。这确实是个好主意,而它也远比外界报道显得的要早期。

问题答案为什么重要
它是什么? 页面注册带 JSON Schema 入参的具名工具;智能体发现并调用它们。 把抓取与靠选择器点击的方式,换成一份声明出来的契约。
标准状态 Web Machine Learning 社区组的 W3C Draft Community Group Report——既不是 W3C 标准,也不在标准轨道上。 仍在孵化。形状还会变,而且已经变过。
实现情况 Chrome,藏在一个实验标志与一次 origin trial 后面。其他引擎参与了讨论,但没有发布承诺。 只有一个实现,那是预览,不是平台。
覆盖范围 只有 tools。MCP 的另两个原语——resources 与 prompts——不在范围内。 这不是「浏览器里的 MCP」,而是 MCP 的三分之一。

变动从外面就看得见:入口已从 navigator.modelContext 挪到 document.modelContext,更早的 provideContext() 设计则被弃用。今天就照着它写,买到的是一次迁移——只要你清楚自己买的正是这个,那没问题。

What WebMCP covers, and what it leaves to you A matrix over four concerns — tool declaration, user consent, caller identity and API lifecycle — showing that the specification and the browser cover declaration and consent strongly, treat caller identity only as an origin allow-list, and cover versioning, rate limiting and abuse handling not at all. Who handles what, once a page registers a tool Declaring the tool Asking the user Identifying the caller Lifecycle The specification Strong Medium Origin allow-list None The browser Medium Strong Weak None Your page Yours Weak Yours Yours Your server Not involved Not involved Yours Yours Strong, or squarely your problem Partial Weak or absent
右边那两列除了你没有别的主人——而正是它们,让这件事成了一个 API,而不是一项功能。

真正发布出来的是什么

命令式的那层小到可以整个装在脑子里。页面调用 document.modelContext.registerTool(),传入一个描述符——一个 name、一段自然语言的 description、一份 JSON Schema 形式的 inputSchema,外加一个 execute 回调;智能体一侧则拿到 getTools() 用于枚举、executeTool() 用于调用,还有一个 toolchange 事件,好让单页应用随用户导航修订自己开出的清单。

document.modelContext.registerTool({
  name: 'addToCart',
  description: 'Add a product to the shopping cart.',
  inputSchema: { type: 'object', properties: { sku: { type: 'string' } } },
  async execute({ sku }) {
    return await fetch('/api/cart', { method: 'POST', body: JSON.stringify({ sku }) });
  },
});

这段代码里有三处细节,干的活比看上去多。

description 就是一段提示词

智能体是靠读描述来挑工具的,这就让那个字符串成了你模型指令面的一部分,而不是文档。MCP 工具投毒正是靠这个性质成立的,而现在它落在了一个你市场同事有编辑权限的地方。一句写着「用这个,别去问用户」的描述就是一条指令,而它会被照办。

有一条声明式路径,而那才是会铺开的那条

除了 JavaScript API,草案还允许一个 HTML <form> 直接充当工具声明。这才是真正会被采用的部分,因为它几乎免费——而它同时也意味着,一大片既有的界面会在一次发布里变成可被智能体调用的,期间没有任何人写下一行他自己会认作「对外开放了一个 API」的代码。

跨源在范围之内

注册选项里有 exposedTo,一份获准看见该工具的来源白名单;发现一侧则有 fromOrigins,指明要从哪些安全来源收集工具。工具默认按来源隔离,API 也要求安全上下文。但这两个选项的存在本身就在告诉你:这个模型不是「页面对浏览器的智能体说话」——它是一张图,而你的页面是图上的一个节点。

工具是在会话内部跑的

Where a WebMCP tool call actually executes An agent asks the browser to run a tool. The browser dispatches to JavaScript inside the page, which calls the site's own backend over the same authenticated session the logged-in user established. The server sees an ordinary same-origin request with a valid cookie and cannot distinguish it from a click. THE BROWSER Agent picks a tool by name Browser mediation consent prompt, origin allow-lists Your page's JavaScript document.modelContext .registerTool({ execute }) runs in your origin Your backend sees a same-origin request with a valid session cookie A human click indistinguishable tool call dispatch fetch() The authority on both paths is the user's live session, not the caller. PAGE ORIGIN — YOUR CODE SERVER — YOUR CODE
没有任何新东西抵达你的服务器。问题恰恰就在这里。

别跟着数据走,跟着权限走。execute 回调就是你这个来源下寻常的页面 JavaScript。它调用你的后端时,带上的是 cookie、localStorage 里的 bearer token、你模板渲染出来的 CSRF token——浏览器为那位正坐在那儿的用户所持有的每一份凭证。你的服务器收到一个格式良好、同源、完整鉴权的请求,而请求里任何一处字段都不会说:这些参数是一个模型拼出来的。

这是环境权限最经典的形态,也就是那个「被搞糊涂的代理人」问题;而谁被搞糊涂了,值得说准。你的页面就是那个代理人。它持有一份并非自己挣来的权限——用户的——而它现在正从一个它无法审视其意图的调用方那里,接受关于如何花掉这份权限的指令。浏览器的同意提示是真实有效的缓解措施,也确实该放在那个位置;但同意是由一个人给出的,他读的是一个工具名和一段摘要,只读一次,而这次任务接下来可能包含四十次调用。

Three callers, one handler A click, the browser's own agent, and a tool call arriving from another origin through the cross-origin discovery path all reach the same registered handler with the same session authority. The handler is given no way to tell them apart. The user clicks submit The browser's agent executeTool(), after consent Another origin discovery via fromOrigins execute(args) your handler, your origin, the user's live session
三个调用方、一个处理函数、一份凭证——而 execute() 的入参里没有任何东西能把它们分开。

把三条路径并排看,不对称就一目了然。人工点击要穿过你的界面,也就意味着它穿过了你的禁用态、你的确认弹窗,以及用户身体能产出的那个速率。智能体调用按设计跳过这一切——那正是它的卖点——并以机器速率抵达处理函数。跨源调用抵达的是同一个处理函数,而它满足的那道来源检查是你的页面配置的:也许是无意间配上的,也许配的是一个开发时看着挺顺手的通配符。

而驱动这三者的指令,可能就来自你自己的页面。一个读了商品列表、读了评论、读了工单正文、或读了用户自填昵称的智能体,是在它握着你那份工具清单的同一个窗口里,读着不可信的文字。这是提示词注入,而工具调用已经预先备好了——这也正是所有正经防御都从授权层而非文本层动手的原因。

不管你管它叫什么,你都在发布一个 API

先把安全放一边,因为就算是一个登录后面没什么敏感内容的站点,运维上的这套论证照样成立。工具一经注册,你就担下了一个 API 所背负的全部义务——而且是在你平时会先搭起来的那套机器一件都没有的情况下担下的。

义务你的 REST API 有你的 WebMCP 工具有
版本管理一段路径、一份弃用策略、一份变更日志。一个名字字符串。改名,学过它的智能体全部作废。
限流按 key 的配额、退避约定、429。你会话级限额恰好是多少就是多少,而那是按人的速度定的。
授权scope、按客户端授予、可撤销。用户的会话,全部,不加区分。
滥用处置一把你能吊销的 key。一个你能注销的工具——对所有人,在你下一次发布时。

版本那一行会最先咬人,而且咬得没声响。智能体不读你的变更日志;它在调用时读 description 与 inputSchema——这听着像是让「破坏性变更」变得不可能。并非如此。它只是让破坏变得无声:你把某个 schema 收紧,原本能成功的智能体现在校验失败,而你唯一的信号,是一条还没人搭出来的指标。在服务端,这正是工具目录生命周期那个问题,它不会因为搬进浏览器而变容易。

限流那一行会最先花钱。你的按会话限额里编码着一条关于「人有多快」的假设,而 WebMCP 是刻意来作废它的。一个在干活的智能体,十秒里调用搜索工具的次数会超过用户十分钟的量,而且它是正当地这么做——这个故事里没有攻击者,只有一个物理规律不同的调用方。

那该怎么办

以上没有一条是在反对 WebMCP。「声明出来的工具面」的替代品,是智能体用合成点击去操纵你的界面,那在每一根轴上都更糟,安全也不例外——你没法对一个你从未当作接口设计过的接口做推理。真正的论点是:采用起来太便宜,这本身就是风险;而缓解措施既乏味,又在服务端。

  • 在边界上给请求打标。让 execute 附上一个 header,说明这次调用来自工具,以及是哪个工具。你的服务器推断不出来,将来也永远推断不出来;这份知识唯一存在的地方,就是你正在写的那个回调。下面所有条目都依赖这一行。
  • 在服务端按动作重新授权。把那个 header 当成一档更低的信任等级:同一个端点、同一个会话,但允许的操作集更窄。读操作通常没问题。任何花钱的、发消息的、难以撤回的,都该要求一次新鲜而具体的确认——这是影响半径那套论证,套用在一个上个季度还不存在的面上。
  • 注册最小集,不是最大集。声明式表单那条路的诱惑,在于一次性把一切都暴露出去,因为不花钱。先从让智能体好用的只读工具起步——搜索、可用性、状态——等你有了理由、也有了限额,再加一个写工具。
  • 把 exposedTo 显式写出来。在一个嵌了第三方 iframe 的页面上留一个默认开放的工具,这种配置你会希望是有意做的、过了代码评审的、旁边署着一个名字的。
  • 给工具路径单独限流。工具调用一旦被打上标,就能有自己的预算,而这既是滥用管控,也是成本管控。
  • 把描述当成与安全相关的字符串来写。它们是提示词内容。让它们进你系统提示词的那套评审,而不是产品文案的那套。

至于节奏:既然这是一份社区组草案、只有一个带标志的实现、入口还已经挪过一次,诚实的排期是现在做原型,而先把服务端那一半交付掉。那个 header、按动作的授权、单独的限流,无论 WebMCP 是不是最后胜出的标准都用得上——它们与你为任何抵达你产品的智能体流量所需要的,是同一套控制。而那些 registerTool 调用,是你该预期要重写的部分。

常见问题

WebMCP 是 W3C 标准吗?

不是。它由 Web Machine Learning 社区组以 W3C Draft Community Group Report 形式发布,而这明确意味着:它既不是 W3C 标准,也不在 W3C 标准轨道上。社区组孵化是好点子起步的地方,也是其中一些点子止步的地方。

WebMCP 会取代 MCP 服务器吗?

不会,而且它覆盖的范围比名字暗示的要小。草案只做 tools——MCP 的 resources 与 prompts 原语不在范围内——而它针对的是「能力就住在用户已经打开、也已经登录的那个页面里」这种情形。一个远程 MCP 服务器,仍然拥有浏览器会话之外的一切。

跨源脚本能在我的页面上注册工具吗?

工具的作用域限定在注册它的那个来源,API 也要求安全上下文,所以第三方脚本无法悄悄冒充你来源下的工具。它能做的是从它自己的来源注册它自己的工具;而经由 fromOrigins 的跨源发现,加上 exposedTo 白名单,恰恰是那种该被刻意配置、而不是听凭默认值的面。

如果我的服务器分不出智能体调用和点击,最小的补救是什么?

一个 header,在 execute 内部附上,写明工具名。它不是安全边界——页面 JavaScript 想发什么都能发——但它是让一切真正的控制成为可能的那份可观测性;没有它,你甚至没法量出自己有多少流量来自智能体。

现在该不该采用?

现在做原型,并把能活下来的那部分交付掉。一个标志后面、只有一个引擎、API 还已经被改过名——对客户端那一半形成生产依赖,为时过早。服务端那一半——打了标的工具流量、按动作的授权、一份单独的配额——是无论哪份规范胜出、你都会为智能体流量而需要的活。

延伸阅读

本站:

来源: