App Intents 與 MCP 之爭:路由問題
兩種協定,App Intents 與 MCP,都讓外部代理操作 App 的領域功能。它們無法合併為一。問題在於誰該走哪條路、為什麼每種協定都是其各自呼叫者的正確答案。
Apple 推出 App Intents,是為了讓 Apple Intelligence 擁有一個型別化、宣告式的介面,能在不觸碰第三方 App UI 的情況下操作它們。1 Anthropic 推出 Model Context Protocol,則是為了讓任何 LLM 擁有一個型別化、伺服器中介的介面,能在不觸碰任何工具 UI 的情況下操作該工具。2 兩者形狀相似,呼叫者卻不同。把它們當成同一個介面來看待,會產出一個兩邊都無法滿足的架構。
本系列前兩篇文章分別涵蓋了各自的協定:App Intents 在 App Intents 是 Apple 通往你 App 的全新 API,MCP 在 兩個代理生態系,一份購物清單。本篇則處理路由問題。某項能力何時該成為 AppIntent、何時該成為 MCP 工具、何時兩者皆需,又有哪些該繼續只留在 App 內部?
TL;DR
- App Intents 是通往 Apple Intelligence、Siri、Shortcuts 與系統建議堆疊的唯一路徑。系統會在 App 安裝時與更新時讓其可用,並透過 App Shortcuts 的 donation 與索引機制將其浮現至 Spotlight 與 Siri 建議中。
- MCP 工具則是通往所有非 Apple LLM(Claude、ChatGPT、Gemini、本地模型)的路徑。傳輸方式為 stdio 或 Streamable HTTP,
.mcpb是常見打包格式,通常內含本地 stdio 伺服器;主機會於 session 開始時載入工具。 - 兩種協定都收斂至型別化結構描述、
entity → action → result的形狀以及參數解析。但在身分、持久性、延遲與呈現介面上分歧。 - 路由規則:若該能力是使用者可能會問 Siri 或從 Spotlight 召喚的,走 App Intents。若該能力是開發者可能會接入 Claude Code session 或外部代理執行流程的,走 MCP。大多數 App 在同一領域中兩者皆需。
兩種協定,相同形狀
兩種協定都定義了外部呼叫者與 App 領域之間的操作合約。合約有三個部分:結構描述(呼叫者能要求什麼)、解析器(App 如何找到結構描述所指的實體)以及動作(執行什麼以及回傳什麼)。
App Intents 以 Swift 表達合約。協定介面為 AppIntent、AppEntity、AppEnum,搭配 @Parameter 巨集驅動結構描述,並以 func perform() 回傳結果。3 結構描述於編譯期生成,並在安裝時打包進 App。Apple Intelligence、Siri、Shortcuts 與 Spotlight 全都讀取相同的結構描述,並透過同一個 perform() 入口點路由型別化的請求。
MCP 則以 JSON-RPC 透過 stdio 或 Streamable HTTP 表達合約。協定介面為 tools/list 與 tools/call 方法,每個工具會宣告名稱、描述與 inputSchema(2025-06-18 規格新增可選的 outputSchema 用於結構化回傳)。4 一個 MCP 主機(Claude Desktop、Claude Code、Cursor、ChatGPT 桌面 App)於 session 開始時探索工具,並以 JSON 酬載依名稱呼叫。主機執行模型;伺服器執行工具。
形狀相同:結構描述、解析器、動作。差異在於誰執行哪個部分,以及信任邊界落在哪裡。App Intents 在 App 行程內、使用者裝置上、以 App 的權限執行,由系統中介呼叫路由。MCP 伺服器則在開發者選擇的任何位置執行(本地 stdio、託管 HTTP、嵌入式套件),由主機 LLM 中介呼叫路由,跨越無界的工具集合。
兩種協定的分歧之處
除了表面形狀之外,有四個操作面的差異會影響路由決策:
身分與持久性。 App Intents 以 AppEntity 型別溝通,系統可以儲存、呈現、稍後重新解析這些型別。我今天透過嘿 Siri,在 Water 中記錄 250ml 儲存的飲水紀錄,能跨重開機保留、跨使用者的 iCloud 裝置同步,並能被其他 intents 在之後參照(顯示我昨天的飲水紀錄)。系統會跨所有這些呼叫追蹤實體 ID。3 MCP 本身是具備生命週期管理的有狀態協定,且 Streamable HTTP 透過 session ID 支援連線連續性,但耐久的領域身分屬於伺服器自有的關注點;協定層級沒有與 AppEntity 識別碼類似、可讓主機模型跨 session 依靠的等價物。MCP 透過 resources 支援持久參考資料,但身分仍是伺服器端責任,而非一級的協定合約。4
延遲與電力。 App Intent 的 perform() 主體在裝置上的 App 或 App extension 環境中執行。任何網路使用都來自 App 自身的程式碼或周圍的 Apple Intelligence/Siri 層,而非來自 intent 合約本身。一個型別化的裝置端動作回傳型別化結果,在常見情況下速度很快。MCP 工具即使是本地的,也會經過 stdio JSON-RPC 框架與獨立行程邊界,遠端 MCP 工具則需 HTTP 來回。延遲預算不同。一個記錄 250ml 的 App Intent 能在 Siri 對話輪換的時間視窗內完成。一個遠端 MCP 工具則可能成為 Claude Code session 的瓶頸。
呈現介面。 App Intents 回傳的結果會由 Apple Intelligence 渲染至系統 UI 中:鎖定畫面橫幅、Siri 回應、Shortcuts 輸出、Spotlight 結果。App 並不控制結果如何呈現。MCP 工具則回傳內容區塊(文字、影像、音訊、嵌入資源或結構化內容),由主機模型讀取並決定如何呈現。Claude Code session 可能會將結果引述回給開發者、做摘要,或將其饋入後續呼叫。呈現決策位於模型層。
可發現性。 Apple Intelligence 自安裝起即提供 App Intents,並透過 App Shortcuts 的 donation 與索引機制,依使用者行為將 intents 浮現至 Spotlight 搜尋與 Siri 建議;App 更新與動態實體也會隨時間調整介面。使用者從不需要輸入工具名稱。MCP 主機則在 session 開始時讀取工具;由使用者(或系統提示詞)決定模型能看到哪些工具。在 MCP 端,發現是明確的設定;在 App Intents 端,則是隱式的系統推論。
兩種協定在身分、延遲、呈現與發現四個屬性上分歧,這四個屬性源自一個根本區別。App Intents 服務於使用者並未設定的系統層級代理。MCP 服務於開發者所配置的 session 層級代理。不同的呼叫者,不同的義務。
路由規則
具備兩種協定的 App,其能力地圖看起來像這樣:
┌──────────────────────────────────────────┐
│ App's domain capabilities │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ CRUD │ │ Queries │ │ Actions │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└────┬────────────┬────────────┬────────────┘
│ │ │
┌──────────┴──────┐ │ ┌────────┴──────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ App Intents │ │ Both (AppIntent + │ │ MCP tools │
│ only │ │ MCP tool wrapper) │ │ only │
└────────────┘ └─────────────────────┘ └──────────────┘
│ │ │
Siri / Spotlight Cross-protocol Claude Code,
Shortcuts capabilities external agents,
Apple Intelligence where both callers LLM tooling,
proactive surfaces should reach the dev workflows
same domain
路由規則由三個依序回答的問題組成。
該能力是否為使用者會問 Siri 或從 Shortcuts 啟動的東西? 若是,該能力需要 App Intent。記錄 250ml 飲水、開始冥想、把香蕉加入清單、我昨天體重多少 都是 intents,因為使用者可能會說出口、輸入到 Spotlight,或在 Shortcuts 中串接它們。對這些能力來說,App Intent 是必須的;沒有其他方式能讓你進入 Apple Intelligence 的第一方代理介面。
該能力是否應該能由外部代理驅動? 若是,該能力需要 MCP 工具。從 Claude Code session 將項目加入購物清單、將 Get Bananas 狀態讀入 Cursor 代理上下文、從遠端使用工具的 LLM 觸發工作流程 都是 MCP 工具,因為呼叫者並非 Apple Intelligence;呼叫者是開發者所連接的任何 LLM。MCP 工具可以包裝與 App Intent 呼叫相同的領域層 Swift 函式,但協定介面是 JSON-RPC 透過開發者所選的傳輸方式。
該能力是否需要在單一 session 之外延續,並具備系統已知的穩定身分? 若是,App Intent 路徑是自然的選擇,因為系統免費提供 AppEntity 身分、查詢支援與持久化語意。若否,MCP 工具可以回傳內容區塊,將耐久身分交由伺服器自行決定,並省去實體建模的成本。
大多數非平凡的 App 能力都落在兩者皆需的欄位中。Water 中的飲水記錄能力同時擁有 AppIntent(讓 Siri 可接受口述)與 MCP 工具(讓 Claude Code session 能從匯出紀錄回填)。兩條路徑共用一個 Swift 函式;該函式並不知道是哪個呼叫者啟動了它。5
實際程式碼形狀則為:一個領域方法,加上兩個都呼叫它的轉接層包裝:
// Domain layer (Swift, no protocol assumptions)
func logWater(amount: Measurement<UnitVolume>, at: Date, caller: Caller) throws -> WaterEntry {
try guards.requireWritePermission(caller)
let entry = WaterEntry(amount: amount, timestamp: at)
try store.insert(entry)
return entry
}
// Adapter 1: App Intent (Apple Intelligence / Siri / Shortcuts)
struct LogWaterIntent: AppIntent {
static var title: LocalizedStringResource = "Log Water"
@Parameter(title: "Amount") var amount: Measurement<UnitVolume>
func perform() async throws -> some IntentResult & ReturnsValue<WaterEntry> {
let entry = try domain.logWater(amount: amount, at: .now, caller: .siri)
return .result(value: entry)
}
}
// Adapter 2: MCP tool (Claude Desktop / Code / external agent)
// Tool name "log_water" with inputSchema {amount_ml: number}
// Handler:
let entry = try domain.logWater(
amount: .init(value: ml, unit: .milliliters),
at: .now,
caller: .mcp(host: hostName)
)
return .text("Logged \(entry.amount) at \(entry.timestamp)")
兩個轉接層看起來不同,是因為它們的呼叫者不同。但它們所呼叫的函式是同一個。
哪些該繼續留在 App 內部
有一組數量不多但很重要的能力應保持為 App 私有。把它們路由至任一協定都是錯誤。
UI 狀態類能力。 「開啟第三個分頁」、「捲到底部」、「強調這一列」並非領域操作,而是互動原語。App Intents 透過 OpensIntent 與 Shortcuts 對此有部分支援,但類型適配很差;使用者通常想要的是結果,而非導航。MCP 對 UI 導航的支援甚至更差:模型不是在驅動畫面,它是在驅動工具。
需要使用者本人在迴圈中的能力。 拍照、生物辨識認證、敏感 PII 輸入,以及任何需要使用者注視螢幕並輕觸的流程。Apple 的 CameraCaptureIntent 為相機流程而存在,但其設計用意是啟動前景擷取活動,而非授予代理背景相機存取權限。對兩種協定都成立的誠實規則是:相機、生物辨識與敏感輸入流程應作為前景 UI 執行並要求明確的使用者確認,而不是作為靜默的 intent 或工具呼叫。將這些能力留在 App 的 UI 後方,讓代理把使用者導向那個畫面,而非穿越畫面。
長時間執行的背景工作。 App Intents 包含 ProgressReportingIntent 用於浮現進度,MCP 也包含進度通知與任務原語,但兩種協定的呈現層皆未為消費型流程上持續的進度而設計。MCP session 中的主機模型,通常會在數分鐘的工具完成之前就讓推理鏈條逾時。對於消費者面向的長時間工作,請透過 App 自己的 UI 暴露該操作;讓協定回傳請求已排入佇列並連結至狀態畫面。
任何觸及他人資料的事情。 兩種協定的信任邊界都是呼叫端代理。Apple Intelligence 在使用者的 iCloud 帳戶下執行。MCP 在開發者所連接的任何憑證下執行。跨使用者的操作(分享、多帳戶存取、管理員動作)透過任一協定都不安全,因為呼叫端身分是錯的身分。
我會以不同方式構建什麼
了解上述路由規則後,我設計 Swift App 領域層的方式,會像我現在設計服務邊界上 API 的方式。領域方法接受型別化輸入並回傳型別化輸出,不烘焙任何協定假設。App Intents 以 @Parameter 結構描述與 perform() 黏合層薄薄包裝領域方法。MCP 工具以 JSON 結構描述與 stdio 框架薄薄包裝相同的領域方法。兩種協定都是薄轉接層;工作在領域中。
由此衍生兩個結果。
呼叫端身分是領域關注點,不是協定關注點。 App Intent 主體接收系統解析過的參數,並在使用者已經走完系統的 intent 啟動流程後執行。MCP 工具主體則接收主機所安排的任何憑證。兩者皆透過明確的 caller 引數傳遞至領域方法。領域方法強制執行授權、確認提示,以及任何其他領域不變式。沒有哪個協定可以假裝呼叫者就是使用者。
兩個轉接層浮現相同的功能集合。 哪些能力要暴露給哪個呼叫端的決定,被捕捉在兩個 manifest 中,而不是散落在協定程式碼裡。新增一項能力需要:一個領域方法、兩個轉接層包裝、兩個 manifest 條目。移除一項能力是對稱的。上方的矩陣會變成一個真實的檔案。
Apple 平台未來幾年的前線並不是挑選一種協定。前線是把兩者當成在同一領域層上組合的正交合約。Apple Intelligence 代理對使用者有一組義務(在裝置上執行、會說 Siri、透過系統呈現)。外部 LLM 代理對開發者有另一組義務(在任何地方執行、會說 JSON-RPC、透過開發者所選的任何模型呈現)。兩者都應擁有通往你 App 的型別化介面。但都不應該是唯一的介面。
何時不該兩者都建
論點有兩面。某些 App 只需要其中一種協定。
沒有開發者介面的純消費者工具。 一支手電筒。一個鳥鳴辨識器。一支擴增實境捲尺。使用者可能想透過 Siri 啟動它(App Intents 有用),但沒有開發者會把它接入 LLM 工作流程(MCP 會是裝飾用)。
沒有終端使用者介面的純開發者工具。 一個 code-formatter MCP 伺服器。一個倉庫搜尋工具。一個套件版本檢查器。使用者就是在 Claude Code session 中的開發者;Siri 與 Apple Intelligence 沒有戲份。
兩種代理類別都服務不好的 App。 高度互動的遊戲、即時多人 App、價值就在於停留在 App 內並專注螢幕的 App。兩種協定都不適合;正確答案是一個出色的 App,沒有代理合約。
決策不是預設一者或兩者皆建。決策是這個 App 是為了什麼,還有誰會想操作它的領域。答案可能是兩者皆無、其中一者,或兩者皆建。如果領域層形狀良好,建一者的成本很小。在那個領域層之上再建另一者,成本也很小。當使用案例需要某一者卻不建時,成本則是該能力在那個代理介面上完全缺席。
這套模式對 iOS 26+ Apple 堆疊的意義
兩個重點。
-
將 App Intents 與 MCP 視為同一領域上的正交合約,而非競爭協定。 Apple Intelligence、Siri、Shortcuts 與 Spotlight 是一類具備系統層級義務的呼叫端。Claude、Cursor、ChatGPT 等則是具備 session 層級義務的第二類呼叫端。兩者都應擁有型別化存取。它們之下的領域層並不會改變。
-
路由規則是誰呼叫,不是執行什麼。 App Intent 與 MCP 工具可以呼叫相同的 Swift 函式。它們在呼叫端所承擔的義務、所獲得的呈現方式以及所期望的持久性上有所不同。把函式做對;讓協定層保持薄。
完整的 Apple 生態系列:用於 Apple Intelligence 的型別化 App Intents、用於跨 LLM 代理的 MCP 伺服器、用於鎖定畫面狀態機的 Live Activities、用於視覺層的 Liquid Glass 模式,以及用於跨裝置觸及的 多平台出貨。Hub 在 Apple 生態系列。更廣泛的 iOS 結合 AI 代理脈絡,請見 iOS Agent Development 指南。
FAQ
對於同一項能力,何時該建 App Intent,何時該建 MCP 工具?
當該能力應觸及 Apple Intelligence、Siri、Shortcuts 或 Spotlight 時,建 App Intent。當該能力應觸及外部 LLM(Claude、ChatGPT、Claude Code 或 Cursor 中的代理)時,建 MCP 工具。對於應同時服務兩類呼叫端的領域能力,請以共用的 Swift 領域方法為基礎,將兩者建為薄轉接層。
App Intents 與 MCP 伺服器是否互相競爭?
不會。App Intents 是通往 Apple 第一方代理堆疊的路徑;MCP 是通往所有其他 LLM 的路徑。Apple Intelligence 不會呼叫 MCP 工具,外部 LLM 代理也無法直接啟動 App Intents(它們會透過系統)。兩種協定服務不同類別的呼叫端,具備不同的信任模型、不同的延遲預算與不同的呈現介面。
單一 App 能否透過兩種協定暴露其領域?
可以,且大多數想要完整代理觸及的非平凡 App 都應該如此做。Get Bananas(在 MCP 伺服器一文中介紹)與 Water(在 App Intents 一文中介紹)是早期範例。模式是:底下一個領域層,上面是 App Intent 轉接層與 MCP 工具轉接層。兩個轉接層呼叫相同的 Swift 函式。
Apple Intelligence 追蹤了哪些 MCP 沒有的狀態?
Apple Intelligence 跨呼叫、session 與重開機追蹤 AppEntity 身分。實體模型給予系統使用者可在 intents 之間串接的持久參照。MCP 本身是具備生命週期管理的有狀態協定,並在 Streamable HTTP 中支援 session ID,但耐久的領域身分是伺服器端責任,而非一級的協定合約;主機模型無法從協定介面取得與 AppEntity 等價的識別碼。MCP 的 resources 概念支援持久參考資料,但運作於同樣由伺服器擁有的層級。
是否有些能力不該透過任一協定暴露?
有。UI 狀態類能力(開啟此分頁、捲動到此處)、需要使用者本人在迴圈中的能力(相機擷取、生物辨識認證、敏感輸入)、長時間執行的背景工作,以及跨多位使用者資料的操作,都應留在 App 的 UI 後方。兩種協定對這些都只有薄弱的原語,且都不具備跨使用者安全運作所需的信任訊號。
參考資料
-
Apple Developer,「App Intents framework」。用於宣告 intents、entities、parameters 與 queries 的介面,可由 Apple Intelligence、Siri、Shortcuts 與 Spotlight 路由。 ↩
-
Anthropic,「Model Context Protocol」。用於跨 LLM 主機進行型別化工具暴露的開放協定。傳輸方式為 stdio 或 Streamable HTTP;
.mcpb是常見打包格式,通常內含本地 stdio 伺服器。規格涵蓋tools/list、tools/call、resources與 prompts。 ↩ -
Apple Developer,「Creating your first app intent」 與 「AppEntity」。
AppIntent協定、@Parameter巨集、func perform()入口點,以及用於持久身分的AppEntity。 ↩↩ -
Anthropic,「MCP Specification: Tools (2025-06-18)」、「MCP Architecture」 與 「Transports (2025-06-18)」。
tools/list與tools/call的 JSON-RPC 方法定義、inputSchema與可選的outputSchema、主機責任、生命週期管理,以及 stdio / Streamable HTTP 傳輸方式。 ↩↩ -
作者於 App Intents 是 Apple 通往你 App 的全新 API 與 兩個代理生態系,一份購物清單 中的分析。雙轉接層模式(一個 Swift 領域方法,兩個協定包裝)在兩篇文章中分別針對 Water 與 Get Bananas 在實作層級進行描述。 ↩