iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Modern Web

別再讓 Agent 猜按鈕:30 天打造並實測 Agent-ready 的 WebMCP 活動網站系列 第 11

Day 11|什麼是 Declarative API?讓 HTML 表單成為 WebMCP Tool

  • 分享至 

  • xImage
  •  

Day 11|什麼是 Declarative API?讓 HTML 表單成為 WebMCP Tool

安安~我是ChiYu~

昨天打開 Inspector 後,Chrome 終於把目前頁面的 Tool 列了出來。面板裡有名稱、
description,還有一大段 input schema。

看到 catalog 當然很開心,但我很快又卡在下一個問題:我並沒有另外寫一份 JSON Schema,
這些欄位到底是從哪裡冒出來的?

如果直接研究完成版搜尋頁,表單、API、畫面更新與商業規則會全部擠在一起。每個東西都像
答案,也就很難知道真正的答案是哪一個。因此我先把正式網站放到旁邊,另外做了一張最小
搜尋表單,只觀察 HTML 如何變成 Declarative Tool。

先切到只有 Declarative Tool 核心的版本

今天使用的程式里程碑是 v3-day-11。先把版本切對,再往下看表單,不然目前的 main
已經累積了後續的自動送出與參數說明,很容易把明後天才會出現的答案一起帶進來:

git switch --detach v3-day-11
npm ci

這個版本只替原本可操作的搜尋表單加入 Declarative Tool 的核心身分,還沒有
toolautosubmitrespondWith() 與正式 Agent submission。換句話說,今天可以驗證三件事:

  1. 人類仍能使用同一張表單搜尋活動。
  2. toolnametooldescription 會形成穩定的 Tool 身分。
  3. 表單欄位會提供 schema 所需的參數語意。

tag 內的 Lab 路徑仍保留早期名稱 labs/day-08-declarative-tool/,這只是資料夾名稱沒有隨文章
天數搬家;重播座標仍以 v3-day-11 為準。

HTML 表單語意形成 Declarative Tool 契約

圖 1:Tool 的身分來自 form,表單欄位則提供參數語意。這張圖說明 E2 契約;實際產生的 schema 仍要以瀏覽器輸出為準。

Declarative API 如何把現有表單變成 WebMCP Tool?

先把 Declarative Tool 拆開來看。它不是要我們用 JavaScript 另外重寫一套功能,而是替
現有的 HTML <form> 加上 WebMCP 標註。

瀏覽器會讀取表單的 toolnametooldescription,再從欄位的 name、label、選項與
限制條件整理出結構化的 input schema。於是同一張表單會有兩種讀法:人類看到輸入框、
下拉選單與按鈕;Agent 看到 Tool 名稱、用途與可接受的參數。

換句話說,我不用另外維護一份「Agent 專用搜尋 API 說明」。原本就存在的表單,補上必要
標註後,便能成為瀏覽器可以公開給 Agent 的 Tool。

Declarative Tool 可以做什麼 在這個活動網站的例子
把既有 HTML form 公開成 Tool 將活動搜尋表單公開為 search_events
從表單欄位形成結構化參數 整理出 querylocationpricelevel
讓 Agent 將參數填回可見表單 把「台北、免費、入門」放進對應欄位
保留人類原本的操作方式 沒有 WebMCP 時仍可手動填表並搜尋
視設定決定是否自動送出 後續會再加入 toolautosubmit 與 Tool result

Declarative Tool 最適合原本就能用表單表達的任務,例如搜尋、篩選、建立客服需求或準備
預約資料。它的優點是人類 UI 與 Agent Tool 共用同一份欄位語意,不必各自維護一套契約。

但它不是所有功能的萬用轉接頭。像「讀取目前 route 的活動」「依頁面狀態動態註冊或移除
Tool」,沒有一張固定表單可以承載,這種情況就比較適合使用明天會介紹的 Imperative API。

還有一條界線要先畫清楚:表單成功註冊成 Declarative Tool,只代表網站已經把能力與參數
說明交給瀏覽器。它不能保證 Agent 一定會選中這支 Tool,也不會替 server 完成權限、安全
與商業規則檢查。

所以今天只拆解「Tool 是怎麼從 HTML 形成的」。真正的 Agent discovery、參數選擇與
invocation,仍要交給後面的 Inspector trace 驗證。

先讓同一張搜尋表單繼續服務人類

這份 Lab 原本就是一張可以操作的 HTML 表單。人類選擇地點、費用與程度,按下「搜尋活動」
後,頁面仍會顯示符合條件的結果。

我沒有另外複製一張「Agent 專用表單」,只在原本的 <form> 補上兩個 Attribute:

<form
  id="event-search"
  toolname="search_events"
  tooldescription="依關鍵字、地點、費用與程度搜尋目前公開活動,並更新使用者可見的活動列表。">
  <!-- query、location、price、level -->
</form>

