DEV Community

Cover image for 如何用 WebMCP 讓你的網站對 Agent 開放?
JH5
JH5

Posted on

如何用 WebMCP 讓你的網站對 Agent 開放?

想像一下,未來的 AI Agent 幫你在網頁上訂機票、買東西時,不再需要靠「螢幕截圖 + 猜按鈕」這種網站一改版就崩潰的方式,而是你的網站主動對 Agent 宣告:「我有這些 API,你可以直接呼叫」。

這就是 W3C 正熱烈討論中的 WebMCP(Web Model Context Protocol)。

如果說各路大神推出的 MCP 是讓 AI 操作你的「後端伺服器」,那麼 WebMCP 就是讓 AI 直接在「使用者瀏覽器」裡流暢操作 UI 的關鍵最後一哩路,這篇文章會用 Chrome 151 實測,帶你快速掌握宣告式(Declarative)與命令式(Imperative)API 的差異,並透過官方 3 個真實使用者案例,看看工具如何隨頁面動態長出!


WebMCP 是什麼?

WebMCP(Web Model Context Protocol)是 W3C 正在討論的網頁標準,核心概念是網站用 JavaScript 或 HTML 註解,向具備代理能力的瀏覽器註冊「工具」,而當 Agent 代理碰到註冊過的工具,就可以直接呼叫相關函式並拿到結構化結果,不用再靠螢幕截圖 + 猜按鈕。

MCP 是後端標準,讓代理連到你的伺服器拿資料;WebMCP 是前端標準,讓代理在你開著的這個分頁裡操作 UI

MCP 的生命週期是永久的(伺服器常駐),而WebMCP 是暫時的(關掉分頁就沒了),在你的服務上如果同時提供了MCP與WebMCP,便可以理解成 MCP 管核心邏輯,WebMCP 管最後一哩的使用者介面處理。

宣告式 API:兩個屬性,表單直接變成工具

宣告式 API 是 WebMCP 的最吸引人的部分,你不用寫一行 JavaScript,直接在 <form> 上加上 toolnametooldescription的注記就完成tool的註冊:

<form toolname="createSupportRequest"
      tooldescription="Submits a request for customer support.">
  <label for="name">姓名</label>
  <input type="text" id="name" name="name">
  <label for="topic">問題類型</label>
  <select name="topic" toolparamdescription="決定問題要路由給哪個團隊">
    <option value="billing">帳單問題</option>
    <option value="tech">技術問題</option>
  </select>
  <button type="submit">送出</button>
</form>
Enter fullscreen mode Exit fullscreen mode

實測後發現 document.modelContext.getTools() 會自動把這個表單轉成完整的 JSON Schema——<select> 的選項變成 anyOf + enumtoolparamdescription ,而 <label> 的中文文字變成欄位說明,也就是說當Agent 識別到這個 schema 後,就知道這個表單可以填什麼,以及每個欄位接受什麼值。

進階控制有三個屬性可以玩:

  • toolparamdescription:單一欄位的用途說明,沒寫就用 <label> 的文字。
  • toolautosubmit:加上後,代理呼叫工具會直接提交表單並導航,不用等人按送出。
  • SubmitEvent.agentInvoked:表單提交時這個布林值會告訴你「這次提交是代理發起的」。搭配 respondWith(Promise) 可以讓代理拿到非同步的執行結果。

另外還有 :tool-form-active:tool-submit-active 兩個 CSS pseudo-class,讓代理在填表單時可以用來顯示正在輸入的欄位,使用者也可以看得到「AI 正在幫我填」的UI顯示。

命令式 API

命令式 API 適合在不是表單處理的場景來做使用——像是查訂單狀態、跑診斷、做狀態管理,主要提供三個方法,全部掛在 document.modelContext 上:

// 註冊工具
await document.modelContext.registerTool({
  name: 'get_order_status',
  description: '查詢訂單狀態,回傳訂單編號、運送狀態與地點',
  inputSchema: {
    type: 'object',
    properties: {
      orderId: { type: 'string', description: '訂單編號' }
    },
    required: ['orderId']
  },
  execute: async ({ orderId }) => {
    return `訂單 ${orderId} 已出貨,狀態:運送中`;
  },
});

// 列出可用工具
const tools = await document.modelContext.getTools();

// 手動執行工具
const result = await document.modelContext.executeTool(tools[0], '{"orderId": "ORD-123"}');
Enter fullscreen mode Exit fullscreen mode

實測的三個方法全部正常:registerTool 成功註冊、getTools() 同時列出自訂工具和宣告式表單工具、executeTool 正確回傳執行結果,另外 toolchange 事件在工具清單變更時會觸發,AbortSignal 則可以取消工具執行或註冊。

不過有個說明有寫但是不是太清楚的部分是,Chrome 150 已把 navigator.modelContext 淘汰,改用 document.modelContext (W3C Web Incubator Community Group 討論中)。不過我的 Chrome 151 上兩者都還是 object,記得此時此刻如果要開工的話,就一律用 document.modelContext 就對了。

官方 demo 的旅程

