跳到主要內容

【2026 最新】Agent-Native 教學:UI、Agent、MCP 共用 Action 三關驗收

最後更新: ·
Agent-Native 教學首圖:UI、Agent 與 MCP 共用 Action 的三關驗收

你替 App 加了一個聊天框,Agent 也能回答「幫我新增待辦」。但按鈕走 /api/tasks、聊天走另一套 tool handler、Claude Code 又接第三套 MCP server;三邊都叫「新增待辦」,驗證、權限與錯誤處理卻可能完全不同。這篇 Agent-Native 教學要處理的正是這個分岔問題。

本文專為第一次做 Agent App 的開發者寫。我們會從官方 Chat template 建一個最小待辦 action,讓 React UI、App 內 Agent、HTTP/MCP 都走同一份 Zod schema 與 run();再用輸入、越權、重複提交、交易失敗與斷線案例做三關驗收。你不必先懂 MCP protocol,但需要能閱讀少量 TypeScript。

先說結論:Agent-Native 不是聊天框外掛,而是共用操作層

Agent-Native=一個 Action,讓 UI、Agent、HTTP/MCP 共用同一條驗證、授權與執行路徑。

  • 第一關,合約一致:不論從哪個入口送資料,無效輸入都要在進入業務邏輯前被同一份 schema 擋下。
  • 第二關,權限一致:authorize 必須在每個 dispatch path 都生效;mcpTool 只決定工具是否出現在外部 Agent 目錄,不等於授權。
  • 第三關,副作用可恢復:共用 action 本身不是 idempotency 或 rollback 的驗收證據;本文用唯一鍵、transaction、錯誤狀態與重試策略明確驗證這兩項承諾。
  • 最重要的判斷:若產品只有一次性問答,聊天框可能夠用;若人與 Agent 都會修改同一份工作,共用 action layer 才開始有價值。
Agent-Native 教學三關驗收流程:UI、Agent、HTTP 與 MCP 共用 Action,依序通過 schema、authorize、idempotency 與 transaction
真正的共用不是四個入口看起來一樣,而是它們最後通過同一份不可繞過的 action contract。

Agent-Native 教學第一步:先分清 Action、Data、App State

官方把核心拆成三種共享。Action 是操作,例如新增、完成或刪除待辦;Data 是資料庫裡持久保存的待辦;Application State 則是目前頁面、選取項目與 active view 等 UI 上下文。三者不能混成一團。

把它想成餐廳:Action 是點餐流程,Data 是廚房真正收到的訂單,App State 是客人現在翻到菜單哪一頁。Agent 不需要假裝一隻手去點按鈕;它直接走與按鈕相同的點餐流程,也能讀到「客人正在看哪張單」。這比在既有網站外面包一個 chatbot,多了可共用、可測、可稽核的應用邊界。想先補 Agent 執行層的全貌,可讀 AI Agent Harness 是什麼實作 AI Agent Harness

從 Chat template 建立最小 App

截至 2026 年 9 月 23 日,官方 Getting Started 要求 Node.js 22.22 以上與 pnpm;npm 的 @agent-native/core latest 是 0.184.0。快速開始可照官方安裝文件執行:

npx --yes @agent-native/core@0.184.0 create todo-agent --standalone --template chat
cd todo-agent
corepack enable && pnpm install
pnpm dev

這裡刻意釘住版本,讓驗收結果能對應同一份框架;要追新版時,再先跑 npm view @agent-native/core version,重做下文三關。Chat template 已有 actions/hello.ts,你可以先從它理解「一個檔案就是一個 action」,再新增 actions/create-todo.ts

最小 shared action:輸入只定義一次

第一版先把 contract 寫清楚。description 給模型判斷用途,schema 同時產生 tool schema 並在 runtime 驗證輸入,run() 才碰資料庫。以下省略資料表 migration、唯一索引與 store helper;它們會在第三關補上,不能在 production 版本裡省略。

import { defineAction } from "@agent-native/core/action";
import { z } from "zod";

export default defineAction({
  description: "Create one todo for the signed-in user.",
  mcpTool: true,
  schema: z.object({
    title: z.string().trim().min(1).max(120),
    requestId: z.string().uuid(),
  }),
  run: async ({ title, requestId }, ctx) => {
    return createTodo({
      ownerEmail: requireUserEmail(ctx?.userEmail),
      title,
      requestId,
    });
  },
});

