你替 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 教學第一步:先分清 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(),資料庫筆數也不能增加。
- UI:暫時繞過表單前端限制,直接呼叫
mutate({ title: "", requestId: "x" })。 - Agent:要求「新增一個空白待辦」,觀察它是否先修正輸入或收到 structured failure。
- HTTP:向
/_agent-native/actions/create-todo送相同 JSON;官方 action route 會先做 schema validation。 - MCP:在 tool host 直接呼叫
create-todo,使用同一組壞參數。
通過後,再各新增一筆合法待辦,重新整理頁面並從 list-todos 查詢。UI 看到 Agent/MCP 建立的資料、Agent 也讀得到 UI 建立的資料,才算 shared data 成立。若只有畫面出現 optimistic item、重新整理就消失,那只是前端幻覺。
第二關:MCP 看得到,不等於呼叫者有權執行
這是最容易被「一次定義、多處可用」口號掩蓋的地方。官方 Access & Authorization 文件把 agentTool、mcpTool、publicAgent、needsApproval 與 authorize 分開。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_email/org_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:從雙擊到斷線恢復
- UI 產生
requestId=A,使用者快速按兩次;兩個 request 最終都回同一個 todo id,資料庫只有一列。 - Agent 用另一個
requestId=B新增待辦;UI 的 query 在 mutation change event 後 refetch,畫面出現同一筆資料。 - MCP 以無權角色呼叫,
authorize回 403;資料庫沒有新增,audit trail 能分辨 caller 是 mcp。 - action 在 transaction 中完成第一個 write、第二個 write 前故意 throw;重新查詢時兩個 write 都不存在。
- response 回傳前斷線,再送相同
requestId=A;action 回原本 todo,而不是新增第二筆。 - 切換頁面再回來,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 題失敗注入清單
- Schema:空字串、超長字串、未知欄位與錯誤 UUID。
- Authentication:無 session、過期 token、錯 audience token。
- Authorization:member 存取別人的 todo,期待 403 與零資料變化。
- Exposure:
mcpTool: false的 action 不該出現在外部工具目錄。 - Duplicate:同一 requestId 並發兩次,只產生一筆。
- Transaction:第二個 write 故障,第一個 write 一起 rollback。
- Disconnect:server 已 commit、client 沒收到 response,再送相同 requestId。
- Navigation:切頁、開第二個 tab、回到原頁,資料與 selection 各自符合 domain data/App State 的契約。
把這八題寫成固定 test,比「我在聊天框試過一次」更有價值。你真正驗收的是入口共用後,失敗也是否共用同一個規則。
怎麼留下能比較的驗收收據?
每一次 call 至少記六個欄位:surface、actor、requestId、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 個重點
- 先記住:一個 Action,多個入口。
- Domain Data 與 Application State 分開設計。
- Zod schema 驗形狀,
authorize驗權限。 - Catalog membership 不是 permission。
- 副作用用 requestId+unique constraint 防重。
- 多步寫入用 transaction;audit 不等於 undo。
- 每新增一個 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 串成可維護的產品工程。