官方 webmcp-tools repo 的 demos 資料夾有 15 個情境,我試了幾個比較完整 UX 的實測,這邊有一個跟Server MCP比較不一樣的,WebMCP 工具不是一次全給的,而是會跟著頁面狀態長出來

WebMCP Sports

WebMCP Sports 是 Angular 寫的體育用品電商,首頁有 5 個全站工具:

  • search_product
  • view_product
  • get_product_info
  • open_cart
  • get_store_promos_and_rules

WebMCP Sports 首頁:全站工具已註冊,getTools() 回傳 5 個

在實測中 search_product {"query":"basketball","category":"BASKETBALL"},得到回傳 count:5 並切到搜尋結果頁,getTools() 結果直接變成 8 個,多出

  • refine_search
  • get_current_search_results
  • add_search_result_to_cart

搜尋結果頁:代理執行 search_product 後,工具從 5 個變成 8 個

接著再呼叫 open_cart 開購物車,工具一口氣長到 13 個XD
結帳相關的tool都飄出來了

  • start_checkout
  • confirm_order
  • update_cart_delivery_option
  • remove_from_cart
  • get_cart

購物車開啟:工具長到 13 個,start_checkout 與 confirm_order 在這一層才出現

突然有一種以前在練習寫購物車的架構與function標準答案 一一浮現的感覺。

三層 scope 的設計讓全站工具、搜尋頁工具、購物車工具分層註冊,Agent 當下能看到的最小工具set,永遠只跟目前 UI 狀態匹配,比起一般的 MCP 一次註冊 20 個工具,這種設計讓Agent不會亂呼叫不該叫的工具反而好很多,應該也比較省Token。

而工具自己也會驗證參數。我故意傳 {"price_range":"wrong"}refine_search,結果 WebMCP 回傳 {"success":false,"message":"Invalid price range 'undefined'. Must be one of: 'all', '0-49.99', '50-99.99', '100+'"}

另外它還內嵌一個 Gemini on-site AI assistant,站在網站自己那一側幫你聊天導購。「網站自帶代理」跟瀏覽器層級的代理是兩條路線,這個 demo 兩條都示範了。

L'Atelier 飯店訂房

另一個範例 L'Atelier 是 React 寫的豪華飯店訂房 app,東京、巴黎、紐約三家分店。首頁先是註冊了 三個工具。

  • search_location
  • view_hotel
  • lookup_amenity

我從 DevTools Protocol 直接呼叫 executeTool(search_location, {"query":"Tokyo"}),回傳值是這樣的:

{"content":[{"type":"text","text":"{\"success\":true,\"message\":\"Navigated to search results for Tokyo with active search filters applied.\"}"}]}
Enter fullscreen mode Exit fullscreen mode

這邊要注意的是回傳的結果包了一層 content 陣列,很多時候 WebMCP 的執行結果就是 MCP 的 content block 格式,而在我呼叫完 getTools(),工具清單又變成 6 個,多出了

  • filter_search_results
  • get_current_search_results
  • reset_filters

搜尋結果頁有自己的工具,代理跳到哪一頁,哪一頁的工具才上線。

除了基本的防呆之外,WebMCP 也提供狀態檢查,我手癢直接 view_hotel,回傳 {"success":false,"error":"Could not find a hotel matching \"1\". Please search first."}

而代理亂呼叫也是會被擋的,start_booking 在搜尋結果頁根本不存在,要到飯店詳情頁才註冊,而整條操作旅程的最後一步(complete_booking 填客人資料)是宣告式表單,命令式 API 開頭、宣告式API 收尾,跟在真實 app 裡的架構上滿match的。

一些小限制們

WebMCP 有三個明確的限制:

一定要有瀏覽器環境。 工具呼叫是在 JavaScript 裡處理的,所以代理必須真的開著一個可見的分頁或 WebView,headless 環境下無法呼叫,這跟 MCP server 隨時待命完全不同。

網站太複雜會很痛。 如果頁面狀態複雜,要利用 JavaScript 去處理應用程式和 UI 狀態的同步,宣告式 API 也幫不了你,Agent能不能利用工具來探索性也是問題,也之後就會有給 Agent看的 sitemap ?

權限政策和跨源 iframe。 工具預設被 tools Permissions Policy 擋住(Same-Origin Policy),跨源 iframe 要加 allow="tools" 且網域需在白名單內,跨源分享工具還要 exposedTo + fromOrigins 兩個參數都設定才生效,以防止潛在的 Clickjacking 與工具注入攻擊

開始的起手式

  1. 開 flagchrome://flags/#enable-webmcp-testing 設為 Enabled,重啟 Chrome。正式環境去加入 WebMCP origin trial。
  2. 選一個表單下手:找站上最常用的搜尋或表單,加上 toolnametooldescription,最多 5 分鐘。
  3. 用 Inspector 驗證:裝 Model Context Tool Inspector 擴充功能,看工具有沒有正確註冊、schema 對不對,再用自然語言 prompt 叫代理執行。

從「給人看」到「給 Agent 用」的 Web 前端新浪潮

WebMCP 的核心價值,不是讓網站多一個花哨的 AI 聊天視窗,而是讓你的網站直接升級為 Agent Ecosystem(代理生態系)的成員。

Top comments (0)