React 不需要再做一條 pass-through API route。按鈕只呼叫同名 action:

const createTodo = useActionMutation("create-todo");

createTodo.mutate({
  title,
  requestId: crypto.randomUUID(),
});

Data & Sync 文件說明,成功的 mutation 會送出 action change event,讓 useActionQuery() 重新抓取資料;callAction() 則沒有快取與自動 refetch。白話說,共用的是 server action 與持久資料,不是每個 React component 的 local state。

第一關:同一份 schema,四個入口都要擋住壞輸入

先用最便宜的負向測試攻擊 contract。對 UI、App 內 Agent、HTTP endpoint 與 MCP tool,各送三種輸入:空白 title、121 字 title、格式錯誤的 requestId。驗收標準不是錯誤文案長得一樣,而是四條路徑都不能進入 run(),資料庫筆數也不能增加

  1. UI:暫時繞過表單前端限制,直接呼叫 mutate({ title: "", requestId: "x" })
  2. Agent:要求「新增一個空白待辦」,觀察它是否先修正輸入或收到 structured failure。
  3. HTTP:/_agent-native/actions/create-todo 送相同 JSON;官方 action route 會先做 schema validation。
  4. MCP:在 tool host 直接呼叫 create-todo,使用同一組壞參數。

通過後,再各新增一筆合法待辦,重新整理頁面並從 list-todos 查詢。UI 看到 Agent/MCP 建立的資料、Agent 也讀得到 UI 建立的資料,才算 shared data 成立。若只有畫面出現 optimistic item、重新整理就消失,那只是前端幻覺。

第二關:MCP 看得到,不等於呼叫者有權執行

這是最容易被「一次定義、多處可用」口號掩蓋的地方。官方 Access & Authorization 文件agentToolmcpToolpublicAgentneedsApprovalauthorize 分開。mcpTool: true 是把 action 放進外部 Agent 的 curated catalog;它是曝光設定,不是 record-level permission。

  • authorize在 input validation 後、run() 前執行,而且 UI、HTTP、Agent、MCP、A2A、CLI 都走這個 guard。
  • needsApproval給 Agent surface 的高後果操作使用,例如寄信、扣款或刪除;它暫停特定 tool call,不代替 UI 自己的確認視窗。
  • mcpTool: false適合依賴目前螢幕 session、外部 host 無法正確使用的 action。
  • publicAgent是額外的 public protocol opt-in;公開網頁本身不會自動把 action 變公開工具。

待辦 lab 至少建立 member 與 todo-admin 兩個角色。member 能建立自己的 todo,不能替別人寫入;todo-admin 可以管理組織範圍。負向測試要用另一個帳號或 token 送出別人的 owner id,期待 403,而且 audit log 應留下 denied/error,而不是只在 UI 隱藏按鈕。

官方安全文件也把責任邊界寫得很明確:框架提供 action auth、參數化查詢與 SQL scoping,但 raw application query 仍要用 access helper;目前文件明示 RLS 尚未啟用。這表示「Agent 只能看到自己的資料」不能靠 prompt 保證,table 的 owner_emailorg_id 與 query guard 才是門鎖。可搭配 AI Agent 密鑰安全教學一起檢查 token 與 secret 的可見範圍。

把 Claude Code 接成 MCP host

先讓 App 保持 pnpm dev,再依官方 External Agents 流程把本機 App 寫入 Claude Code 的 MCP 設定。把下方 <APP_URL> 換成 pnpm dev 印出的網址(預設從 8080 開始,若被占用會遞增):

npx @agent-native/core@0.184.0 connect <APP_URL> --client claude-code --scope project

重啟 Claude Code,執行 /mcp,從 MCP UI 完成驗證並確認工具目錄,再呼叫 create-todo。若 tool 不在初始目錄,不要立刻判定 auth 壞掉:Agent-Native 預設送 compact catalog,其餘 action 可由 tool-search 找到;要進 curated catalog 才明確寫 mcpTool: true。正式環境還要使用具身份的 OAuth/token,不能拿匿名 local probe 推導 production 權限已通過。

第三關:重複提交、切頁與斷線,驗的是副作用

官方 client 文件明示 action request 遇到 timeout 會回報錯誤,而不是靜默重試;但使用者可能雙擊、proxy 可能在 response 遺失後重送、Agent 或外部 MCP client 也可能再次呼叫。解法不是期待每個入口都「小心一點」,而是讓 action 對同一個 business request 有穩定身份。

