十万个店铺、一个开关:WebMCP 如何让店铺对 AI Agent「可读」
💡 工具推荐: JSON 格式化、cURL 命令生成器、HTTP 头检查器
过去两年「让网站对 AI 友好」的做法基本是猜:写好结构化数据、祈祷模型能读懂你的 HTML、再在 robots 和 llms.txt 里许愿。WebMCP 换了个思路——别让 Agent 猜,直接把工具给它。它是一个由 Google 提出的浏览器标准,让页面把 search_products 这类能力以结构化工具的形式暴露出来,Agent 直接调用,而不是去翻下拉框、被 cookie 弹窗挡住、或者被反爬挑战拦住。Cloudflare 在 8 月推出了 WebMCP 的边缘实现(beta):打开开关,边缘注入一小段脚本,站长一行代码都不用写。Commerce.com 管着近十万个域名,预览一出来他们的问题很实际:能不能一次给所有店铺打开?这篇文章讲清他们是怎么做到的,以及那个「单个开关 vs 十万个商家」的矛盾怎么解。
Agent 应该直接调用 search_products,而不是去猜下拉框
一、WebMCP 到底解决什么问题
先说清楚它现在的状态,别把它当成成熟标准:WebMCP 目前还是一个处在早期预览阶段的浏览器标准提案,只在实验性 Chrome 开关后可用(chrome://flags/#enable-webmcp-testing)、在 Chrome Origin Trials 里、在某些 Agent 浏览器里,以及像 Cloudflare Kitesurf 这样的开发环境中可用。它的价值在于把「Agent 购物」从视觉猜测变成接口调用:没有 WebMCP 时,Agent 只能模拟人类点击,然后被 cookie 横幅挡住、误解规格下拉框、或者被用来区分机器人和真人的反爬挑战拦下;有 WebMCP 时,Agent 在 document.modelContext 上发现店铺的工具,然后干净地发两次调用。对商家的意义是:店铺出现在答案里,顾客留在自己站点上,结账流程还在自己手里。
<!-- What the browser injects at the edge. Site owners write nothing. -->
<script type="module"
src="/.webmcp/bridge.js"
data-packs="c2pa,mcp-server-client"
data-mcp-url="/api/mcp"></script>
<!-- data-mcp-url matters more than it looks. The bridge originally
assumed every site served MCP at /mcp. Commerce.com's server lives
at /api/mcp, so the path is now a zone-level setting. -->二、为什么 Commerce.com 想要它
Commerce.com 的产品管理总监 Kristina Pototska 说得很直白:商家一直在问怎么跟上 Agent 购物这件事,当购物者让 Agent 去找某个东西时,商家希望自己的店出现在答案里,同时希望留住顾客、保住结账控制权,而且不想为此重建整站。WebMCP 这三点都照顾到了——Agent 拿到真正的工具去搜商品、查详情、建购物车、拿结账链接,不用在页面上瞎摸;购物者仍在商家站内,商家仍握着客户关系。还有一个容易被忽略的设计点:它和服务端 MCP 是并存关系,不是替代。服务端 MCP 面向「直接连到你店铺」的 Agent(比如帮人跨多个网站比价的助手),WebMCP 面向「在购物者浏览器里工作」的 Agent。两个通道背后是同一套购物工具,商家不用二选一,也不用维护两套。
// What an agent sees on the page: structured tools on document.modelContext.
const ctx = document.modelContext;
if (ctx) {
const tools = await ctx.getTools();
console.log(tools.map((t) => t.name));
// ["search_products", "get_product_details", "build_cart", "get_checkout_link"]
} else {
// No WebMCP support: fall back to normal navigation. Never block the page.
console.log("WebMCP not available, continuing as a normal storefront");
}三、边缘注入:站长零代码
底层依赖两个原则。第一,WebMCP 的工具是在购物者当前浏览器会话里、在店铺自己的域名上执行的,所以 Agent 看到的价格和购物车与本人完全一致,没有任何凭据流向第三方,访问依然发生在商家站内。第二,Cloudflare 作为 Commerce.com 后端 MCP 服务器的边缘交付层,原样代理工具定义和动作,商家对自己的工具逻辑保持完整控制。落到实现上就是一段注入脚本,但它有个坑:桥接脚本原本假设每个站点的 MCP 服务都在 /mcp,而 Commerce.com 的在 /api/mcp,所以现在 MCP 路径变成了 zone 级别的配置项,会被传进注入的 script 标签里。这个细节看似琐碎,实际上是「通用边缘能力」和「真实客户部署」之间最常见的摩擦点。
// Per-merchant opt-out, evaluated at the edge. No allowlist, no lookup.
{
"action": "set_config",
"expression": "cf.hostname.metadata.webmcp_opt_out eq \"true\"",
"action_parameters": {
"webmcp_enabled": false
}
}
// Commerce.com enables WebMCP on the main zone, then this rule checks
// merchant metadata. A merchant flipping "opt out" in the dashboard
// produces: no injected script, no extra latency, no remote call.四、核心矛盾:一个开关,十万个商家
Commerce.com 用的 Cloudflare for SaaS 把成千上万个商家自有域名挂在共享 zone 上,而 WebMCP 最初的设计是整个 zone 一个开关——要么全开,要么全关。他们没有去造一套复杂的自定义白名单,而是把 WebMCP 挂到 Cloudflare 已有的两个概念上:Configuration Rules 和自定义主机名元数据。Commerce.com 在主 zone 上打开 WebMCP,再加一条规则在边缘检查商家元数据;如果某个商家在自己的后台里把「退出」开关一拨,边缘就直接对这个店铺跳过 WebMCP。因为规则是在页面返回之前于边缘求值的,被退出的店铺得到的是零额外延迟、零远程查询、零注入脚本。这个模式值得记住:默认全开+细粒度退出,比默认全关+白名单更容易在平台级落地,而且退出成本几乎为零。
# Verify what a storefront actually serves before and after the switch.
check() {
host=$1
echo "== $host"
# is the bridge script present?
curl -sS "https://$host/" | grep -o 'src="/\.webmcp/bridge.js"' || echo "bridge: absent"
# does the MCP endpoint answer?
curl -sS -o /dev/null -w 'mcp_status=%{http_code}\n' "https://$host/api/mcp"
}
check shop-one.example.com
check shop-two.example.com # this merchant opted out -> expect absent
# Guard the failure mode that matters: an opted-out store must look
# byte-identical to how it looked before WebMCP existed.五、失败路径必须写进设计
任何预览功能上线后都会暴露几个没算到的细节,这次也一样。除了自定义 MCP 路径,还有一条更重要的:无缝降级。如果某个店铺的 MCP 服务器挂了,或者用户的浏览器根本不支持 WebMCP,脚本就直接退出——店铺正常加载,真人购物者完全不受影响。最后那段代码就是这个契约的写法:超时 1.5 秒、非 200 就抛错、catch 里只打一行 debug 日志,绝不在页面上给用户看红色错误。这条纪律对任何往线上页面注入能力的团队都适用:注入的脚本是增强,不是依赖;增强失败时页面必须表现得像它从没存在过。另外值得强调,结账那一步仍然由人点,支付信息不经过 Agent——这不是能力不足,是刻意保留的边界。
# The safe-fallback contract, in code.
try {
const res = await fetch(MCP_URL, { signal: AbortSignal.timeout(1500) });
if (!res.ok) throw new Error(`mcp ${res.status}`);
const tools = await res.json();
window.modelContext?.provideTools?.(tools);
} catch (err) {
// Server offline, wrong path, browser without support, slow network:
// exit quietly. Human shoppers must never see an error because of this.
console.debug("webmcp bridge skipped:", err.message);
}六、如果你也想让站点对 Agent 可读
四条可操作的检查项。第一,先想清楚你要暴露哪些工具,并且把它们当成公开 API 来设计——命名要具体(search_products 优于 do_search),参数要可校验,返回要稳定;Agent 不理解你的业务黑话,它只理解 schema。第二,确认你的 MCP 端点路径和边缘注入的默认假设一致,不一致就用配置项传进去,别硬编码。第三,写好降级:超时、非 200、浏览器不支持,三种情况都必须静默退出,绝不能让真人用户看到错误。第四,把退出机制做成商家可自助的开关,并在边缘求值,这样平台级推广才不会被「某个商家不想开」卡住。Cloudflare 说接下来会继续投入 WebMCP 标准演进,第一步是帮 Agent 更容易发现一个站点有哪些工具。如果你在做电商或 SaaS 平台,现在就可以先量一件事:把页面上的商品搜索、详情、加购、结账这几个动作,能不能各自用一句话描述成工具?描述不出来的,通常是你的流程本身还不够结构化。
结账这一步仍然由人点,这是故意的
📌 常见问题 FAQ
WebMCP 现在是正式标准吗?
不是。Cloudflare 明确它是仍处在早期预览阶段的浏览器标准提案,目前只在实验性 Chrome 开关(chrome://flags/#enable-webmcp-testing)后面、Chrome Origin Trials 中、部分 Agent 浏览器以及 Kitesurf 这类开发环境里可用。生产使用必须按预览功能的预期来设计降级路径。
WebMCP 和 MCP 是什么关系?
并存互补。服务端 MCP 面向直接连接到店铺的 Agent,例如帮人跨多个网站购物的助手;WebMCP 面向在购物者浏览器里工作的 Agent。Commerce.com 的说法是两个通道背后是同一套购物工具,商家不需要二选一,也不需要分别维护。
为什么不是给每个商家做白名单?
因为 Commerce.com 挂在 Cloudflare for SaaS 的共享 zone 上有近十万个域名,造自定义白名单既复杂又难维护。他们改用 Configuration Rules 加自定义主机名元数据:主 zone 默认打开,商家在后台一拨退出开关,边缘规则就把这个店铺跳过,零额外延迟、零远程查询、零注入脚本。
启用之后店铺会有性能损失吗?
按 Cloudflare 的描述,规则在边缘、页面返回之前求值,被退出的店铺没有额外延迟、没有远程查询、也没有注入脚本。对于开启的店铺,桥接脚本在 MCP 服务离线或浏览器不支持时会直接退出,页面按正常方式加载,真人购物者不受影响。
支付和凭据会被 Agent 拿到吗?
不会。WebMCP 的工具是在购物者当前浏览器会话中、在店铺域名上执行的,所以价格和购物车与本人看到的一致,没有任何凭据流向第三方。结账这一步仍然由购物者自己点击完成,支付信息不经过 Agent。