使用者說「找 2000 元以下的鍵盤」,網站需要把關鍵字與預算交給商品搜尋,再回傳商品名稱、價格與連結。
今天沿用 WordPress 外掛,新增 search_products 與 get_product。商品資料直接來自 WooCommerce Store API,前端不用放 API Key,也不用自己重做商品搜尋。
本機測試中,1,290 元的機械鍵盤符合條件,2,490 元的高階鍵盤被排除。這篇最容易踩到的地方是價格單位:輸入的 2,000 元,要依商店小數位換成 API 使用的整數。
本次流程是:
Agent
→ search_products/get_product
→ fetch
→ WooCommerce Store API
→ 整理後的商品 JSON
Store API 提供前台商品、購物車與結帳所需的介面。本篇只使用公開商品讀取:
GET /wp-json/wc/store/v1/products
GET /wp-json/wc/store/v1/products/{id}
商品搜尋支援關鍵字、價格、分類與屬性。本次 Tool 先開放 keyword、maxPrice、limit 三個參數,讓輸入範圍清楚。
這與需要驗證的 WooCommerce 後台管理 API 分工不同。商品查詢使用 credentials: 'omit',不傳送登入 Cookie、Nonce 或管理用 API Key。
?webmcp_lab=25。本機網址是 https://wordpress.local/?webmcp_lab=25。本機示範資料如下:
| 商品 | ID | 價格(TWD) | 狀態 |
|---|---|---|---|
| Day25 機械鍵盤 | 101 | 1290 | 已發佈 |
| Day25 高階鍵盤 | 102 | 2490 | 已發佈 |
| Day25 無線耳機 | 103 | 1680 | 已發佈 |
| Day25 草稿鍵盤 | 104 | 699 | 草稿 |
商品 ID 由各站的 WordPress 產生。公開商品使用該站搜尋結果中的 ID;草稿商品的 ID 則從後台商品編輯網址的 post= 參數取得。
Tool 收到的 maxPrice 是使用者看得懂的金額,Store API 的 max_price 則使用最小價格單位。換算方式是:
API 價格 = 使用者金額 × 10 的小數位次方
本機小數位是 2,因此:
maxPrice: 2000
→ max_price=200000
prices.price: "129000"
currency_minor_unit: 2
→ 1290 TWD
不能只看到 TWD 就把小數位寫死為 0。外掛從 WooCommerce 讀取實際設定,再交給 JavaScript:
wp_localize_script(
'wp-webmcp-products',
'wpWebMCPProducts',
array(
'productsUrl' => esc_url_raw(rest_url('wc/store/v1/products')),
'currency' => get_woocommerce_currency(),
'minorUnit' => wc_get_price_decimals(),
)
);
程式會驗證金額是否非負、是否超過商店允許的小數位,以及換算後是否仍是安全整數。像小數位為 2 時輸入 1.001,會回傳 invalid_input,不把預算偷偷四捨五入成另一個金額。
圖片 1|Store API 回傳價格 129000、小數位 2,Tool 換算為 1,290 TWD。
Tool 的輸入格式如下:
{
"keyword": "鍵盤",
"maxPrice": 2000,
"limit": 5
}
keyword:必填,1~100 字。maxPrice:選填,使用商店幣別的非負金額;省略就不限制最高價格。limit:選填,1~10 的整數,預設 5。註冊時把用途寫清楚:搜尋商品,不查歷史訂單,也不操作購物車。下面的 run() 是完整範例 assets/products.js 中共用的查詢函式。
await document.modelContext.registerTool({
name: 'search_products',
description: 'Search public WooCommerce products by keyword and optional maximum price in the store currency. For product discovery, not articles, carts or past orders. Does not buy anything.',
inputSchema: {
type: 'object',
properties: {
keyword: { type: 'string', minLength: 1, maxLength: 100 },
maxPrice: { type: 'number', minimum: 0 },
limit: { type: 'integer', minimum: 1, maximum: 10, default: 5 }
},
required: ['keyword'],
additionalProperties: false
},
annotations: { readOnlyHint: true },
execute: args => run('search_products', args)
});
共用函式完成驗證與價格換算後,加入這些查詢條件:
search=鍵盤
per_page=5
orderby=price
order=asc
max_price=200000
結果依價格由低到高排列。網址由 PHP 的 rest_url() 提供,程式同時支援 /wp-json/ 與 index.php?rest_route=...,不把固定網址格式寫死。
前端請求只讀取公開資料:
const response = await fetch(url, {
credentials: 'omit',
cache: 'no-store',
headers: { Accept: 'application/json' }
});
接著整理 Store API 回應。價格使用每筆商品回傳的 currency_minor_unit 換算,並保留 currency_code,避免 Agent 把原始整數當成售價。
搜尋結果會保留 id、name、type、price、onSale、inStock、hasOptions 與 url。其中一筆價格資料為:
{
"id": 101,
"name": "Day25 機械鍵盤",
"price": {
"amount": 1290,
"currency": "TWD",
"range": null
}
}
有價格區間時,range 保留最低與最高金額;沒有定價時,amount 使用 null。商品有選項時,也保留 hasOptions,供後續選擇規格使用。
search_products。{"keyword":"鍵盤","maxPrice":2000,"limit":5}。status: success、count: 1,以及機械鍵盤的 price.amount: 1290、currency: TWD。圖片 2|search_products 篩出 2,000 元以下的機械鍵盤,並回傳明確的金額與幣別。
搜尋清單先提供挑選商品所需的資訊。選定商品後,再將搜尋結果的 ID 傳給 get_product:
{ "productId": 101 }
get_product 只接受正整數 productId,查詢 Store API 的單一商品 endpoint。除了商品基本資料,回傳內容增加:
shortDescription:純文字短說明。description:純文字完整說明。isPurchasable:API 回傳的可購買狀態。attributes:屬性名稱與選項。HTML 說明先放入未掛到畫面的 template,移除 script、style,再擷取文字;頁面用 textContent 顯示。Tool 描述同時要求把商品文字視為資料,不把商品說明中的句子當成操作指令。
get_product。{"productId":101}。<p> 標籤。這個 Tool 負責讀取商品詳情。選擇變體、加入購物車與付款不在本篇功能範圍內。
圖片 3|get_product 使用搜尋結果中的商品 ID,取得純文字說明與商品狀態。
將 Inspector 的 Tool 改回 search_products,在 Input Arguments 輸入:
{ "keyword": "xyz-123", "limit": 5 }
按 Execute Tool。這次 API 回傳 HTTP 200 與空陣列,Tool 整理成:
{ "status": "no_results", "count": 0, "products": [] }
這表示沒有符合條件的商品。網路失敗或 API 異常則回傳 internal_error,不把所有錯誤都寫成「沒有商品」。
圖片 4|查詢成功但沒有符合商品時,Tool 回傳 no_results 與空清單。
Store API 的公開商品集合會排除草稿與其他未發佈商品;直接用未發佈商品 ID 查詢,也會回 HTTP 404。
先在後台確認 ID 104 的「Day25 草稿鍵盤」維持草稿狀態,再回到 Inspector,將 Tool 改成 get_product,在 Input Arguments 輸入 {"productId":104},按 Execute Tool。回傳結果如下:
{
"status": "no_results",
"httpStatus": 404,
"message": "找不到可讀取的商品。"
}
以下整理本次透過 Inspector 的 Execute Tool 取得的實測結果:
| 操作 | HTTP | 結果 |
|---|---|---|
| 鍵盤、最高 2000 元 | 200 | 只回傳 ID 101,1290 TWD |
| 讀取商品 101 | 200 | 回傳價格與純文字商品說明 |
| 搜尋 xyz-123 | 200 | no_results,空清單 |
| 讀取草稿商品 104 | 404 | no_results |
圖片 5|直接查詢草稿商品 ID,Store API 回傳 404,Tool 保留查無商品的結果。
使用 Inspector 的 Send 測試自然語言;每題先 Reset,再記錄實際 Tool 與參數:
search_products 是否帶入 keyword 與 maxPrice。get_product。