iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Modern Web

WebMCP:30 天打造 AI Agent 看得懂、也操作得動的網站系列 第 9

Day 09|登入前後 Tools 不一樣怎麼辦?動態註冊 WebMCP Tool 實戰

  • 分享至 

  • xImage
  •  

本篇重點

WebMCP Tool 不一定要從頁面載入一路活到關閉。官方 registerTool() 可以搭配 AbortSignal 管理 Tool 生命週期;可用 Tools 改變時,document.modelContext 也會有 toolchange event。

今天模擬一個最常見的狀況:Guest 只看到 login;登入後移除 login,改提供 get_profilelogout

為什麼不要永遠註冊全部 Tools?

假設你偷懶,一進頁面就註冊:

login
logout
get_profile
get_orders
delete_account

但訪客其實只能登入。

即使後端權限最後會擋住 get_orders,Agent 還是多看到一堆此刻不能用的 Tools,增加選錯與無效嘗試。

比較合理的做法是:

Guest
→ login

Member
→ get_profile
→ get_orders
→ logout

用 AbortController 管 Tool

我們先寫一個 Helper:

const toolControllers = new Map();

async function registerManagedTool(tool) {
  const controller = new AbortController();

  await document.modelContext.registerTool(tool, {
    signal: controller.signal
  });

  toolControllers.set(tool.name, controller);
}

function unregisterTool(name) {
  const controller = toolControllers.get(name);

  if (!controller) return;

  controller.abort();
  toolControllers.delete(name);
}

官方 Imperative API 目前支援把 AbortSignal 傳給 registerTool();signal 被 abort 時,Tool 就會被移除。

Guest Tool

const loginTool = {
  name: 'login',
  description: 'Open the sign-in flow when the user wants to access member-only features.',
  inputSchema: {
    type: 'object',
    properties: {}
  },
  annotations: {
    readOnlyHint: false
  },
  execute: async () => {
    openLoginDialog();
    return 'Login dialog opened.';
  }
};

載入:

await registerManagedTool(loginTool);

📸 圖片 1|未登入時只提供 login Tool
https://ithelp.ithome.com.tw/upload/images/20260918/20121296vzhNxlzKVf.png

登入後切換 Tool Set

async function onLogin(user) {
  unregisterTool('login');

  await registerManagedTool({
    name: 'get_profile',
    description: 'Get the signed-in user profile.',
    inputSchema: {
      type: 'object',
      properties: {}
    },
    annotations: {
      readOnlyHint: true
    },
    execute: async () => {
      return JSON.stringify({
        id: user.id,
        name: user.name
      });
    }
  });

  await registerManagedTool({
    name: 'logout',
    description: 'Sign out the current user.',
    inputSchema: {
      type: 'object',
      properties: {}
    },
    execute: async () => {
      await onLogout();

      const response = JSON.stringify({
        status: 'success',
        message: 'Signed out.'
      });

      window.alert('登出完成!\n目前狀態:Guest(尚未登入)\n可用 Tool:login');
      return response;
    }
  });
}

登出反過來:

async function onLogout() {
  unregisterTool('get_profile');
  unregisterTool('logout');
  await registerManagedTool(loginTool);
}

📸 圖片 2|登入後的 Member Tools
https://ithelp.ithome.com.tw/upload/images/20260918/20121296aBkupBWFTl.png

登出完成提示

在 Inspector 選擇 logout,Input Arguments 填入 {},再按 Execute Tool。頁面上的「登出」按鈕也會執行相同的登出流程。

移除會員 Tools 並重新註冊 login 後,Demo 會顯示提示視窗:

登出完成!
目前狀態:Guest(尚未登入)
可用 Tool:login

提示會停留到按下「確定」。此時登入狀態與 Tool Set 已切回 Guest;logout 的回傳則會在關閉提示後完成。本地 Demo 也會在提示出現前更新頁面上的最後一次執行結果。

📸 圖片 3|登出完成提示與 Guest 狀態
https://ithelp.ithome.com.tw/upload/images/20260918/20121296qS10CpwPPU.png

用 toolchange 觀察能力變化

document.modelContext.addEventListener('toolchange', async () => {
  const tools = await document.modelContext.getTools();

  console.log(
    'Tools changed:',
    tools.map(tool => tool.name)
  );
});

各階段完成後的預期 Tool 清單:

初始:
Tools changed: ["login"]

登入:
Tools changed: ["get_profile", "logout"]

登出:
Tools changed: ["login"]

事件發生時序不應被拿來當 Business Logic 的唯一依據。規格對 toolchange 有自己的 task timing,開發上把它當「通知」比較合理,不要靠事件先後做交易邏輯。
本地 Demo 的紀錄會分別標記瀏覽器的 toolchange 事件,以及額外讀取的初始/登入/登出完成快照。實際事件可能包含註冊或移除過程中的中間清單,不一定剛好只有上面的三行。關閉登出提示後,可查看完整事件紀錄。

📸 圖片 4|toolchange 記錄登入/登出時的 Tool List
https://ithelp.ithome.com.tw/upload/images/20260918/20121296JkY5KCyiZM.png

SPA / React / Livewire 更需要生命週期

假設一個 Product Component 每次切換商品都重新 mount:

registerTool({ name: 'add_current_product_to_cart', ... })

如果上一個 Component 沒清掉,就可能:

  • 重複名稱註冊失敗。
  • Tool 還綁著舊商品。
  • Agent 操作到已經不存在的狀態。

所以 Component mount / unmount 應和 Tool register / unregister 對齊。

這個原則同樣適用於 WordPress、Laravel Livewire:

UI State Lifecycle
≈ Tool Lifecycle

重要:不提供 Tool ≠ 權限控管

不要以為未登入時不註冊 delete_account 就安全了。

真正安全仍然要在 Server 驗證:

Authentication
Authorization
CSRF / Nonce
Business Rule

WebMCP Tool List 只是讓 Agent 少看到不該用的能力,不是後端 Access Control。

可帶走的重點

  1. Tool 應跟著目前頁面/登入狀態動態存在。
  2. AbortSignal 可以管理 Tool 註冊生命週期。
  3. toolchange 可觀察可用能力變化。
  4. Component-based Framework 要特別避免重複註冊與 stale Tool。
  5. Tool 是否存在不能取代後端 Authentication/Authorization。

參考資料


上一篇
Day 08|搜尋不到算錯誤嗎?WebMCP Result/Error 的 4 種回傳設計
下一篇
Day 10|表單不用重寫一套 Tool?Declarative WebMCP 把現有 HTML 直接給 AI 用
系列文
WebMCP:30 天打造 AI Agent 看得懂、也操作得動的網站14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言