瀏覽器原生交互的關(guān)鍵協(xié)議)
最近在調(diào)試一個自動處理后臺工單的 Agent我發(fā)現(xiàn)自己真正花時間的不是提示詞也不是模型能力而是“讓 Agent 和網(wǎng)頁真正打交道”這一層。很多 Agent 看起來智能一旦要登錄系統(tǒng)、點按鈕、翻表格立刻變得笨拙——要么給網(wǎng)站單獨寫接口要么靠截圖做視覺猜測要么用一套脆弱的 CSS 選擇器硬撐。直到我把瀏覽器這一層按 WebMCP 的思路重新整理了一遍才體會到什么叫“瀏覽器原生”的交互方式。這篇文章不是某個官方文檔的翻譯而是我在實際項目里圍繞 WebMCP、AI Agent、瀏覽器原生交互做的一次系統(tǒng)踩點和總結(jié)。適合正在做 Agent 產(chǎn)品、內(nèi)網(wǎng)辦公自動化、網(wǎng)頁數(shù)據(jù)運營的人讀讀完能少走很多彎路。1. Agent 卡在“瀏覽器之外”WebMCP 要填的坑到底是什么1.1 現(xiàn)在主流的網(wǎng)頁交互方案各有什么死穴先說清楚 WebMCP 為什么值得聊。過去一年我見過太多 Agent 項目架構(gòu)圖上畫得很漂亮大模型做規(guī)劃、向量庫做記憶、工具層接了一堆 API看起來無所不能。但只要落到真實業(yè)務(wù)第一個卡住的地方永遠(yuǎn)是網(wǎng)頁操作。目前主流做法基本是四條路線各自的痛點都很明顯。第一條路線是 API 優(yōu)先。讓網(wǎng)站方提供官方接口Agent 直接調(diào)用。這條路線最干凈但現(xiàn)實很骨感內(nèi)部老系統(tǒng)沒有 API第三方后臺不開放 API就算有 API字段含義和權(quán)限模型也未必跟頁面操作對得上。我接過一個項目客戶堅持說“我們有 API”結(jié)果一查API 只能查數(shù)據(jù)不能提交表單最終還是要回到頁面操作。第二條路線是爬蟲加選擇器解析。寫一堆 XPath、CSS 選擇器去抓取頁面數(shù)據(jù)。缺點是極其脆弱頁面結(jié)構(gòu)只要微調(diào)選擇器就崩而且很多站點有反爬策略維護(hù)成本不亞于寫一個獨立應(yīng)用。關(guān)鍵在于爬蟲抓到的是靜態(tài) HTML對頁面交互點擊、拖拽、彈窗幾乎無能為力。第三條路線是視覺方案。截屏把圖片丟給多模態(tài)大模型讓模型看圖識別按鈕并給出坐標(biāo)。優(yōu)點是適應(yīng)性強缺點也同樣明顯截圖有延遲、Token 成本高、坐標(biāo)精度不穩(wěn)定遇到動態(tài)加載的內(nèi)容經(jīng)常翻車。實測下來視覺方案適合做兜底和校驗不適合做主力操作路徑。第四條路線是瀏覽器自動化腳本比如 Selenium、Playwright 這一派。腳本本身很成熟穩(wěn)定性也不錯。但問題在于這些工具強調(diào)的是“腳本執(zhí)行命令”不是“讓 Agent 理解頁面”。你需要為每一次操作編寫?yīng)毩⑦壿嫛却硞€元素、判斷是否存在、處理彈窗——等于每個站點都要定制開發(fā)一套驅(qū)動程序。代碼量巨大且不可通用。你把這四條路線擺在一起看會發(fā)現(xiàn)一個共同死穴沒有任何一個標(biāo)準(zhǔn)協(xié)議讓瀏覽器主動向 Agent 聲明“我能干什么、現(xiàn)在是什么狀態(tài)”。所有方案都是 Agent 在外部猜而不是瀏覽器在內(nèi)部提供能力。WebMCP 想解決的正是這個問題。1.2 “瀏覽器原生”到底意味著什么WebMCP可以拆成 Web 加 MCP。MCP 是 Model Context Protocol 的縮寫核心思路是把“模型需要的外部能力”標(biāo)準(zhǔn)化成一套協(xié)議。好比電腦上的 USB-C 接口不管接的是顯示器、硬盤還是充電器接口形態(tài)統(tǒng)一了設(shè)備之間就能即插即用。WebMCP 就是把這套協(xié)議搬進(jìn)瀏覽器環(huán)境。更直白一點瀏覽器不再只是一個被操作的對象而成為 MCP 的宿主運行時。網(wǎng)頁本身可以作為一個“能力端點”存在主動暴露自己的表單字段、按鈕、列表狀態(tài)、加載狀態(tài)Agent 通過標(biāo)準(zhǔn)化的協(xié)議去發(fā)現(xiàn)這些能力并調(diào)用它們。我用一個生活化的類比。傳統(tǒng)做法是你想讓一個人幫你操作一臺老舊儀器你得先寫一份極其詳細(xì)的操作手冊告訴他哪個旋鈕在哪兒、怎么擰、擰到什么位置。WebMCP 的做法是這臺儀器自己貼了一張標(biāo)簽上面寫著“我有三個旋鈕、兩個按鈕、一個顯示屏支持以下操作”操作員看一眼標(biāo)簽就能開工而且換一臺儀器只要標(biāo)簽寫得規(guī)范操作員幾乎不用重新學(xué)習(xí)。所以所謂“瀏覽器原生”交互核心就三點能力可發(fā)現(xiàn)、操作可標(biāo)準(zhǔn)化、狀態(tài)可感知。頁面自己把底牌亮出來Agent 按統(tǒng)一規(guī)則來打這就是與之前所有方案的分水嶺。1.3 誰最需要它結(jié)合我接觸到的需求方下面這幾類人最應(yīng)該關(guān)注 WebMCP。做內(nèi)部辦公自動化 Agent 的團(tuán)隊。ERP、CRM、工單系統(tǒng)這類老后臺普遍沒有開放接口但業(yè)務(wù)流程又必須走頁面。WebMCP 能把這些頁面變成可以被 Agent 調(diào)度的“虛擬 API”自動化程度直接上一個臺階。做垂直場景助手的開發(fā)者。比如讓 Agent 幫用戶查物流、比價、填表單、管理后臺內(nèi)容。有了標(biāo)準(zhǔn)化協(xié)議你不用為每個網(wǎng)站單獨寫適配器開發(fā)效率提升是數(shù)量級的。做瀏覽器插件產(chǎn)品的團(tuán)隊。很多插件本質(zhì)上就是在替用戶操作網(wǎng)頁之前只能靠內(nèi)容腳本硬編碼邏輯現(xiàn)在可以讓插件成為一個 MCP 網(wǎng)關(guān)把頁面能力開放給上層 Agent。另外還有一類做 RPA 改造的公司。傳統(tǒng) RPA 靠錄屏和選擇器綁定遇到頁面改版就廢。WebMCP 這種語義化、意圖化的思路恰好補上了 RPA 在“頁面理解”上的短板。2. 從 MCP 到 WebMCP三個關(guān)鍵設(shè)計讓瀏覽器變成 Agent 的“原生環(huán)境”2.1 先回顧 MCP 的三件套約定在深入 WebMCP 之前得先把 MCP 的基礎(chǔ)約定說清楚否則后面全是空中樓閣。MCP 的設(shè)計很簡潔它定義了三種核心原語。第一是 Tools代表動作能力。比如“查詢天氣”“創(chuàng)建訂單”“提交表單”每個 Tool 有自己的輸入?yún)?shù)和輸出格式。Agent 通過調(diào)用 Tool 完成具體操作類似函數(shù)調(diào)用。第二是 Resources代表數(shù)據(jù)資源。比如“用戶信息”“商品詳情”“頁面上的表單字段列表”。Resources 是只讀的Agent 先從 Resources 里獲取上下文再決定調(diào)用哪些 Tools。第三是 Prompts代表可復(fù)用的提示模板。用于把常見任務(wù)固化成模板減少 Agent 的規(guī)劃負(fù)擔(dān)。MCP 的設(shè)計哲學(xué)很像給 AI 界的各種模型和工具之間做了一套通用的“插線板”。你不需要針對每個模型寫一套工具適配代碼只要工具側(cè)實現(xiàn)了 MCP 協(xié)議任何兼容的模型都能直接調(diào)用。WebMCP 沿用了這套三件套但把實現(xiàn)環(huán)境放在了瀏覽器里。這帶來一個關(guān)鍵轉(zhuǎn)變傳統(tǒng)的 MCP 工具通常是外掛在應(yīng)用之外的比如一個 Python 服務(wù)去調(diào)數(shù)據(jù)庫而 WebMCP 的工具和資源是網(wǎng)頁元素本身。按鈕、輸入框、下拉菜單、列表、分頁器在 WebMCP 的視角下都是可以被聲明和暴露的“原生資源”。2.2 關(guān)鍵設(shè)計一頁面即端點Page as Endpoint這是我認(rèn)為 WebMCP 最核心的設(shè)計理念。傳統(tǒng)的瀏覽器自動化里頁面只是一個被遙控的目標(biāo)腳本在外面操作它。而 WebMCP 把每個頁面看作一個 MCP 端點Endpoint頁面自己負(fù)責(zé)向 Agent 描述自身的能力。具體怎么做以 Chromium 系瀏覽器為例通過擴展或者注入腳本在頁面加載完成后做一次“能力盤點”。盤點內(nèi)容包括表單字段有哪些、可見按鈕有哪些、當(dāng)前處于什么加載狀態(tài)、有無彈窗或錯誤提示。然后生成一份結(jié)構(gòu)化的描述文檔相當(dāng)于頁面的“能力名片”。這份描述文檔不是隨便生成的它要遵循統(tǒng)一的 schema。比如一個表單字段會記錄它的語義 ID、類型文本、日期、下拉、當(dāng)前值、是否必填、關(guān)聯(lián)的校驗規(guī)則。Agent 拿到這份文檔不需要自己猜測頁面上有什么直接按圖索驥即可。我把這個設(shè)計叫“頁面即端點”是因為它徹底改變了能力的歸屬關(guān)系。之前是“Agent 想辦法去夠頁面”現(xiàn)在是“頁面主動把手伸給 Agent”。別小看這個反轉(zhuǎn)它帶來的直接好處是通用性。只要每個頁面都實現(xiàn)了這套端點化描述Agent 面對任何一個站點都不再是“盲人摸象”而是先看名片再辦事。2.3 關(guān)鍵設(shè)計二從“坐標(biāo)點擊”到“意圖操作”傳統(tǒng)瀏覽器自動化最常見的毀譽參半的點就是依賴坐標(biāo)或選擇器。比如“點擊頁面坐標(biāo) (x, y)”或“點擊 #submit-btn”。坐標(biāo)的問題很明顯頁面窗口大小一變、布局一變坐標(biāo)就廢了。選擇器的問題也很明顯前端重構(gòu)一下 class 名腳本直接崩潰。WebMCP 的思路是切換到“意圖操作”。Agent 告訴瀏覽器“我要提交這個表單”“我要把發(fā)貨狀態(tài)切換到已完成”而不是“我要點坐標(biāo)為 (320, 480) 的地方”。瀏覽器側(cè)收到意圖后自己去找對應(yīng)的元素、判斷是否可交互、執(zhí)行操作并返回結(jié)果。這就好比你和人打交道你不會說“請把你的右手食指抬高 5 厘米指向那個紅色按鈕”你會說“請按一下左邊的紅色按鈕”。意圖操作的價值在于它把“如何定位元素”這件事留給了瀏覽器側(cè)而 Agent 只關(guān)心“我想達(dá)成什么效果”。頁面前端再怎么改版只要功能語義不變Agent 的任務(wù)定義就不用變。在我自己的測試?yán)镞@個設(shè)計的實際收益非常明顯。之前用 Playwright 寫一套后臺自動化頁面改版后往往要改幾個小時的選擇器。改用意圖化描述后大多數(shù)情況下只需要重新生成一次能力文檔Agent 側(cè)的任務(wù)定義幾乎不動。2.4 關(guān)鍵設(shè)計三會話綁定與上下文黏性還有一個容易被忽略、但實際使用中極其重要的設(shè)計會話綁定。Agent 操作網(wǎng)頁天然涉及多輪交互。你要先登錄然后跳到某個菜單再填寫查詢條件最后點擊搜索并讀取結(jié)果。這個過程里狀態(tài)是連續(xù)的。如果每次操作都當(dāng)作獨立的無狀態(tài)請求就完了。WebMCP 借用瀏覽器的天然能力來解決這個問題。Cookie、IndexedDB、LocalStorage 這些瀏覽器本地狀態(tài)天然就是會話的載體。WebMCP 會為每個任務(wù)創(chuàng)建綁定到特定標(biāo)簽頁的會話上下文Agent 的操作始終在這個上下文里執(zhí)行。更近一步跨標(biāo)簽頁的時候也能保持住“會話黏性”。比如任務(wù)需要同時參考兩個頁面的數(shù)據(jù)WebMCP 可以給兩個標(biāo)簽頁的會話掛上同一個任務(wù) ID。這樣即使 Agent 在頁面 A 登錄了切換到頁面 B 時登錄態(tài)依然能通過共享的瀏覽器上下文被感知不需要重復(fù)認(rèn)證。這個設(shè)計也帶來了一個嚴(yán)肅的問題會話安全。一個 Agent 可能同時處理多個任務(wù)如果會話上下文串了任務(wù) A 的操作跑到任務(wù) B 的頁面里后果不堪設(shè)想。所以 WebMCP 的會話綁定通常會做嚴(yán)格隔離每個任務(wù)一個獨立會話空間并且配合用戶授權(quán)機制。3. WebMCP 落地的模塊拆分?jǐn)U展層、橋接層與 DOM 能力層怎么協(xié)同3.1 建議的四層架構(gòu)參考聊完理念來點實際的。如果你要在自己項目里落地 WebMCP建議按下面這個四層架構(gòu)來拆。這個架構(gòu)不是嚴(yán)格標(biāo)準(zhǔn)而是我結(jié)合幾個實踐項目總結(jié)的相對省力的組織方式。第一層瀏覽器擴展層。它負(fù)責(zé)三件事識別需要注入的頁面、管理用戶授權(quán)、維護(hù) WebMCP 端點生命周期。沒有這一層后面的能力層根本沒有入口。擴展層要在頁面加載前就準(zhǔn)備好運行時并且要處理用戶對哪些站點授權(quán)、哪些站點拒絕的決策邏輯。第二層協(xié)議橋接層。這是最容易被低估的一層。它負(fù)責(zé)把瀏覽器側(cè)發(fā)生的“原生事件”比如表單值變化、點擊、頁面跳轉(zhuǎn)、請求失敗翻譯成 MCP 協(xié)議格式的消息同時把 Agent 發(fā)來的 MCP 請求翻譯回瀏覽器側(cè)的 DOM 操作。相當(dāng)于一個翻譯官。第三層DOM 能力層。這一層負(fù)責(zé)生成“能力文檔”并執(zhí)行指令。具體來說就是遍歷 DOM 樹抽取可交互元素和相關(guān)狀態(tài)生成結(jié)構(gòu)化的資源描述同時實現(xiàn)“意圖操作”的執(zhí)行——Agent 說要提交表單這層去定位、校驗、點擊、等待回執(zhí)。第四層調(diào)度與會話層。它管多任務(wù)并發(fā)、標(biāo)簽頁歸屬、會話隔離、超時重試。如果一個 Agent 同時處理兩個任務(wù)分別操作兩個標(biāo)簽頁調(diào)度層必須保證兩邊互不干擾。四層之間可以理解為這樣的協(xié)作關(guān)系會話層接到任務(wù)詢問擴展層哪個頁面被授權(quán)了然后通過橋接層與 DOM 能力層溝通DOM 能力層再返回操作結(jié)果橋接層把結(jié)果格式化最終會話層把結(jié)果交給 Agent 決策。層級主要職責(zé)典型實現(xiàn)要點瀏覽器擴展層注入、授權(quán)、生命周期manifest 配置、站點白名單、運行時清理協(xié)議橋接層事件雙向翻譯、協(xié)議格式化MCP 消息編解碼、錯誤映射、重試邏輯DOM 能力層能力盤點、意圖執(zhí)行語義 ID 抽取、元素定位、狀態(tài)檢測調(diào)度與會話層并發(fā)控制、會話隔離任務(wù) ID 綁定、標(biāo)簽頁映射、超時熔斷3.2 一次完整任務(wù)交互的時序展開紙上談兵沒有用我把一次真實任務(wù)的完整時序拆出來。假設(shè)用戶對 Agent 說“把后臺里上個月的異常訂單導(dǎo)出來按金額倒序生成一個表格”。第一步Agent 的任務(wù)規(guī)劃層拆分出多個子任務(wù)打開后臺、登錄如果未登錄、進(jìn)入訂單列表、篩選日期、導(dǎo)出數(shù)據(jù)。第二步調(diào)度層檢查現(xiàn)有的會話發(fā)現(xiàn)沒有對應(yīng)后臺的授權(quán)于是提示用戶授權(quán)。用戶同意后擴展層在目標(biāo)站點頁面注入 WebMCP 運行時。第三步擴展層通知 DOM 能力層做一次能力盤點生成該頁面的能力文檔。文檔里記錄了當(dāng)前頁面有登錄表單、登錄按鈕、輸入框等。第四步Agent 下發(fā)意圖操作“填充登錄表單并提交”。橋接層翻譯請求DOM 能力層執(zhí)行返回登錄成功或失敗的狀態(tài)。第五步登錄成功后DOM 能力層重新盤點頁面能力因為登錄后的頁面和登錄前的頁面完全是兩個狀態(tài)。Agent 根據(jù)新的能力文檔繼續(xù)規(guī)劃點擊“訂單管理”、輸入篩選條件、點擊導(dǎo)出。第六步所有操作完成后結(jié)果數(shù)據(jù)回傳會話層把任務(wù)標(biāo)記為完成并清理不再需要的臨時狀態(tài)。這個時序里最值得注意的一點是每次頁面狀態(tài)變化后都必須重新做能力盤點。很多初次實現(xiàn) WebMCP 的人習(xí)慣在頁面加載時只盤點一次后面就一直用舊文檔。結(jié)果就是 Agent 以為頁面上還有登錄按鈕實際上已經(jīng)在訂單列表頁了直接原地出錯。我反復(fù)強調(diào)能力文檔是快照不是永久檔案頁面一變快照就必須刷新。3.3 狀態(tài)持久化的細(xì)節(jié)處理狀態(tài)持久化是個不顯眼但繞不開的問題。Agent 在頁面上操作產(chǎn)生的狀態(tài)散落在各處登錄態(tài)在 Cookie 里、篩選條件在頁面內(nèi)存里、表格排序在 UI 狀態(tài)里、臨時數(shù)據(jù)在 JS 變量里。WebMCP 的實踐經(jīng)驗是按層級分層持久化。第一層是瀏覽器原生狀態(tài)比如 Cookie 和 IndexedDB托管給瀏覽器本身Agent 不用管。第二層是頁面交互產(chǎn)生的臨時狀態(tài)比如一個沒提交的表單內(nèi)容DOM 能力層需要維護(hù)一份內(nèi)存映射并且在頁面刷新后能恢復(fù)能恢復(fù)則恢復(fù)不能恢復(fù)就明確告訴 Agent 需要重新操作。第三層是 Agent 側(cè)的會話記錄也就是每一輪操作的歷史、操作前后的頁面快照這部分要持久化到后端形成審計鏈路。我在項目里踩過一個大坑Agent 填了一半表單網(wǎng)絡(luò)閃斷頁面刷新填的內(nèi)容全沒了。之前沒有“恢復(fù)草稿”機制Agent 完全意識不到自己丟了多少狀態(tài)直接進(jìn)入下一步操作結(jié)果把空表單提交了。后來我在 DOM 能力層加了一個“表單草稿快照”每填一個字段就同步一份到會話存儲。恢復(fù)后Agent 先比對快照發(fā)現(xiàn)缺失字段就重新填才算解決了這個問題。4. 跑通一個最小 WebMCP 項目讓 Agent 真正操作一個網(wǎng)頁表單4.1 建立環(huán)境和目標(biāo)理論說太多容易飄還是動手跑個最小項目。這里的目標(biāo)是用本地服務(wù)起兩個頁面一個表單頁一個結(jié)果頁讓 Agent 通過 WebMCP 的簡化實現(xiàn)來自動填寫表單并提交驗證核心鏈路能通。這個項目不需要完整實現(xiàn)標(biāo)準(zhǔn) MCP 協(xié)議重點是跑通“能力盤點 意圖操作 會話綁定”的主鏈路我已經(jīng)用這種方式在一個內(nèi)部項目里驗證過整個思路的可行性。環(huán)境準(zhǔn)備如下。一臺裝有 Chromium 系瀏覽器的電腦Chrome 或 Edge 均可打開開發(fā)者模式準(zhǔn)備一個臨時擴展。本地起一個簡單的靜態(tài)服務(wù)比如 npx serve 或者任意靜態(tài)服務(wù)器。表單頁用 demo-form.html結(jié)果頁用 demo-result.html。Agent 側(cè)的調(diào)用用簡單的 Node 腳本加 fetch 請求模擬不走完整實現(xiàn)但保留協(xié)議結(jié)構(gòu)。你不需要任何云服務(wù)全部本地跑通。整個過程大概半小時能完成。4.2 擴展側(cè)的核心代碼第一步是創(chuàng)建一個最小 Chrome 擴展。目錄結(jié)構(gòu)簡單點manifest.json 加 content.js 就夠。manifest 需要聲明 content script 匹配本地頁面并允許訪問頁面 DOM。{ manifest_version: 3, name: Local MCP Bridge, version: 0.1.0, content_scripts: [ { matches: [http://127.0.0.1/*], js: [content.js], run_at: document_idle } ], permissions: [storage] }content.js 里做一個極簡的 WebMCP 運行時。頁面加載完成后盤點當(dāng)前表單字段生成能力文檔并暴露一個本地接口供 Agent 側(cè)調(diào)用。// content.js —— 簡化版把當(dāng)前頁面當(dāng)作 MCP 端點 (function registerEndpoint() { // 1. 能力盤點掃描表單字段和按鈕 function scanCapabilities() { const fields Array.from(document.querySelectorAll(input, select, textarea)).map((el) { return { id: el.id || el.name || field- Math.random().toString(36).slice(2, 8), type: el.type || el.tagName.toLowerCase(), currentValue: el.value || , required: el.hasAttribute(required), label: document.querySelector(label[for${el.id}])?.textContent?.trim() || }; }); const buttons Array.from(document.querySelectorAll(button, input[typesubmit])).map((el) { return { id: el.id || el.name || btn- Math.random().toString(36).slice(2, 8), text: el.textContent?.trim() || el.value || }; }); return { fields, buttons, url: location.href, title: document.title }; } // 2. 意圖操作按字段名填值 function fillField(fieldId, value) { const el document.getElementById(fieldId); if (!el) return { ok: false, error: field not found }; el.value value; el.dispatchEvent(new Event(input, { bubbles: true })); return { ok: true, fieldId, value }; } function submitForm() { const form document.querySelector(form); if (!form) return { ok: false, error: form not found }; form.dispatchEvent(new Event(submit, { bubbles: true, cancelable: true })); return { ok: true }; } // 3. 暴露給外部通過 window 上掛一個全局對象方便 Agent 側(cè)調(diào)用 window.__localMCPBridge__ { scanCapabilities, fillField, submitForm, sessionId: demo-session-local }; console.log([WebMCP] endpoint registered for:, location.href); })();這個示例里的 dispatchEvent 很重要很多前端框架比如 React監(jiān)聽的是合成事件直接賦值 el.value 不會觸發(fā)框架的 onChange必須補一個 input 事件否則表單數(shù)據(jù)不會被框架捕獲。4.3 Agent 側(cè)調(diào)用鏈路擴展準(zhǔn)備好后表單頁加載時會自動注入運行時?,F(xiàn)在寫 Agent 側(cè)的調(diào)用腳本。這里用 Node 的 fetch 來模擬 Agent 發(fā)送 MCP 風(fēng)格請求。// agent-simulator.js —— 用 fetch 模擬 Agent 的 MCP 調(diào)用 const endpoint http://127.0.0.1:9222/mcp; // 實際生產(chǎn)環(huán)境會通過調(diào)試協(xié)議或擴展橋接 const headers { Content-Type: application/json }; async function scan(targetUrl) { const res await fetch(${endpoint}/resource, { method: POST, headers, body: JSON.stringify({ target: targetUrl, resource: dom://capabilities, sessionId: demo-session-local }) }); return res.json(); } async function runTask() { // 1. 盤點頁面能力 const caps await scan(http://127.0.0.1:3000/demo-form.html); console.log(頁面能力清單:, caps); // 2. 填充字段 const fillRes await fetch(${endpoint}/tool, { method: POST, headers, body: JSON.stringify({ tool: form.fill, input: { fieldId: name, value: 測試訂單-202401 }, sessionId: demo-session-local }) }); console.log(填充結(jié)果:, await fillRes.json()); // 3. 提交表單 const submitRes await fetch(${endpoint}/tool, { method: POST, headers, body: JSON.stringify({ tool: form.submit, input: {}, sessionId: demo-session-local }) }); console.log(提交結(jié)果:, await submitRes.json()); } runTask().catch(console.error);這個腳本里的 URL 和接口路徑是我為了演示定義的真正生產(chǎn)環(huán)境會統(tǒng)一走遠(yuǎn)程 MCP 端點或者通過擴展的消息通道。但結(jié)構(gòu)不需要變先盤點能力再調(diào)用工具再拿結(jié)果。如果你把目標(biāo) URL 換成任意一個授權(quán)過的頁面只要頁面里實現(xiàn)了 WebMCP 運行時鏈路就是一樣的。4.4 驗證結(jié)果和最先遇到的坑啟動本地服務(wù)、加載擴展、運行 agent-simulator.js正常情況會看到控制臺依次輸出能力清單、填充結(jié)果、提交結(jié)果。提交成功后瀏覽器自動跳到 demo-result.html頁面顯示“收到訂單測試訂單-202401”。跑通之后有四個高頻坑你幾乎一定會遇到。第一個坑content script 注入時機。如果 run_at 是 document_idle頁面主框架還在加載時可能拿不到完整 DOM。解決辦法是加上run_at: document_start并在 DOMContentLoaded 后再盤點或者用 MutationObserver 監(jiān)聽頁面變化。第二個坑input 事件沒觸發(fā)。前面提過React 和 Vue 這類框架對原生事件有封裝填充 value 后必須手動派發(fā)事件否則組件內(nèi)部狀態(tài)沒更新。第三個坑表單校驗攔截。很多表單有前端校驗如果 Agent 填的值不滿足校驗規(guī)則表單根本提交不出去。DOM 能力層必須在提交前調(diào)用 form.checkValidity()把校驗失敗信息返回給 Agent讓它重新填。第四個坑頁面跳轉(zhuǎn)導(dǎo)致腳本失效。表單提交后頁面跳轉(zhuǎn)content script 在新頁面會重新注入但 Agent 可能還抱著舊會話 ID。所以會話層要監(jiān)聽頁面跳轉(zhuǎn)事件自動切換新的能力文檔而不是硬闖。以我的經(jīng)驗這四個坑里最傷的是第二個因為它不會直接報錯而是表現(xiàn)為“填了值但提交后數(shù)據(jù)是空的”排查起來非常迷惑。建議從現(xiàn)在開始凡是實現(xiàn)瀏覽器側(cè)填充邏輯一律補 input 事件。5. 實測里最折磨人的五個問題會話漂移、DOM 漂移與誤觸攔截5.1 DOM 漂移SPA 和懶加載的頻繁偷襲我在第 3 章提過能力文檔要及時刷新實際操作中DOM 漂移是最大頻率翻車點。尤其是單頁應(yīng)用SPA它不會整頁刷新而是通過 JS 重寫局部 DOM比如點擊 Tab 切換面板。如果 Agent 用的是舊能力文檔它看到的字段可能已經(jīng)全部移除了。更隱蔽的是懶加載。很多列表頁在滾動到底部時才加載更多數(shù)據(jù)項加載前頁面上根本沒有這些元素。Agent 想采集全部數(shù)據(jù)結(jié)果只讀到第一屏。對策分兩層。DOM 能力層要做“溫和重掃”每次意圖操作執(zhí)行前都檢查當(dāng)前 DOM 與能力文檔的差異如果關(guān)鍵元素列表變化超過閾值就重新生成文檔并警告 Agent。調(diào)度層要做“時機感知”對于懶加載場景Agent 下發(fā)滾動操作后要等待一段時間讓數(shù)據(jù)加載完成而不是立刻采集。我看過很多團(tuán)隊在 DOM 漂移上死磕選擇器方向就偏了。這問題的本質(zhì)是頁面是動態(tài)的所以方案也必須是動態(tài)的——能力文檔不是一次生成永久使用而是伴隨頁面狀態(tài)變化的增量化快照。5.2 多標(biāo)簽頁的會話歸屬錯亂當(dāng) Agent 同時處理多個任務(wù)時最容易出現(xiàn)的詭異問題是任務(wù) A 的 Agent 調(diào)了一個 form.fill結(jié)果表單出現(xiàn)在任務(wù) B 的標(biāo)簽頁里。根因是會話 ID 沒有和具體標(biāo)簽頁的頂層執(zhí)行上下文綁定。我的解決方案是給會話 ID 增加層級任務(wù)ID:標(biāo)簽頁ID:執(zhí)行上下文ID。任務(wù) ID 標(biāo)識一次用戶請求標(biāo)簽頁 ID 標(biāo)識具體瀏覽器 Tab執(zhí)行上下文 ID 標(biāo)識頁面里的一個 iframe 或 Shadow DOM 分區(qū)。每層獨立層層校驗。還有一個很現(xiàn)實的坑標(biāo)簽頁可能被用戶手動關(guān)閉。Agent 還在往這個會話發(fā)請求結(jié)果發(fā)現(xiàn)目標(biāo)頁面沒了。調(diào)度層必須監(jiān)聽標(biāo)簽頁關(guān)閉事件發(fā)現(xiàn)會話綁定的標(biāo)簽頁不存在時立即標(biāo)記會話異常讓 Agent 決定是重新打開頁面還是轉(zhuǎn)人工。5.3 權(quán)限確認(rèn)的誤觸用戶被問太多次就點到“一律允許”WebMCP 涉及瀏覽器操作必然要引入權(quán)限確認(rèn)機制。但這里存在一個產(chǎn)品層面的兩難每次操作都彈窗確認(rèn)用戶嫌煩Agent 效率也低完全不確認(rèn)風(fēng)險大得沒法接受。我在實踐中的折中方案是分級授權(quán)。按操作類型分三級第一級是只讀操作比如讀取頁面數(shù)據(jù)配置好站點白名單后不再彈窗第二級是寫入操作比如填表單、點擊提交彈窗確認(rèn)但記住站點級偏好第三級是高敏感操作比如轉(zhuǎn)賬、刪除、發(fā)送消息必須每次單獨確認(rèn)并且確認(rèn)按鈕要有冷卻時間。這個分級的實現(xiàn)并不復(fù)雜但在體驗和安全的平衡上很有價值。用戶會逐漸形成信任只讀和常規(guī)寫入不用管高敏感操作始終被保護(hù)。如果你不做分級一股腦全彈窗用戶很快就會不耐煩地點“一律允許”權(quán)限防線等于崩潰。5.4 iframe 與 Shadow DOM 的“黑盒”問題真實的后臺系統(tǒng)里iframe 嵌套是常態(tài)特別是老企業(yè)內(nèi)部系統(tǒng)。而 Shadow DOM 則是現(xiàn)代前端組件庫比如 Web Components的常見產(chǎn)物。這兩個東西對 WebMCP 的直接威脅是主文檔 DOM 能力層巡視不到它們。iframe 的問題在于跨源限制。如果 iframe 和主頁面同源可以直接遞歸遍歷如果跨源就不能直接操作其內(nèi)部 DOM必須通過瀏覽器擴展的跨上下文機制或者 postMessage 橋接。Shadow DOM 的問題在于事件穿透和選擇。普通 querySelector 無法穿透 Shadow DOM 邊界必須使用選擇器對 shadowRoot 進(jìn)行遞歸匹配。對策就是一條DOM 能力層必須同時處理三種容器頂層 document、iframe document、Shadow root。并且在能力文檔里顯式標(biāo)注每個元素的容器歸屬和來源 origin。Agent 看到的是統(tǒng)一抽象但真正執(zhí)行時由能力層按歸屬路由到對應(yīng)容器。5.5 并發(fā)任務(wù)時的瀏覽器資源爭搶最后一個高頻問題是并發(fā)。Agent 不是單線程思考它可能在等待頁面 A 的接口響應(yīng)時同時去操作頁面 B。如果用的是同一個瀏覽器實例兩個并發(fā)任務(wù)會爭搶標(biāo)簽頁、爭搶 CPU 計算、爭搶網(wǎng)絡(luò)帶寬嚴(yán)重時直接導(dǎo)致操作互相踩踏。我建議的架構(gòu)是瀏覽器實例池。把每個 Agent 任務(wù)綁定到獨立的瀏覽器上下文可以理解為隱身會話空間任務(wù)之間物理隔離。如果機器資源不允許開太多瀏覽器實例那就回到調(diào)度層的串行隊列——同一時刻只允許一個任務(wù)持有某個頁面組的操作權(quán)。注意瀏覽器實例池不是簡單的多開窗口。它需要和 WebMCP 的會話綁定配合實例 A 里的頁面授權(quán)、Cookie、IndexedDB 完全隔離于實例 B。這樣不僅減少爭搶還順便解決了敏感數(shù)據(jù)串號的問題。缺點是多實例占用內(nèi)存所以在低配機器上我通常建議并發(fā)任務(wù)數(shù)小于 4 用實例池大于 4 上隊列不要讓 Agent 插件模式把所有任務(wù)硬塞進(jìn)一個瀏覽器。6. 真要上生產(chǎn)我建議你先盯緊這幾條底線6.1 最小權(quán)限不是口號是落地的配置項WebMCP 把瀏覽器能力開放給 Agent本質(zhì)上等于把用戶的操作權(quán)交給了程序。如果你不做權(quán)限約束Agent 能訪問任何授權(quán)站點的任何數(shù)據(jù)、執(zhí)行任何操作那隱患比人工操作還大——因為程序可以無限次、極快地執(zhí)行。所以最小權(quán)限原則必須作為系統(tǒng)配置項存在而不是寫進(jìn)需求文檔的一句話。具體落地參考如下按站點分策略每個域名的默認(rèn)權(quán)限是“禁止”。按動作分級別讀、寫、高敏感三層分別配置。按用戶分范圍普通用戶只能讓 Agent 操作其自身可訪問的頁面。按時間窗口限制比如下班后只允許只讀操作不允許寫入。這套配置看起來很繁瑣但我可以說一個真實案例。我見過一個團(tuán)隊把 Agent 接到客戶管理系統(tǒng)沒有做分級Agent 拿到授權(quán)后一口氣給 500 個客戶群發(fā)了營銷消息其中一半是催款內(nèi)容。這種事故一旦發(fā)生負(fù)責(zé)人很難向客戶交代。所以別嫌權(quán)限管理系統(tǒng)麻煩它本質(zhì)上是你避免事故的最后閘門。6.2 審計日志比 Agent 的推理過程更重要很多 Agent 項目一上來就關(guān)注大模型的推理鏈路恨不得把每一次思考都記錄下來。但 WebMCP 場景不一樣你更需要的是操作事實的審計。我強烈建議在會話層記錄以下四類信息。第一是意圖快照Agent 每次下發(fā)的意圖操作原樣記錄下來。第二是執(zhí)行結(jié)果DOM 能力層的返回值成功還是失敗、返回了什么數(shù)據(jù)。第三是頁面前后快照關(guān)鍵操作前后的 DOM 狀態(tài)摘要或者截圖用于事后比對。第四是權(quán)限決策記錄為什么這次操作被允許走的是哪個授權(quán)規(guī)則。我一般會定期把審計日志拿去做回放分析。角色切換成“老刑警”模式先把日志按任務(wù) ID 分組再按操作時間排序看看不同任務(wù)執(zhí)行的路徑差異。這個方法在定位“某次自動化事故到底是 Agent 規(guī)劃錯了還是頁面狀態(tài)異?!睍r非常有用。沒有這套日志排查這類問題就是大海撈針。6.3 操作回滾與熔斷機制讓 Agent 操作網(wǎng)頁必須接受一個現(xiàn)實任何自動化都會出錯。所以設(shè)計階段就要考慮“錯了之后怎么收場”。寫入類操作盡量做可逆或部分可逆。比如填表單在操作前快照表單所有字段的舊值提交前如果 Agent 發(fā)現(xiàn)某個環(huán)節(jié)異常可以先把表單恢復(fù)原狀。這不難實現(xiàn)在能力文檔里加入snapshot和restore兩個工具即可。更底層的保障是熔斷。如果連續(xù)出現(xiàn)操作失敗比如連續(xù) 3 次 DOM 能力盤點失敗、連續(xù) 2 次表單校驗失敗調(diào)度層必須停止繼續(xù)下發(fā)新操作并把控制權(quán)交回給 Agent 或人工。不要指望 Agent 自己發(fā)現(xiàn)問題它的長鏈路推理里很容易把異常當(dāng)作新任務(wù)處理一路走偏。熔斷機制是系統(tǒng)層面的“剎車”。高敏感操作的回滾比較復(fù)雜。比如“刪除一條記錄”基本不可逆。這種操作在核心流程里建議不要給 Agent 完全自動執(zhí)行的權(quán)限一定要加人工審批節(jié)點。寧可操作慢一步也比事后補救強十倍。6.4 灰度上線讓 Agent 先在仿真環(huán)境跑最后一個底線是灰度驗證。不要讓 Agent 直接連接生產(chǎn)站點尤其是剛接入 WebMCP 的初期。在內(nèi)部項目里我會先搭一套仿真環(huán)境把生產(chǎn)頁面完整復(fù)制一份連后端接口都 Mock 掉。Agent 先在仿真環(huán)境跑通全流程確認(rèn)無誤后再切換到生產(chǎn)并且一開始只開放低頻、低風(fēng)險的操作范圍。這個做法的好處有兩個。第一是排錯安全Agent 在仿真環(huán)境里怎么折騰都不影響真實業(yè)務(wù)。第二是可用真實數(shù)據(jù)驗證能力文檔的完整性仿真環(huán)境會有很多生產(chǎn)環(huán)境才有的極端頁面狀態(tài)比如某字段超長、某元素重復(fù) IDAgent 在仿真環(huán)境暴露的問題越多上線后的坑越少。灰度上線的節(jié)奏我一般這樣安排仿真環(huán)境全量測試通過后切到生產(chǎn)環(huán)境的一個測試賬號跑 3 到 5 天重點觀察審計日志里的異常比例。異常比例降到閾值以下才逐步開放到部分真實用戶。這個過程不復(fù)雜但特別考驗?zāi)托暮芏鄨F(tuán)隊在仿真環(huán)境剛跑通就急著全面放量結(jié)果第二天就被頁面改版打回原形。結(jié)尾我的實際體會和一個小建議最后聊點個人的體會。WebMCP 的價值不在于又多了一個新協(xié)議名詞而在于它把“網(wǎng)頁能力如何暴露給 Agent”這件事從每個團(tuán)隊各自為政變成了一套可復(fù)用、可審計、可組合的標(biāo)準(zhǔn)化思路。我實際跑下來最明顯的感受是當(dāng)網(wǎng)頁自己會“說話”之后Agent 的開發(fā)重心可以重新回到任務(wù)規(guī)劃和異常處理上而不是花費大量的時間與某個按鈕的選擇器纏斗。你不再需要為一個頁面的改版而焦慮因為 WebMCP 層會重新盤點能力Agent 只不過換了一張新的能力清單。當(dāng)然它目前還遠(yuǎn)談不上完美權(quán)限模型、會話隔離、跨源 iframe 這些都是需要繼續(xù)打磨的地方也不適合所有場景。如果你準(zhǔn)備入局我建議從小站點、低頻的寫入類任務(wù)開始驗證這套思路先把安全性、審計、回滾這些底線工程做扎實再談大規(guī)模自動化。這比急著做一個全自動的“瀏覽器機器人”要靠譜得多。