MCP 接入指南
本指南面向希望透過 MCP(Model Context Protocol) 將電商或業務平台接入 Webnav.ai 的開發者。MCP 是基於 JSON-RPC 2.0 over HTTP 的開放標準工具協議。
MCP 模式為 Enterprise 功能。
為什麼選擇 MCP?
MCP 是 AI Actions 的接入方式:你執行一個 MCP 工具伺服器,Webnav.ai 透過 tools/list 自動發現你的工具,依其 annotations(destructiveHint、readOnlyHint 等)驅動確認邏輯,並以開放標準呼叫它們。所有工具皆套用相同的確認流程、SKU 校驗、呼叫日誌與 SSRF 防護。
架構
訪客 → Widget → Webnav.ai ──JSON-RPC 2.0──► 你的 MCP 伺服器
◄──────────────── (工具結果)- 域名初次載入時,Webnav.ai 對你的 MCP 端點發送
initialize。 - 呼叫
tools/list發現可用工具(含 annotations)。 - 訪客觸發動作時,Webnav.ai 呼叫
tools/call並傳入工具名與參數。 - 寫工具(
create_order、create_refund)會先彈出確認卡片——訪客必須點確認才會執行。
Canonical 工具(5 個標準工具)
以下五個工具是 Webnav.ai 原生識別的工具,建議全部實作以獲得完整電商支援;也可只實作其中一部分。
工具總覽
| 工具 | 類型 | 需確認 | 用途 |
|---|---|---|---|
list_products | 讀 | 否 | 回傳商品目錄,用於 SKU 校驗 |
query_order | 讀 | 否 | 查詢訂單狀態/詳情 |
query_logistics | 讀 | 否 | 查物流軌跡 |
create_order | 寫 | 是 | 下新訂單(items 陣列,一次呼叫) |
create_refund | 寫 | 是 | 發起退款申請 |
list_products由 Webnav.ai 在內部使用,用於在顯示確認卡片前校驗 SKU。若訪客要求的 SKU 不在list_products結果中,助手會提示其從真實商品中選擇——無效 SKU 不會彈出確認卡片。
工具規格
list_products
{
"name": "list_products",
"description": "回傳可購買的商品目錄。",
"inputSchema": { "type": "object", "properties": {} },
"annotations": { "readOnlyHint": true }
}回應 data.products:
[
{ "sku": "PB-10000", "name": "行動電源 10000 mAh", "price": "19.99" },
{ "sku": "CB-USBC", "name": "USB-C 充電線 2m", "price": "7.99" }
]query_order
{
"name": "query_order",
"description": "依訂單 ID 查詢訂單狀態與詳情。",
"inputSchema": {
"type": "object",
"properties": { "order_id": { "type": "string", "description": "訂單識別碼" } },
"required": ["order_id"]
},
"annotations": { "readOnlyHint": true }
}回應 data:
{
"order_id": "SO20260613",
"status": "paid",
"amount": "199.00",
"currency": "USD",
"items": [{ "sku": "PB-10000", "name": "行動電源", "qty": 2 }]
}query_logistics
{
"name": "query_logistics",
"description": "查詢訂單的物流狀態。",
"inputSchema": {
"type": "object",
"properties": { "order_id": { "type": "string" } },
"required": ["order_id"]
},
"annotations": { "readOnlyHint": true }
}回應 data:
{
"order_id": "SO20260613",
"carrier": "順豐",
"tracking_no": "SF123456789",
"status": "in_transit",
"tracks": [
{ "time": "2026-06-12 14:30", "desc": "到達深圳轉運中心" },
{ "time": "2026-06-13 09:10", "desc": "派送中" }
]
}create_order
{
"name": "create_order",
"description": "下新訂單,每件商品必須是 list_products 中的真實商品。",
"inputSchema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"description": "要訂購的商品清單",
"items": {
"type": "object",
"properties": {
"sku": { "type": "string", "description": "商品 SKU" },
"quantity": { "type": "integer", "minimum": 1 },
"name": { "type": "string", "description": "商品名稱(可選)" }
},
"required": ["sku", "quantity"]
}
},
"address": { "type": "string", "description": "配送地址(可選)" }
},
"required": ["items"]
},
"annotations": { "destructiveHint": true, "idempotentHint": false, "title": "下單" }
}回應 data:
{ "order_id": "SO20260614", "pay_url": "https://pay.example.com/SO20260614", "amount": "39.98" }
items為陣列——訪客在一次對話中可新增多件商品,Webnav.ai 會一次性呼叫create_order並帶入所有商品,只彈一張確認卡片。
create_refund
{
"name": "create_refund",
"description": "為一筆訂單發起退款申請。",
"inputSchema": {
"type": "object",
"properties": {
"order_id": { "type": "string" },
"reason": { "type": "string", "description": "退款原因(可選)" }
},
"required": ["order_id"]
},
"annotations": { "destructiveHint": true, "title": "申請退款" }
}回應 data:
{ "refund_id": "RF20260613", "status": "pending", "amount": "199.00" }工具 Annotations
Annotations 是 tools/list 中的工具元資料,告知 Webnav.ai 如何處理每個工具:
| Annotation | 類型 | 作用 |
|---|---|---|
destructiveHint | bool | true → Webnav.ai 呼叫前一定顯示確認卡片 |
readOnlyHint | bool | true → 無需確認,直接呼叫 |
idempotentHint | bool | false → 額外謹慎,可能強制更嚴格的單次使用待確認 |
openWorldHint | bool | true → 工具可能有不可預期的副作用 |
title | string | 確認卡片上顯示的可讀標籤 |
信任模型: destructiveHint 為你的伺服器自行申報。Webnav.ai 不完全信任 annotations——它還會套用基於名稱的規則(create_*、refund_*、cancel_*、pay_* → 一律確認)以及你在後台配置的 mcpConfirmTools 清單。三層 OR 疊加,寫工具永遠不會因漏標 destructiveHint 而被跳過確認。
通訊格式
握手(initialize)
域名首次連接時,Webnav.ai 發送:
{
"jsonrpc": "2.0",
"id": "1",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"clientInfo": { "name": "webnav-ai", "version": "1.0" },
"capabilities": {}
}
}你的伺服器回應:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"protocolVersion": "2024-11-05",
"serverInfo": { "name": "my-shop-mcp", "version": "0.1.0" },
"capabilities": {}
}
}若協議版本不符,Webnav.ai 記錄 versionMismatch 警告(在 Dashboard → AI Actions → 測試連接中可見),但仍繼續運作。
工具發現(tools/list)
// 請求
{ "jsonrpc": "2.0", "id": "2", "method": "tools/list" }
// 回應
{
"jsonrpc": "2.0",
"id": "2",
"result": {
"tools": [
{
"name": "list_products",
"description": "回傳商品目錄。",
"inputSchema": { "type": "object", "properties": {} },
"annotations": { "readOnlyHint": true }
},
{
"name": "create_order",
"description": "下新訂單。",
"inputSchema": { "type": "object", "properties": { "items": { "type": "array" } } },
"annotations": { "destructiveHint": true, "title": "下單" }
}
]
}
}工具呼叫(tools/call)
// 請求
{
"jsonrpc": "2.0",
"id": "3",
"method": "tools/call",
"params": {
"name": "query_order",
"arguments": { "order_id": "SO20260613" }
}
}
// 回應 —— canonical 格式(推薦)
{
"jsonrpc": "2.0",
"id": "3",
"result": {
"ok": true,
"data": { "order_id": "SO20260613", "status": "paid", "amount": "199.00" },
"display": {
"title": "訂單 SO20260613",
"fields": [{ "label": "狀態", "value": "已付款" }]
}
}
}
// 回應 —— MCP content-block 格式(也接受)
{
"jsonrpc": "2.0",
"id": "3",
"result": {
"content": [{ "type": "text", "text": "{\"order_id\":\"SO20260613\",\"status\":\"paid\"}" }],
"isError": false
}
}兩種格式均支援。Canonical {ok, data, display} 格式可獲得更豐富的 widget 卡片渲染效果。
Display 卡片渲染能力
工具結果上的可選 display 物件決定 widget 如何渲染結構化卡片。所有欄位皆為可選,可自由組合。
"display": {
"title": "訂單 SO20260613",
"subtitle": "標題下方的輔助說明。",
"fields": [{ "label": "狀態", "value": "已付款" }],
"items": [{ "title": "到達轉運中心", "time": "2026-06-12 14:30" }],
"buttons": [{ "label": "查看訂單", "url": "/orders/SO20260613", "style": "primary" }],
"options": [{ "label": "AlipayPlus", "value": "alipay", "hint": "本地電子錢包" }],
"submit": { "label": "生成訂單", "url": "/checkout?method={value}", "target": "_self" }
}| 欄位 | 類型 | 渲染為 |
|---|---|---|
title | string | 卡片標題 |
subtitle | string | 標題下方的淺色說明 |
fields | [{label, value}] | 鍵值行 |
items | [{title|desc, time?}] | 時間軸列表(如物流軌跡) |
products | […] | 橫向滾動商品卡 |
buttons | [{label, url, style?}] | 操作按鈕。url 於新分頁開啟;根相對 /path 會以你的 data-base-url 為前綴。style:primary(預設)/ secondary |
options + submit | 見下 | 單選卡(radio 列表 + 一個提交按鈕) |
單選卡(options + submit)
用於讓訪客不打字、選一項即可操作。widget 把 options 渲染為 radio 列;提交按鈕在選中前保持禁用。
options:[{ label, value, hint? }]submit:點擊後的行為(url優先於prompt):submit.url——導航宿主視窗至該 URL。{value}/{label}以所選項替換。根相對/path以嵌入頁面的 origin 解析(而非data-base-url)。submit.target:_self(預設)或_blank。submit.prompt——把該文字作為訪客的下一條對話訊息發送(交回助手處理;同樣替換{value}/{label})。當下一步需要再經由助手時使用。submit.label——按鈕文字;省略時 widget 顯示本地化預設值。
範例:充值工具把啟用的支付方式作為
options回傳,並給一個submit.url如"/wallet?amount=5&method={value}&autopay=1"。訪客選方式、點按鈕,宿主頁面即下單並開啟結帳——無需額外對話輪次或確認。
授權卡(AUTH_REQUIRED)
當用戶相關工具沒有有效的 user token 時,回傳一個帶 display 的業務錯誤,而非靜默失敗:
{ "ok": false, "error_code": "AUTH_REQUIRED", "message": "請先登入。",
"display": { "title": "連接你的帳號",
"subtitle": "登入後繼續。",
"buttons": [{ "label": "登入", "url": "/login", "style": "primary" }] } }widget 會把它渲染成授權卡——讀工具、以及寫工具確認失敗時皆適用——讓訪客看到登入按鈕而非一行錯誤。
訪客身份轉發(user token)
對於用戶相關的工具(某帳號的訂單、餘額、API Key、用量…),你的站點為已登入訪客簽發短時 user token 並交給 widget:
window.WebnavWidget.setUserToken('YOUR_SIGNED_USER_TOKEN')Webnav.ai 不解析也不儲存此令牌。每次 tools/call 時,它會把令牌不透明地作為 HTTP 標頭轉發給你的 MCP 端點——預設為:
X-Webnav-User-Token: <你的 user token>你可在 後台 → AI Actions 改用其它標頭名(如 Authorization)。由你的 MCP 伺服器自行驗證該令牌(如校驗 JWT 簽名)並還原真實用戶。
寫工具/確認: 令牌在確認呼叫時會再次重發,並以雜湊與待確認動作綁定——確保已確認的 create_* 以發起時的同一身份執行。確認時令牌缺失/被竄改會被拒絕(TOKEN_MISMATCH)且不執行。
需要用戶身份但未收到有效令牌的讀工具,應回傳業務錯誤(如
{ok:false, error_code:"AUTH_REQUIRED"})——widget 會渲染授權卡片提示訪客登入,而非靜默失敗。
錯誤歸一化
Webnav.ai 將所有 MCP 傳輸和協議錯誤統一映射為 error_code:
| 情境 | error_code |
|---|---|
| HTTP 狀態非 2xx | MCP_HTTP_<status>(如 MCP_HTTP_503) |
JSON-RPC error 欄位 | MCP_RPC_ERROR |
結果中 isError: true | MCP_TOOL_ERROR |
| 連接逾時 | MCP_TIMEOUT |
| Host 無法連接/拒絕連接 | MCP_UNREACHABLE |
這些錯誤碼會出現在 Dashboard → AI Actions → 呼叫日誌,以及助手轉述給訪客的錯誤訊息中。
你自己的業務錯誤應在 data.error_code 欄位使用描述性錯誤碼(如 ORDER_NOT_FOUND、OUT_OF_STOCK)。
後台配置
- 進入 Dashboard → AI Actions。
- 將 模式 切換為 MCP。
- 輸入你的 MCP 端點(如
https://mcp.yourshop.com/mcp)。 - 視需要新增鑑權標頭(鍵值對——值加密儲存,不以明文回傳)。
- 點測試連接——Webnav.ai 透過
initialize+tools/list探測你的端點,並顯示發現的工具清單與 annotations。 - 在需確認工具面板中,確認預選的寫工具是否正確,並補充任何其他需要確認卡片的工具名稱。
- 點儲存。
關於測試連接的說明: 探測請求會透過 Webnav.ai 後端代理發出——你的 MCP 端點不會被瀏覽器直接訪問,同樣受 SSRF 防護(本地環境開啟
ACTIONS_ALLOW_INSECURE時除外)。
安全
| 要求 | 原因 |
|---|---|
| 使用 HTTPS 服務 | Webnav.ai 綁定解析 IP 並驗證憑證。 |
| 使用公網 host | Webnav.ai 拒絕私網/環回/元數據位址(SSRF 防護)。 |
| 驗證鑑權標頭 | 在 mcp_headers 中設定 bearer token 或 API key,並在每次請求時於你的伺服器端驗證。 |
| 寫工具冪等設計 | Webnav.ai 只發一次確認呼叫,但網路重試可能發生——設計 create_order/create_refund 使其安全地可重複接收。 |
不依賴 destructiveHint 作為唯一安全機制 | Webnav.ai 還會套用名稱規則與 mcpConfirmTools 作為額外安全層。 |
Demo MCP 伺服器
倉庫提供兩個演示用 MCP 伺服器,皆實作全部五個 canonical 工具並帶有正確的 annotations。
方案 A — 模擬商城整合端點(推薦)
演示商城(examples/shop/shop_server.py,連接埠 4000)暴露 /mcp 端點,與店面共用同一份訂單資料。AI 透過 MCP 下的單會即時出現在店面「我的訂單」面板中——這是端到端體驗「訪客 → widget → MCP → 訂單」完整鏈路的最佳方式。
# 同時啟動店面 + /mcp,位於 http://localhost:4000
WEBNAV_SECRET=webnav-mock-secret-001 ./.venv/bin/python examples/shop/shop_server.py可選 Bearer 鑑權:啟動時設定 MCP_AUTH_TOKEN=<token>,再於後台 Auth Headers 加入 Authorization: Bearer <token>。不設定 = 不鑑權(演示預設)。
然後在 Dashboard → AI Actions 中設定模式 = MCP、端點 = http://localhost:4000/mcp,點測試連接即可。
方案 B — 獨立參考伺服器
examples/mcp_server.py(連接埠 4100)是最精簡的獨立參考實作,使用自己的記憶體訂單儲存(不與店面面板連動),適合純協議測試。
警告: 兩者皆為參考/演示實作。獨立伺服器沒有鑑權或冪等保護。請勿直接部署到生產環境。
./.venv/bin/python examples/mcp_server.py # 啟動在 http://localhost:4100/mcp直接測試通訊格式
# 握手(獨立伺服器請把 4000 換成 4100)
curl -s -X POST http://localhost:4000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"test","version":"1"}}}'
# 發現工具
curl -s -X POST http://localhost:4000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"2","method":"tools/list"}'
# 呼叫工具
curl -s -X POST http://localhost:4000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"list_products","arguments":{}}}'