toolname 是 Tool 的穩定識別;tooldescription 則告訴 Agent,這支 Tool 會做什麼、
資料範圍在哪裡,以及結果會如何呈現在頁面上。

這兩個 Attribute 必須成對出現。Chrome 的 Declarative API 文件寫得很直接:移除其中
任何一個,這張 form 就不再註冊為 Tool。也就是說,按鈕上的「搜尋活動」可以改成
「找活動」,search_events 卻不應該跟著 UI 文案每天換名字。

property name 與允許值,原本就藏在表單欄位裡

Tool 有了名稱,接著輪到參數。Declarative API 會從表單欄位整理 property name、說明與
允許值。HTML constraint 也留在同一張表單裡,但是否被映射成特定 JSON Schema keyword,
仍要看當下瀏覽器輸出,不能只靠原始碼先替它宣布答案:

HTML 線索 人類在畫面上看到什麼 Tool schema 得到什麼
<label for="location"> 「地點」 參數用途
name="location" 不直接顯示 穩定的 property name
<option value="taipei"> 「台北」 可接受的 enum value
maxlength="100" 關鍵字長度限制 本篇只驗證 HTML constraint,不預先宣稱 schema 映射結果

這裡最容易被忽略的是 nameid 讓 label 能找到欄位,name 才是提交資料與 schema
使用的鍵。畫面上兩者常常剛好寫成一樣,所以少寫一個時,肉眼不一定立刻看得出來。

toolparamdescription 可以替個別參數補上更精確的說明,但它不是今天這個最小版本的主角。
欄位已經有正確關聯的 label 時,瀏覽器會先沿用 label 內容。等參數語意無法只靠「地點」或
「程度」說清楚時,再補 toolparamdescription,比每個欄位先貼一段重複文案更實在。

欄位少了 name,畫面照常出現,契約卻少了一塊

接著我故意把穩定鍵拿掉:

<label>
  地點
  <select>
    <option value="taipei">台北</option>
  </select>
</label>

瀏覽器還是會畫出一個寫著「地點」的下拉選單,人類也看得懂。問題出在資料送出時沒有
欄位名稱,Tool 無法穩定取得 location 這個 property。畫面沒有爆炸,契約只是安靜地
缺了一角,這種 bug 最擅長在 Demo 時裝沒事。

另一個常見誤會是把 placeholder 當成規格。placeholder 適合提示輸入範例,卻會隨著文案
調整;拿它代替 name 或欄位說明,等於讓 API 契約跟著行銷文案一起漂流。

所以我替這張表單留下的規則很單純:可見文字要讓人類理解,name 與允許值要讓資料契約
保持穩定,HTML constraint 則繼續保護人類操作。兩邊描述的是同一個搜尋任務,至於 Chrome
最後合成哪一段 schema,交給實際 catalog 回答。

實際驗收表單與 Tool 契約

啟動網站後,開啟 Lab、選擇台北並送出,先確認人類操作仍能得到「WebMCP 入門工作坊」。
畫面能用之後,再執行兩支 focused tests:

npm test -- tests/unit/day-08-declarative.test.ts
npx playwright test tests/browser/day-08-declarative.spec.ts

unit test 鎖住 search_events 與 description,避免重構時偷偷改掉 Tool 身分;browser
test 則真的操作表單,確認多了 WebMCP Attribute 之後,人類 fallback 沒有被弄壞。

這些結果屬於 E2:程式契約與 deterministic tests 可以重播。它們還不能證明真實 Agent
會從自然語言選中 search_events,也沒有證明 Inspector 在這個 Lab 上完成 discovery。
昨天看見 catalog,今天拆出 schema 來源,兩份證據各自回答不同問題,不混在一起比較安全。

目前 main 的讀者版 Lab 已經累積後續功能,所以你會看到 toolparamdescription
toolautosubmit。那不是今天的程式壞掉,而是未來版本已經往前走了;要重播本文,就留在
剛才切好的固定 tag。

表單會描述能力了,動態頁面卻沒有固定的 form

今天完成後,同一張搜尋表單有了兩種讀法:人類照常看 label、選條件、按按鈕;瀏覽器則從
toolnametooldescription 與欄位語意整理出 Tool 契約。

搜尋表單一直待在頁面上,Declarative API 很適合它。活動詳情就麻煩了:使用者進入某場
活動時才應該看見 get_event_details,離開後舊 Tool 也得跟著消失。明天我們會改用
Imperative API,處理 Tool 的註冊、去重與解除,看看上一場活動的 event ID 會不會忘記
下班。

參考資料


上一篇
Day 10|什麼是 WebMCP Inspector?從安裝、設定到看見網站 Tool
下一篇
Day 12|什麼是 Imperative API?用 JavaScript 控制 Tool 的註冊與解除
系列文
別再讓 Agent 猜按鈕:30 天打造並實測 Agent-ready 的 WebMCP 活動網站15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言