如果所有 Tool 都只有「可以呼叫/不可以呼叫」兩種狀態,安全設計很快會失控。search_posts、add_to_cart、delete_account 都是 Tool,但副作用、可逆性、資料敏感度完全不同。
今天做一張 Tool Risk Matrix,把能力分成 Read、Navigation、Reversible Action、Consequential Action,並對應 WebMCP annotations 與後端權限策略。
這四級是本文整理的設計分類,不是 WebMCP 官方定義的權限等級。實際風險還要一起考慮資料敏感度、操作範圍與使用情境。
目前系列已經有:
get_page_info
search_content
get_content
navigate_to_section
prepare_contact_message
add_to_favorites
prepare_checkout
confirm_checkout
如果只寫:
這些都是 Agent Tools
資訊太少。
不修改業務資料,但可能更新畫面上的查詢結果:
search_content
get_content
get_product
get_cart
通常:
annotations: {
readOnlyHint: true
}
但「read-only」不代表沒有隱私風險。
get_cart 可能透露使用者興趣;get_orders 更可能透露地址、消費紀錄。
所以資料權限仍要檢查。
例如:
navigate_to_section
open_filter_panel
對 Server 沒有永久副作用,但會改目前 UI/route。
通常風險低,但要避免釣魚式跨 Origin 導航或意外離開未儲存頁面。
例如:
add_to_favorites
add_to_cart
remove_from_cart
save_draft
會改狀態,但通常可逆。
需要依業務規則考慮:
例如訪客購物車可以使用 Session,不一定要求會員登入。加入收藏可以設計成重複不變,但 add_to_cart 通常會累加數量;「可逆」不等於「冪等」。
例如:
confirm_checkout
delete_account
publish_post
transfer_money
book_flight
會造成真實、重大或難逆轉的結果。
這類 Tool 應考慮:
annotations: {
readOnlyHint: false,
consequentialHint: true
}
並配合 Explicit Confirmation。
📸 圖片 1|WebMCP Tools 的四級風險矩陣
假設:
search_reviews
get_forum_posts
fetch_external_content
即使它們都是 Read,輸出可能包含 Prompt Injection。
因此可以加:
untrustedContentHint: true
這跟「有沒有副作用」是另一條軸。untrustedContentHint 只標示輸出需要謹慎處理,不會自動清除惡意文字,也不能保證擋下 Prompt Injection。本日 search_reviews 使用固定的模擬評論示範這個標記。
所以 Tool Risk 不是一個單純 Enum,而比較像:
State-changing?
Consequential?
Contains untrusted output?
Contains private data?
Requires authentication?
下面是設計示例,不是所有網站都必須採用的固定政策。Auth 的 Session 表示需要辨識目前購物車的擁有者,不一定是已登入會員;本日 Demo 則選擇用模擬登入展示存取限制。△ 代表依資料來源判斷,不是 annotation 的第三種值。
| Tool | Read-only | 可逆 | Consequential | Untrusted output | Auth |
|---|---|---|---|---|---|
| search_products | ✅ | - | ❌ | △ | ❌ |
| get_cart | ✅ | - | ❌ | ❌ | Session |
| add_to_cart | ❌ | ✅ | ❌ | ❌ | Session |
| search_reviews | ✅ | - | ❌ | ✅ | ❌ |
| confirm_checkout | ❌ | 依取消/退款規則 | ✅ | ❌ | ✅ |
| delete_account | ❌ | 依復原機制 | ✅ | ❌ | ✅ |
這張表會直接影響:
要不要確認
要不要登入
要不要記 audit log
Tool 能不能跨 Origin 暴露
Result 能不能包含私人資料
在本地 Demo 中,Guest 可以呼叫 search_products,傳入 {"keyword":"外套","color":"黑色","maxPrice":2000},取得兩筆公開商品。這個操作不會修改購物車,也不需要登入。
📸 圖片 2|Guest 可直接搜尋公開商品
同樣是 Guest,以 {} 呼叫 get_cart,Demo 會回傳 unauthenticated。雖然 get_cart 標記 readOnlyHint: true,購物車仍可能包含私人資訊,因此本例要求先登入。
九個 Tools 在 Guest 狀態下仍然可見;限制是在執行時檢查,而不是靠隱藏 Tool 名稱。
📸 圖片 3|未登入讀取購物車,回傳 unauthenticated
按下「模擬登入」後,以 add_to_cart 加入商品 102 的 M 號一件:
{"productId":102,"variationId":"102-M","quantity":1}
成功結果包含尺寸、數量與小計 NT$1,890。這次呼叫確實修改了購物車;若要撤回,可用 remove_from_cart 傳入 {"variationId":"102-M"},移除整個尺寸項目。
再次執行 add_to_cart 會累加數量,因此不能把重試視為沒有副作用。
📸 圖片 4|模擬登入後,add_to_cart 成功加入 M 號外套一件
購物車有一件商品後,以 {} 呼叫 prepare_checkout,會得到小計 NT$1,890、運費 NT$60、總額 NT$1,950 的預覽。再把回傳的 previewId 交給 confirm_checkout,網站會顯示商品明細與確認按鈕。
這時 Tool 正在等待使用者選擇,尚未建立訂單。按「確認並模擬下單」才會建立模擬訂單;按「取消」則回傳 cancelled。
📸 圖片 5|網站顯示商品與總額,等待使用者確認
這個視窗由網站程式提供,不是 consequentialHint 自動產生的瀏覽器確認介面。Demo 用模擬下單呈現高風險流程,不會實際扣款,也沒有實作刪除帳號。
以上圖片是 Inspector 手動呼叫結果;模擬登入、購物車與訂單都只存在本頁記憶體,不能視為已驗證真正的後端授權。重新整理或模擬登出會清空資料。
這點一定要重複。
consequentialHint: true
並不等於後端自動安全。
例如在 WordPress 中,後端仍需執行權限檢查:
if (! current_user_can('delete_users')) {
return new WP_Error('forbidden', 'Not allowed', ['status' => 403]);
}
上面只是管理使用者功能的權限檢查片段,不是「使用者刪除自己帳號」的完整實作。實際操作還要驗證目標帳號、請求來源、確認流程及適用的業務限制。
WebMCP annotations 是給 Agent/Browser 的安全語意,不是 ACL(存取控制清單)。前端的模擬登入旗標和確認按鈕,也不能替代後端的身分與授權檢查。
例如:
Tool: get_orders
Authentication:
Required
Authorization:
Only current user's orders
Side effect:
None
Sensitive output:
Order history, partial address
Cross-origin exposure:
No
Confirmation:
No
這比只寫一個 Description 更接近 Production Design。
readOnlyHint、consequentialHint、untrustedContentHint 是不同維度。