在 todos table 加上 request_id,並建立 (owner_email, request_id) unique index。createTodo() 必須在 transaction 中先查既有結果、再插入;若兩個相同 request 同時競爭,由 unique constraint 決勝,失敗的一方重新讀回同一筆。這叫 idempotency:同一張訂單按兩次,結果仍是一張訂單。

return db.transaction(async (tx) => {
  const existing = await findByRequestId(tx, ownerEmail, requestId);
  if (existing) return existing;

  return insertTodo(tx, { ownerEmail, title, requestId });
});

這個骨架還需要處理 concurrent unique-conflict:捕捉衝突後,以同一個 ownerEmail+requestId 重新查詢,不能把任意 database error 都吞掉。多表寫入則放進同一個 transaction;刻意在第二個 write 前 throw,驗收第一個 write 也沒有留下。Audit log 能告訴你呼叫從 frontend、tool、HTTP 或 MCP 進來以及結果,但 audit trail 本身不是 undo。

一條完整 trace:從雙擊到斷線恢復

  1. UI 產生 requestId=A,使用者快速按兩次;兩個 request 最終都回同一個 todo id,資料庫只有一列。
  2. Agent 用另一個 requestId=B 新增待辦;UI 的 query 在 mutation change event 後 refetch,畫面出現同一筆資料。
  3. MCP 以無權角色呼叫,authorize 回 403;資料庫沒有新增,audit trail 能分辨 caller 是 mcp。
  4. action 在 transaction 中完成第一個 write、第二個 write 前故意 throw;重新查詢時兩個 write 都不存在。
  5. response 回傳前斷線,再送相同 requestId=A;action 回原本 todo,而不是新增第二筆。
  6. 切換頁面再回來,domain data 仍在;navigation/selection 可重建,但不要把短暫 App State 當成待辦資料庫。

如果你還沒有 eval 心智模型,可先讀 AI Evals 入門;如果準備把 MCP 放到 production,再接著做 MCP 上線前驗收的 token、timeout、撤銷與負向測試。

和傳統 chatbot wrapper 做 A/B:差別不在回答像不像人

做一個對照版:保留原本 UI API,再替聊天另外寫 tool handler。兩邊都完成「新增待辦」,然後改一次 title 長度、角色規則或錯誤碼。你要觀察的不是模型文筆,而是每次 policy change 要改幾個地方、哪條路徑最容易漂移。

  • chatbot wrapper:容易快速 demo;但 UI API、tool schema、MCP adapter 與 audit 邏輯若分開,contract drift 會隨功能數增加。
  • Agent-Native:Action contract 集中,UI 與 Agent 都能吃同一份能力;代價是你必須把 action surface、tenant scope、transaction 與工具曝光當成正式 API 設計。
  • 共同限制:模型仍可能選錯工具、漏做步驟或提出壞參數;shared action 降低的是執行邊界漂移,不是把模型變成確定性程式。

截至 2026 年 9 月 23 日,GitHub API 顯示 repository 約 6,315 stars;這是熱度快照,不是可靠性證明。官方 issue tracker 在 9 月 22 日仍有一則custom provider connect-time SSRF/DNS rebinding gap 的開放回報,同日也有一則scoped team roles feature request。它們代表真實 adoption 邊界正在被碰到,但單一 issue 不能外推成整個框架所有路徑都失效。

Agent-Native 教學的 8 題失敗注入清單

  1. Schema:空字串、超長字串、未知欄位與錯誤 UUID。
  2. Authentication:無 session、過期 token、錯 audience token。
  3. Authorization:member 存取別人的 todo,期待 403 與零資料變化。
  4. Exposure:mcpTool: false 的 action 不該出現在外部工具目錄。
  5. Duplicate:同一 requestId 並發兩次,只產生一筆。
  6. Transaction:第二個 write 故障,第一個 write 一起 rollback。
  7. Disconnect:server 已 commit、client 沒收到 response,再送相同 requestId。
  8. Navigation:切頁、開第二個 tab、回到原頁,資料與 selection 各自符合 domain data/App State 的契約。

把這八題寫成固定 test,比「我在聊天框試過一次」更有價值。你真正驗收的是入口共用後,失敗也是否共用同一個規則。

怎麼留下能比較的驗收收據?

