前端開發 別再讓 Agent 猜按鈕:30 天打造並實測 Agent-ready 的 WebMCP 活動網站 · 第 14 / 30 篇

Day 14|五支 Tool 不再往外長:把產品範圍寫成會亮紅燈的契約

2026 iThome 鐵人賽 WebMCP Day 14:安安~我是ChiYu~ 昨天終於讓 Inspector 自己選中 search_events,留下第一筆完整自然語言 trace。看到成功 畫面的那一刻,我腦中很自然地冒出下一串需求:登入、付款、後台、推薦活動,好像都可以 「順便」補進來。 這種順…

快速導覽 查看本文目錄

本文同步自 2026 iThome 鐵人賽原文 。完整系列可見 iThome 系列頁 。

安安~我是ChiYu~

昨天終於讓 Inspector 自己選中 search_events,留下第一筆完整自然語言 trace。看到成功 畫面的那一刻,我腦中很自然地冒出下一串需求:登入、付款、後台、推薦活動,好像都可以 「順便」補進來。

這種順便很有吸引力,也很會製造加班。

前幾天已經把網站上的候選操作刪到只剩五支 Tool。今天不再重新討論每一支為什麼存在, 而是處理更實際的問題:怎麼讓這個決定不只留在文章裡,而是變成後續開發不能偷偷跨過的 契約?

我的做法是先凍結產品範圍,再把每條 Journey 的最低條件寫成會亮紅燈的測試。明天開始 實作搜尋時,只要碰到收藏、報名或第六支 Tool,測試就應該先提醒我:「今天不是來做這個的。」

今天使用搜尋基線,但不宣稱五支 Tool 已經完成

今天切到 v3-day-14:

git switch --detach v3-day-14
npm ci
npm run dev

這個版本建立正式活動網站外殼、人類可用的搜尋流程,以及後續 Tool 會共用的產品基線。 它適合用來確認畫面、搜尋與功能邊界,卻不代表五支 Tool 已經全數實作,更不代表二十道 Agent 題目已經通過。

今天完成的是規格層的凍結:

產品只保留五個正式 Tool 名稱
→ 五支 Tool 串成三條 Journey
→ 不同 route 使用明確 input contract
→ 報名與取消停在人類確認以前
→ 後續測試只能在這個範圍內補齊能力

五個 Tool、三條 Journey 與人類停點

圖 1:搜尋與收藏可以完成;報名與取消只做到準備,正式 mutation 留在人類確認之後。

三條 Journey 先決定 Agent 最遠能走到哪裡

活動網站最後只處理三條流程:

JourneyAgent 可以做到哪裡人類保留的決定
J1 搜尋 → 詳情 → 收藏完成唯讀查詢與可復原收藏收藏後仍可從 UI Undo
J2 準備報名 → 正式送出把活動、姓名與 Email 放進可見表單人類檢查後送出報名
J3 準備取消 → 正式取消顯示取消對象與後果人類閱讀後確認取消

第一條包含唯讀與低風險寫入,Agent 可以完成。後兩條會改變名額或既有權益,所以 Tool 名稱直接使用 prepare,而不是 submit_registration 或 cancel_registration。

這個停點不能只靠一段溫馨提醒。Tool catalog 裡不提供 finalizer,server 端也要驗證 session、CSRF、ownership、目前狀態與 confirmation intent。Agent 願意停下來是一層; 系統本身不讓它越過去,才是另一層。

產品固定五個名稱,active catalog 仍由 route 決定

正式名稱只有這五個:

search_events
get_event_details
save_event
prepare_event_registration
prepare_registration_cancellation

這是整個產品允許出現的能力集合,不是要求每個頁面同時排出五支 Tool。最後採用的 route 契約如下:

Route/狀態Active Tool這一頁的 input 與狀態前提
/eventssearch_events、get_event_details、save_event搜尋回傳 opaque ID;詳情帶入該 event_id;收藏只能使用目前已顯示的同一活動
/events/:eventIdget_event_details、save_event詳情 Tool 已綁定 route,只接受 {};收藏沿用目前活動 ID
/events/:eventId/registerprepare_event_registrationroute 決定活動;Agent 只準備姓名與 Email
/registrations 有有效報名時prepare_registration_cancellation唯一一筆可用 {};多筆時使用頁面提供的 registration_id
其他頁面或無可操作狀態無不把與目前頁面無關的能力硬塞進 catalog

同名的 get_event_details 在列表頁與詳情頁使用不同 schema。列表頁不知道要看哪一場,所以 要求 event_id;詳情頁已由 route 綁定活動,因此只接受空物件。

我沒有把它設計成一個「有時候填、有時候不填」的選填欄位。這樣可以少掉兩種模糊狀態: 模型在該省略時傳入 null,或在 route-bound 情境偷偷換成另一個活動 ID。

