跳到主要内容

WebMCP — 声明式表单工具

本章讲 WebMCP 的「不写 JS」分支:光给 HTML <form> 加几个属性,浏览器就自动把它变成一个工具。讲清属性映射、表单与命令式的等价关系、提交回流、以及哪些算法目前还是 TODO。

1. 为什么需要声明式

命令式 registerTool(§01)要求写 JS。但网页大量功能是用语义 HTML(尤其 <form> 和各种 <input>)实现的。声明式 API 的动机就是:让作者不重写 JS,直接复用已有表单(declarative-api-explainer.md:18-33)。

它要做三件事(declarative-api-explainer.md:24-33):

  1. <form> 和表单关联元素加新属性,把它们暴露成工具。
  2. 用算法确定性地把表单「编译」成一个 WebMCP input schema,让 agent 知道怎么填、怎么提交。
  3. 把表单的响应回传给 agent。

2. 属性映射:HTML 属性 ↔ 工具字段

核心属性加在 <form> 上(declarative-api-explainer.md:37-57):

HTML 属性对应命令式字段作用
toolnameModelContextTool.name工具名
tooldescriptionModelContextTool.description工具描述
toolautosubmit(布尔)(无直接对应)允许 agent 填完后自动提交;不写则只聚焦提交按钮,让用户手动确认

参数(schema 的每个 property)来自表单控件(declarative-api-explainer.md:59-67):

  • 控件的标准 name 属性 → schema 里 property 的名字。
  • 新增的 toolparamdescription 属性 → 该 property 的 description(因为 HTML 没有现成的描述属性)。

等价关系一眼看懂。 explainer 给了对照(declarative-api-explainer.md:69-95):下面这段命令式登记……

// declarative-api-explainer.md:71-84(节选)
await document.modelContext.registerTool({
name: "search-cars",
description: "Perform a car make/model search",
inputSchema: { type: "object", properties: {
make: { type: "string", description: "The vehicle's make (e.g., BMW, Ford)" },
model: { type: "string", description: "The vehicle's model (e.g., 330i, F-150)" },
}, required: ["make", "model"] },
execute({make, model}) { /* ... */ }
});

……等价于这段纯 HTML(注意 required 属性变成 schema 的 required):

<!-- declarative-api-explainer.md:89-94 -->
<form toolname="search-cars" tooldescription="Perform a car make/model search">
<input type=text name="make" toolparamdescription="The vehicle's make (i.e., BMW, Ford)" required>
<input type=text name="model" toolparamdescription="The vehicle's model (i.e., 330i, F-150)" required>
<button type=submit>Search</button>
</form>

3. 生命周期:表单变动如何牵动工具

声明式工具的生死绑在 DOM 上(declarative-api-explainer.md:55-5799-105):

  • 带这些属性的表单被插入/移除/属性更新时,会创建一个新的声明式工具,其 input schema 按合成算法生成。
  • 表单被 reset,或其工具声明改变(如改 toolname),会取消在途调用并通知 agent。

这与命令式的「AbortSignal 注销」(§01)是同一思路的不同触发器:都把工具生命周期绑到某个可观察的状态变化上。

4. 把响应回传给 agent:两条路

表单提交后,结果怎么回到 agent?explainer 给了两条路(declarative-api-explainer.md:29-33157-173):

路 A:SubmitEvent.respondWith() —— 不导航,JS 手工造响应。 SubmitEvent 新增两个成员(declarative-api-explainer.md:166-173):

// declarative-api-explainer.md:167-172
interface SubmitEvent : Event {
readonly attribute boolean agentInvoked; // 是不是 agent 发起的提交
undefined respondWith(Promise<any> agentResponse); // 覆盖默认提交,把响应喂给 agent
};

用法:在 submit 事件里先 preventDefault(),再调 respondWith(promise),表单的 action不会导航,promise resolve 的值成为 agent 拿到的响应(declarative-api-explainer.md:161-164)。

路 B:导航后抓 application/ld+json(有争议)。 若表单确实导航到新页面,旧提案是把目标页第一个 <script type=application/ld+json> 当作工具响应(declarative-api-explainer.md:121-133)。但这条目前正在被争论,挂在 Issue #135 上(declarative-api-explainer.md:118-119),所以放在 <details> 折叠块里。

5. 给作者的 UI 钩子:伪类与事件

表单被 agent 填好、等用户确认时(即没加 toolautosubmit),作者可能想高亮它。为此引入两个 CSS 伪类(declarative-api-explainer.md:136-154):

伪类匹配什么
:tool-form-active声明式工具正在「运行」中的 <form>
:tool-submit-active上述表单的提交按钮

「运行中」的定义:从表单开始被 agent 输出填充,直到表单被 reset/移除、respondWith 的 promise resolve、toolname/tooldescription 改变、或因 toolautosubmit 自动提交为止(declarative-api-explainer.md:143-150)。

对应还有两个事件,fire 在 ModelContext 上(declarative-api-explainer.md:175-188):toolactivated(工具被填好但提交前,给作者机会引起用户注意)和 toolcanceled(agent 取消调用时)。

6. 边界与局限(这章一半是 TODO)

声明式 API 是 WebMCP 里最不成熟的部分,要诚实标出:

  • 规范主体里几乎全空。 index.bs 的声明式一节明写 “This section is entirely a TODO”,合成算法只有一行 “TODO: Derive a conformant JSON Schema object…”(index.bs:559-570)。本章内容主要来自 explainer(declarative-api-explainer.md),不是规范正文。
  • schema 合成算法未定。 怎么把 <input>/<select>step/min/max 归约成带 anyOf/oneOf/maximum 的 JSON Schema,explainer 也标 TBD;Chromium 在试一个「宽松版」并做试验(declarative-api-explainer.md:107-115)。
  • outputSchema、声明式工具如何暴露给 JS 都是 open question(declarative-api-explainer.md:202-212)。

7. 代码地图

主题文件符号 / 锚点
动机与三件事declarative-api-explainer.md:6-33Motivation
表单属性declarative-api-explainer.md:36-57toolnametooldescriptiontoolautosubmit
参数描述属性declarative-api-explainer.md:59-95toolparamdescription
提交回流declarative-api-explainer.md:118-173SubmitEvent.respondWithagentInvoked
伪类declarative-api-explainer.md:136-154:tool-form-active:tool-submit-active
事件declarative-api-explainer.md:175-188toolactivatedtoolcanceled
规范占位(待实现)index.bs:559-570synthesize a declarative JSON Schema object algorithm