每一次 call 至少記六個欄位:surfaceactorrequestId、HTTP/tool 結果、最終 todo id、資料庫列數。再把 framework 版本、Git commit、測試帳號角色與預期結果一起釘住。這份收據不放 token,也不複製完整 session;它只保存足以回答「哪個入口、哪個身份、哪個請求,造成哪個資料結果」的資訊。

通過標準要寫成可以判定的句子:合法輸入四個入口各成功一次;非法輸入四個入口都零寫入;同 requestId 的兩次成功回應具有相同 todo id;越權呼叫得到 403 且零寫入;transaction 中途失敗後相關資料列皆不存在。不要只寫「看起來同步」「Claude 有成功」或「沒有報錯」,因為它們都無法分辨 UI cache、資料庫 commit 與 Agent 回覆是不是同一件事。

若要做 A/B,固定同一組輸入、帳號、資料庫 seed 與錯誤注入,只替換架構:A 組讓 UI route 與 Agent tool 分開,B 組共用 action。比較新增一條政策時要改的檔案數、負向測試通過率、重複寫入數與無法追到 caller 的事件數。這些指標直接回答維護成本,不需要用模型主觀評分替架構背書。

常見問題 FAQ

1. Agent-Native 就是替 App 加聊天框嗎?

不是。聊天只是入口之一;核心是 UI、Agent、HTTP、MCP 與 CLI 共用 action registry、schema、access check 與 implementation。

2. 寫了 Zod schema 就安全了嗎?

不夠。Schema 驗資料形狀;誰能操作哪筆資料,要靠 authentication、authorize、access helper 與 tenant-scoped query。

3. mcpTool: true 會把 action 公開給所有人嗎?

不等於公開。它宣告 external-agent catalog membership;外部呼叫仍要通過 OAuth/token、external-agent policy 與 action access guard。Public protocol exposure 另由 publicAgent 控制。

4. Shared action 會自動防止重複建立嗎?

不要這樣假設。把 requestId、unique constraint 與 conflict recovery 寫進業務層,才能讓 UI、Agent、HTTP、MCP 一起得到相同的 idempotency。

5. Audit log 可以拿來 rollback 嗎?

不是同一件事。Audit log 留下誰從哪個 surface 做了什麼;transaction 保證同一次多步寫入的原子性;真正的 undo/version restore 還要設計 snapshot 或 inverse operation。

6. App State 能拿來存正式待辦嗎?

不建議混用。Application State 適合 navigation、selection、focused object 等上下文;正式待辦應進有 schema、ownership 與 migration 的 domain table。

7. MCP 呼叫會再跑一次 LLM 嗎?

直接 tool call 不需要。MCP host 已選定 action 時,呼叫同一個 server action;只有使用 ask-agent 這類 meta-tool,才把任務交給完整 Agent loop。

8. 什麼情況先不要導入?

只有一次性問答、沒有共享 domain data、也沒有 Agent 寫入需求時,先別急。先用現有 API 與小型 tool adapter 驗證需求;當同一能力開始在 UI、Agent、integration 間重複實作,再評估把 operation 收斂成 action。

給新手的 7 個重點

  1. 先記住:一個 Action,多個入口。
  2. Domain Data 與 Application State 分開設計。
  3. Zod schema 驗形狀,authorize 驗權限。
  4. Catalog membership 不是 permission。
  5. 副作用用 requestId+unique constraint 防重。
  6. 多步寫入用 transaction;audit 不等於 undo。
  7. 每新增一個 surface,就重跑壞輸入、越權、重複與斷線測試。

接著閱讀

左右滑動查看更多推薦

結語:先做三關,再決定它是不是你的 App 架構

Agent-Native 最值得帶走的不是「所有 App 都要有 Agent」,而是把 Agent 從 UI 外掛提升成正式 caller:它和按鈕、HTTP、MCP 一樣,都只能走同一個可驗證的 action seam。今天就挑一個低風險寫入,例如新增待辦,依序跑完 schema、authorization、idempotency/transaction 三關;只要任何一關在某個入口失效,就先修 contract,不要繼續堆功能。

若你想把這套方法延伸成完整的 Agent 開發路線,可以從 AlphaLab AI 專區繼續學習,或直接查看 AI 實戰課程,把共用 action、MCP 與 eval 串成可維護的產品工程。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

每週最多三封:一封 Weekly 週報與最多兩封關鍵 Alpha Signal。

我們不會 spam,隨時可退訂。