/events 上的三支 Tool 會一起存在,但一起出現不等於可以跳步。搜尋先提供合法 ID,詳情 再把同一場活動顯示到頁面,收藏最後才接受目前目標一致的 ID。順序由 schema 與 state guard 保護,不靠 Agent 自己記住「照理說應該先做什麼」。

把文章裡的決定寫成會失敗的驗收條件

正式功能還沒補齊以前,我先寫下六個最低條件:

搜尋:UI 與 Tool 使用相同條件時,必須回傳同一批活動
詳情:列表頁只能使用 search_events 回傳的 ID;詳情頁以 {} 讀取目前 route
生命週期:離開詳情 route 後,舊的 route-bound Tool instance 必須失效
收藏:只能收藏目前已顯示的同一活動;重複呼叫仍只保留一筆
報名:prepare 階段的 POST /api/registrations 必須維持 0
取消:prepare 階段的取消 POST 必須維持 0;對象不唯一時必須要求選擇

有些測試在今天會亮紅燈,這是刻意的。紅燈不是失敗紀錄,而是一條施工線:明天只處理 搜尋,就不能因為收藏寫起來很順,順便把報名也塞進同一個 commit。

我也用 APPROVED_TOOL_NAMES 凍結五個正式名稱,讓 client catalog 與 shared contract 必須 一致,並明確拒絕 submit_registration、cancel_registration 之類的第六支 finalizer。 文章裡一句「這次不再加」很容易反悔,測試看門比較可靠。

二十道題不等最後一天才一次開獎

前一週整理的二十道題,接下來跟著功能分批執行:

開發階段同步檢查的行為
搜尋與詳情Tool 選擇、參數保留、opaque ID、route context 與舊 instance
收藏與多步驟同一 ID、操作順序、畫面更新與重複安全
報名與取消資訊不足先追問、ownership、確認前零 mutation 與 session expiry
安全與復原不該呼叫時停下、不可信內容、暫時失敗與可採取的下一步

可控制的程式行為交給 deterministic tests;真正由模型選 Tool、組參數與串接下一步的部分, 則保存 Inspector trace。

兩種證據都重要,但不能互相代打。測試程式知道該呼叫誰,不代表模型面對自然語言也知道; 一次 Agent 成功,同樣不能替其餘十九題簽到。

OAuth、付款與後台先留在範圍外

真實活動平台當然可能需要登入、付款、資料庫與管理後台。但它們會帶來新的帳號模型、金流 責任與權限邊界,卻不會直接回答這個系列正在追的問題:

網站 Tool 能不能被正確選擇、取得目前頁面 context,並在副作用發生以前停下來?

所以這次不加 OAuth、不做付款、不蓋後台,也沒有第六支 Tool。把範圍切小,才能讓每個 成功與失敗都回到同一條 WebMCP 主線,不會被一大桌 CRUD 蓋過去。

今天完成的是可檢查的範圍,不是五支 Tool 全數通關

昨天的 trace 只證明一次唯讀搜尋;今天則把五個正式名稱、三條 Journey、route profile、 人類停點與驗收條件固定下來。

因此今天能主張的是 E1:產品契約已存在,而且可以被檢查。v3-day-14 還不是最終實作, 後續版本也必須用自己的測試與 trace 補上證據,不能把昨天單一搜尋的 E4 直接加到整份 catalog。

範圍終於釘好後,明天先沿著最安全的一段往前走:讓人類表單與 search_events 共用同一套 搜尋規則,再拿不同說法檢查 Agent 有沒有保留所有條件。第一支 Tool 如果還會給出兩套答案, 第五支只會讓問題更熱鬧。

讀完了!你覺得這篇如何?

你的回饋會決定下一篇要寫什麼。

還有疑問

Learning path

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

