作品集 ·
WebMCP Agent
Chrome 側邊欄擴充功能:用自選的 LLM(Ollama、OpenRouter、Anthropic、Gemini、Chrome 內建模型)透過 WebMCP 呼叫網頁自己註冊的工具,在頁面上實際執行動作,且每次執行前都能確認或改參數。
背景與動機
WebMCP 是 Chrome 從 150 版開始實驗的網頁端 Model Context Protocol:網頁可以用 document.modelContext 對外註冊自己的工具,讓 AI 代理不必再靠猜測 DOM、模擬點擊來操作頁面。但這個能力目前只綁在瀏覽器內建的 Gemini Nano 上。
這個專案想回答的問題是:能不能讓使用者自己挑模型——本機跑的 Ollama、OpenRouter 上的任何模型、或自架的 OpenAI 相容端點——一樣去驅動網頁提供的 WebMCP 工具,而且整個過程使用者看得到、擋得住。
做了什麼
一個 Chrome MV3 擴充功能,側邊欄是聊天介面,輸入指令後由代理迴圈接手:把當前分頁註冊的工具轉成該 provider 的 tool 定義、串流接收模型回覆、逐一執行工具呼叫、把結果餵回去,直到任務完成。
- 六種 LLM 提供者:Ollama、OpenRouter、任意 OpenAI 相容端點、Anthropic、Google Gemini,以及 Chrome 內建的 Gemini Nano。全部是原生
fetch加自己寫的 SSE / NDJSON 解析,沒有引入任何廠商 SDK - 人在迴圈中:每個工具呼叫都會跳出確認卡片,可以允許、拒絕、或修改參數後再允許;確認策略分成「每次都問」「只問會改狀態的」「全自動」三段,也能對單一工具在本次對話中一律放行
- 跨 iframe 工具:內容腳本注入每一個 frame,只回報屬於自己
window的工具,再由側邊欄依 frameId 彙整並加上前綴,避免不同 frame 的同名工具互相蓋掉 - 提示注入防護:標記為
untrustedContentHint的工具結果會包進<untrusted_content>,並在 system prompt 明確要求模型把它當資料而非指令 - 示範商店與端對端腳本:附一組會註冊唯讀、有狀態、會導頁、含 iframe 與注入測試字串等各類工具的測試頁,搭配透過 CDP 驅動真實 Chrome 的煙霧測試與完整代理測試腳本
技術組成
WXT + React 19 + TypeScript + Tailwind 4,以 pnpm 管理。三個擴充功能情境用一份自己定義的訊息協定溝通:內容腳本是 document.modelContext 的薄包裝,背景 service worker 只負責側邊欄行為與徽章,代理迴圈刻意跑在側邊欄而不是 service worker——MV3 隨時會把 worker 殺掉,長時間的代理任務放在那裡並不安全。
單元測試用 Vitest,把 fetch 換成能一小塊一小塊吐串流的假實作,專門打各家串流解析器的邊界;端對端則用 playwright-core 接上一個帶著 WebMCP flag、獨立設定檔的 Chrome for Testing。
亮點與難點
- 對著會動的 API 開發:
document.modelContext仍在演進,executeTool回傳的是 JSON 字串、inputSchema有時是字串有時是物件、輸入要先當物件送再退回字串重試——這些行為都得靠實測歸納,型別宣告也只能自己手寫 - 工具把頁面導走的那一刻:呼叫進行到一半頁面就換了,內容腳本隨之死亡、訊息通道直接斷線。做法是把這類錯誤判讀成「導航發生了」,等分頁穩定後改讀新文件的
<script type="application/ld+json">取結果,跟 Chrome 官方檢查工具的慣例一致 - 讓穩定版 Chrome 也能用工具:Chrome 內建的 Prompt API 還沒有原生 tool calling,這裡用結構化輸出(
responseConstraint)把工具呼叫模擬出來 - 錯誤要能回收:拒絕、未知工具、執行失敗都不是中斷,而是轉成 tool 訊息回饋給模型,讓它有機會換個方式再試