這篇是系列中的第 14 / 30 篇。可以順著列表補完整脈絡,也可以直接跳到需要的段落。

  1. 01 Day 01|網站真的會被 Agent 使用嗎?我先丟了一句自然語言
  2. 02 Day 02|換到全新資料夾後,網站還能不能跑?
  3. 03 Day 03|先別急著做 Tool,我把活動網站自己走了一遍
  4. 04 Day 04|按鈕只多包一層,哪種 Playwright Locator 撐得住?
  5. 05 Day 05|只改按鈕四個字,Playwright 為什麼停在 click 以前?
  6. 06 Day 06|Playwright、REST API、MCP 與 WebMCP,各自負責哪一段?
  7. 07 Day 07|Agent 偶爾答對還不夠,我先出二十道測試題
  8. 08 Day 08|WebMCP Tool 該怎麼設計?把「點按鈕」改成「搜尋活動」
  9. 09 Day 09|網站該公開哪些 WebMCP Tool?從操作盤點到五支正式能力
  10. 10 Day 10|Inspector 裝好了,為什麼 Tool 面板還是一片空白?
  11. 11 Day 11|什麼是 Declarative API?讓 HTML 表單成為 WebMCP Tool
  12. 12 Day 12|Tool 註冊完還不算完成:離開 route 後誰把它收回來?
  13. 13 Day 13|第一次按下 Inspector 的 Send,先被 Gemini 429 擋在門外
  14. 14 Day 14|五支 Tool 不再往外長:把產品範圍寫成會亮紅燈的契約 目前閱讀
  15. 15 Day 15|讓人類與 Agent 共用同一套活動搜尋規則
  16. 16 Day 16|Agent 怎麼知道「我現在看的活動」是哪一場?
  17. 17 Day 17|Tool 回太少做不完,回太多又把內部資料倒給 Agent
  18. 18 Day 18|活動服務暫時失敗時,Agent 應該重試還是停下來?
  19. 19 Day 19|同一場活動收藏兩次,網站為什麼只能留下同一筆?
  20. 20 Day 20|表單填好了,為什麼這次報名仍然不能算通過?
  21. 21 Day 21|一句「這個我不要了」,Agent 到底敢不敢直接取消?
  22. 22 Day 22|五支 Tool 如何共用同一套規則,串成三條完整流程?
  23. 23 Day 23|在問 Agent 聰不聰明前,先證明網站沒有壞
  24. 24 Day 24|活動摘要叫 Agent 洩漏祕密,它會把這句話當資料還是命令?
  25. 25 Day 25|localhost 成功不算交付:怎麼證明公開網址跑的是同一份程式?
  26. 26 Day 26|把自然語言送進公開站,Agent 真的會自己選對 Tool 嗎?
  27. 27 Day 27|寫入型 Tool 怎麼驗收?把 Trace、Network、UI 與 Server 狀態放在一起看
  28. 28 Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層?
  29. 29 Day 29|把網站交給 Agent 前,我最後會檢查哪十件事?
  30. 30 Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼?

Further reading

Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼? 封面圖 2026-09-30 Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼? Day 29|把網站交給 Agent 前,我最後會檢查哪十件事? 封面圖 2026-09-29 Day 29|把網站交給 Agent 前,我最後會檢查哪十件事? Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層? 封面圖 2026-09-28 Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層?

留言討論

用 GitHub 帳號留言,留言會同步到這個 Blog 的 GitHub Discussions。

閱讀導覽

約 6 分鐘

本文目錄

本篇所在系列 · 第 14 / 30 篇

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

  1. Day 01|網站真的會被 Agent 使用嗎?我先丟了一句自然語言
  2. Day 02|換到全新資料夾後,網站還能不能跑?
  3. Day 03|先別急著做 Tool,我把活動網站自己走了一遍
  4. Day 04|按鈕只多包一層,哪種 Playwright Locator 撐得住?
  5. Day 05|只改按鈕四個字,Playwright 為什麼停在 click 以前?
  6. Day 06|Playwright、REST API、MCP 與 WebMCP,各自負責哪一段?
  7. Day 07|Agent 偶爾答對還不夠,我先出二十道測試題
  8. Day 08|WebMCP Tool 該怎麼設計?把「點按鈕」改成「搜尋活動」
  9. Day 09|網站該公開哪些 WebMCP Tool?從操作盤點到五支正式能力
  10. Day 10|Inspector 裝好了,為什麼 Tool 面板還是一片空白?
  11. Day 11|什麼是 Declarative API?讓 HTML 表單成為 WebMCP Tool
  12. Day 12|Tool 註冊完還不算完成:離開 route 後誰把它收回來?
  13. Day 13|第一次按下 Inspector 的 Send,先被 Gemini 429 擋在門外
  14. Day 14|五支 Tool 不再往外長:把產品範圍寫成會亮紅燈的契約
  15. Day 15|讓人類與 Agent 共用同一套活動搜尋規則
  16. Day 16|Agent 怎麼知道「我現在看的活動」是哪一場?
  17. Day 17|Tool 回太少做不完,回太多又把內部資料倒給 Agent
  18. Day 18|活動服務暫時失敗時,Agent 應該重試還是停下來?
  19. Day 19|同一場活動收藏兩次,網站為什麼只能留下同一筆?
  20. Day 20|表單填好了,為什麼這次報名仍然不能算通過?
  21. Day 21|一句「這個我不要了」,Agent 到底敢不敢直接取消?
  22. Day 22|五支 Tool 如何共用同一套規則,串成三條完整流程?
  23. Day 23|在問 Agent 聰不聰明前,先證明網站沒有壞
  24. Day 24|活動摘要叫 Agent 洩漏祕密,它會把這句話當資料還是命令?
  25. Day 25|localhost 成功不算交付:怎麼證明公開網址跑的是同一份程式?
  26. Day 26|把自然語言送進公開站,Agent 真的會自己選對 Tool 嗎?
  27. Day 27|寫入型 Tool 怎麼驗收?把 Trace、Network、UI 與 Server 狀態放在一起看
  28. Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層?
  29. Day 29|把網站交給 Agent 前,我最後會檢查哪十件事?
  30. Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼?
看完整系列 30 篇