App Intents vs MCP: The Routing Question
Two protocols, App Intents and MCP, both let an outside agent operate an app’s domain. They do not collapse into one. The question is which goes where, and why each protocol is the right answer for its own caller.
Apple shipped App Intents to give Apple Intelligence a typed, declarative surface to operate third-party apps without touching their UI.1 Anthropic shipped Model Context Protocol to give any LLM a typed, server-mediated surface to operate any tool without touching its UI.2 The shapes are similar. The callers are not. Treating them as one surface produces an architecture that satisfies neither.
The two prior posts in this cluster covered each protocol on its own: App Intents in App Intents Are Apple’s New API to Your App, MCP in Two Agent Ecosystems, One Shopping List. The post at hand is the routing question. When does a capability get an AppIntent, when does it get an MCP tool, when does it get both, and what stays exposed only inside the app?
TL;DR
- App Intents are the only path to Apple Intelligence, Siri, Shortcuts, and the system suggestion stack. The system makes them available from installation and through app updates, with App Shortcuts donation and indexing surfacing them into Spotlight and Siri suggestions.
- MCP tools are the path to every non-Apple LLM (Claude, ChatGPT, Gemini, local models). Transport is stdio or Streamable HTTP, with
.mcpbas a packaging format that commonly ships a local stdio server; the host loads tools at session time. The current spec revision is 2025-11-25, with a 2026-07-28 release candidate reworking the protocol around a stateless core67. - Both protocols converge on a typed schema, an
entity → action → resultshape, and parameter resolution. They diverge on identity, persistence, latency, and the rendering surface. - The routing rule: if the capability is something a user might ask Siri or summon from Spotlight, App Intents. If the capability is something a developer might wire into a Claude Code session or an external agent run, MCP. Most apps need both for the same domain.
Two Protocols, Same Shape
Both protocols define an operation contract between an outside caller and an app’s domain. The contract has three parts: the schema (what the caller can ask for), the resolver (how the app finds the entities the schema names), and the action (what runs and what comes back).
App Intents express the contract in Swift. The protocol surface is AppIntent, AppEntity, AppEnum, with @Parameter macros driving the schema and func perform() returning the result.3 The schema is generated at compile time and bundled into the app at install. Apple Intelligence, Siri, Shortcuts, and Spotlight all read the same schema and route a typed request through the same perform() entry point.
MCP expresses the contract in JSON-RPC over stdio or Streamable HTTP. The protocol surface is the tools/list and tools/call methods, with each tool declaring a name, a description, and an inputSchema (the 2025-06-18 spec added optional outputSchema for structured returns, and the 2025-11-25 revision establishes JSON Schema 2020-12 as the default dialect, adds tool icons as metadata, and formalizes governance beyond Anthropic).46 An MCP host (Claude Desktop, Claude Code, Cursor, the ChatGPT desktop app) discovers tools at session start and calls them by name with a JSON payload. The host runs the model; the server runs the tool. The protocol is also mid-transition: the 2026-07-28 release candidate rebuilds MCP around a stateless core that runs on ordinary HTTP infrastructure, moves long-running work into a Tasks extension, and adds server-rendered UI through MCP Apps7. The routing argument below survives that revision intact, because none of it changes who the caller is.
The shape is the same: schema, resolver, action. The difference is who runs each piece and where the trust boundary lives. App Intents run inside the app process, on the user’s device, under the app’s entitlements, with the system mediating call routing. MCP servers run wherever the developer chose to run them (local stdio, hosted HTTP, embedded bundle), with the host LLM mediating call routing across an unbounded set of tools.
A third caller with the same shape is worth naming because it completes the picture: the on-device model behind Apple’s Foundation Models framework calls app code through its Tool protocol, a named, described, typed capability like the other two. A well-shaped domain layer feeds all three surfaces without knowing which one is calling.
Where The Two Protocols Disagree
Beyond the surface shape, four operational differences matter for routing:
Identity and persistence. App Intents speak in AppEntity types that the system can store, present, and re-resolve later. The water entry I save today via Hey Siri, log 250ml in Water survives across reboots, syncs across the user’s iCloud devices, and can be referenced by other intents later (Show me yesterday’s water entries). The system tracks the entity ID across all those calls,3 and iOS 27 extends the story across hardware: an entity conforming to SyncableEntity carries an identifier consistent across a user’s devices, so Siri can hand a conversation about the same object from iPhone to Mac to Watch.8 MCP is itself a stateful protocol with lifecycle management, and Streamable HTTP supports session IDs for connection continuity, but durable domain identity is a server-owned concern; there is no protocol-level analog to AppEntity identifiers that a host model can rely on across sessions. MCP supports resources for persistent reference data, and the 2025-11-25 spec adds experimental tasks for tracking durable requests, but identity for domain objects remains a server-side responsibility rather than a first-class protocol contract.46 The underlying discipline is the same one that applies inside the app’s own data layer: stable identifiers that survive the process, covered for SwiftData in schema discipline.
Latency and battery. The App Intent’s perform() body executes in the app or app extension context on the device. Any network use comes from the app’s own code or from the surrounding Apple Intelligence/Siri layer, not from the intent contract itself. A typed, on-device action returning a typed result is fast in the common case. MCP tools, even local ones, go through stdio JSON-RPC framing with a separate process boundary, and remote MCP tools incur HTTP round trips. The latency budget is different. A log 250ml App Intent can complete inside a Siri turn-taking window. A remote MCP tool might be the bottleneck of a Claude Code session.
Rendering surface. App Intents return results that Apple Intelligence renders into the system UI: a Lock Screen banner, a Siri response, a Shortcuts output, a Spotlight result. The app does not control how the result presents. MCP tools return content blocks (text, images, audio, embedded resources, or structured content) that the host model reads and decides how to surface. A Claude Code session might quote the result back to the developer, summarize it, or feed it into a follow-up call. The rendering decision lives at the model layer.
Discoverability. Apple Intelligence makes App Intents available from installation onward, with App Shortcuts donation and indexing surfacing the intents into Spotlight searches and Siri suggestions based on user behavior; app updates and dynamic entities adjust the surface over time. The user never types a tool name. MCP hosts read tools at session start; the user (or the system prompt) decides which tools the model can see. Discovery is explicit configuration on the MCP side and implicit system inference on the App Intents side.
The two protocols disagree on identity, latency, rendering, and discovery: four properties that follow from one root distinction. App Intents serve a system-level agent the user did not configure. MCP serves a session-level agent the developer did. Different callers, different obligations.
The Routing Rule
The capability map for an app with both protocols looks like this:
┌──────────────────────────────────────────┐
│ 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
The routing rule is three questions, in order.
Is the capability something the user would ask Siri or invoke from Shortcuts? If yes, the capability needs an App Intent. Log 250ml of water, Start a meditation, Add bananas to my list, What did I weigh yesterday are intents because the user might say them out loud, type them into Spotlight, or chain them in Shortcuts. The App Intent is non-optional for those capabilities; nothing else gets you to Apple Intelligence’s first-party agent surface.
Is the capability something an external agent should be able to drive? If yes, the capability needs an MCP tool. Add an item to the shopping list from a Claude Code session, Read Get Bananas state into a Cursor agent context, Trigger a workflow from a remote tool-using LLM are MCP tools because the caller is not Apple Intelligence; the caller is whichever LLM the developer wired up. The MCP tool can wrap the same domain-layer Swift function the App Intent calls, but the protocol surface is JSON-RPC over the developer’s chosen transport.
Does the capability need to outlive a single session with stable, system-known identity? If yes, the App Intent path is the natural fit, because the system gives you AppEntity identity, query support, and persistence semantics for free. If no, the MCP tool can return a content block, leave durable identity to the server’s discretion, and skip the entity-modeling cost.
Most non-trivial app capabilities fall in the both column. A water-logging capability in Water has an AppIntent (so Siri can take dictation) and an MCP tool (so a Claude Code session can backfill from an exported log). The two routes share a Swift function; the function does not know which caller invoked it.5
The shape, in code, looks like one domain method and two adapter wrappers that both call it:
// 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)")
The two adapters look different because their callers are different. The function they call is the same.
What Stays In The App
A small but important set of capabilities should stay private to the app. Routing them to either protocol is a mistake.
UI-state capabilities. “Open the third tab,” “scroll to the bottom,” “highlight this row” are not domain operations. They are interaction primitives. App Intents support some of this through OpensIntent and Shortcuts, but the genre fit is poor; the user usually wants a result, not a navigation. MCP support for UI navigation is even worse: the model is not driving a screen, it is driving a tool.
Capabilities that require a person’s body in the loop. Photo capture, biometric authentication, sensitive PII entry, any flow that requires the user to look at the screen and tap. Apple’s CameraCaptureIntent exists for camera flows, but the design intent is to launch a foreground capture activity, not to grant background camera access to an agent. The honest rule for both protocols: camera, biometric, and sensitive-entry flows should run as foreground UI with explicit user confirmation, not as silent intent or tool calls. Keep these capabilities behind the app’s UI and let the agent route the user to the screen, not through it.
Long-running background work. Both protocols grew real primitives here, and the calculus shifted from “never” to “with the protocol’s machinery, deliberately.” On the Apple side, iOS 27’s LongRunningIntent lets an intent run past the system’s 30-second background limit, on the condition that it reports progress (the protocol refines ProgressReportingIntent), with Live Activities rendering that progress8. On the MCP side, the 2025-11-25 spec adds experimental tasks for durable requests with polling and deferred results, graduating to a Tasks extension in the 2026-07-28 release candidate67. The honest boundary now: use the primitives when the caller initiated the work and expects to watch it; keep the work behind the app’s own UI when the “progress” a consumer needs is richer than a percentage, or when a host model would time out its reasoning chain waiting. The request was queued, here is the status screen is still the right return value for anything open-ended.
Anything that touches another user’s data. The trust boundary in both protocols is the calling agent. Apple Intelligence runs under the user’s iCloud account. MCP runs under whichever credentials the developer wired in. Operations that span users (sharing, multi-account access, admin actions) are not safe through either protocol because the calling identity is the wrong identity.
What I Would Build Differently
Knowing the routing rule above, I would design the domain layer in a Swift app the way I now design APIs at the boundary of a service. The domain methods take typed inputs and return typed outputs, no protocol assumptions baked in. App Intents thinly wrap domain methods with @Parameter schema and perform() glue. MCP tools thinly wrap the same domain methods with JSON schema and stdio framing. Both protocols are thin adapters; the work is in the domain.
Two consequences follow.
Caller identity is a domain concern, not a protocol concern. The App Intent body receives system-resolved parameters and runs in a context where the user has gone through the system’s intent invocation flow. The MCP tool body receives whatever credentials the host arranged. Both pass through to the domain method as an explicit caller argument. The domain method enforces authorization, confirmation prompts, and any other domain invariants. Neither protocol gets to pretend the caller is the user.
Both adapters surface the same affordance set. The decision of which capabilities to expose to which caller is captured in two manifests, not in scattered protocol code. Adding a new capability is one domain method, two adapter wrappers, two manifest entries. Removing a capability is symmetric. The matrix above becomes a real file.
The Apple platform’s frontier for the next few years is not picking one protocol. The frontier is treating both as orthogonal contracts that compose at the same domain layer. Apple Intelligence agents have one set of obligations to the user (they run on-device, they speak Siri, they render through the system). External LLM agents have a different set of obligations to the developer (they run wherever, they speak JSON-RPC, they render through whichever model the developer chose). Both deserve a typed surface into your app. Neither deserves to be the only surface.
When Not To Build Both
The argument cuts both ways. Some apps need one protocol and not the other.
Pure consumer utilities with no developer surface. A flashlight. A bird-call identifier. An augmented-reality measuring tape. The user might want to invoke it via Siri (App Intents are useful), but no developer is wiring it into an LLM workflow (MCP would be cosmetic).
Pure developer tools with no end-user surface. A code-formatter MCP server. A repository-search tool. A package-version inspector. The user is the developer in a Claude Code session; Siri and Apple Intelligence have no role.
Apps that serve neither agent class well. Highly interactive games, real-time multiplayer apps, apps where the value is being in-app and on-screen. Neither protocol is a good fit; the right answer is a great app and no agent contract.
The decision is not one or both by default. The decision is what is this app for and who else might want to operate its domain. The answer might be neither, one, or both. The cost of building one is small if the domain layer is well-shaped. The cost of building both, on top of that domain layer, is also small. The cost of not building one when the use case demands it is the capability’s complete absence from that agent surface.
What This Pattern Means For The Apple Stack On iOS 26+
Two takeaways.
-
Treat App Intents and MCP as orthogonal contracts on the same domain, not competing protocols. Apple Intelligence, Siri, Shortcuts, and Spotlight are one caller class with system-level obligations. Claude, Cursor, ChatGPT, and the rest are a second caller class with session-level obligations. Both deserve typed access. The domain layer beneath them does not change.
-
The routing rule is who calls, not what runs. The App Intent and the MCP tool can call the same Swift function. They differ on the obligations the caller carries, the rendering they get back, and the persistence they expect. Get the function right; let the protocol layers be thin.
The full Apple Ecosystem cluster: typed App Intents for Apple Intelligence, MCP servers for cross-LLM agents, Live Activities for the Lock Screen state machine, Liquid Glass patterns for the visual layer, and multi-platform shipping for cross-device reach. The hub is at the Apple Ecosystem Series. For the broader iOS-with-AI-agents context, see the iOS Agent Development guide.
FAQ
When should I build an App Intent vs an MCP tool for the same capability?
Build an App Intent when the capability should reach Apple Intelligence, Siri, Shortcuts, or Spotlight. Build an MCP tool when the capability should reach external LLMs (Claude, ChatGPT, agents in Claude Code or Cursor). For domain capabilities that should serve both caller classes, build both as thin adapters over a shared Swift domain method.
Do App Intents and MCP servers compete with each other?
No. App Intents are the path to Apple’s first-party agent stack; MCP is the path to every other LLM. Apple Intelligence does not call MCP tools, and external LLM agents cannot directly invoke App Intents (they go through the system). The two protocols serve different caller classes with different trust models, different latency budgets, and different rendering surfaces.
Can a single app expose its domain through both protocols?
Yes, and most non-trivial apps that want full agent reach should. Get Bananas (covered in the MCP server post) and Water (covered in the App Intents post) are early examples. The pattern is a domain layer underneath, with App Intent adapters and MCP tool adapters above. Both adapters call the same Swift functions.
What state does Apple Intelligence track that MCP does not?
Apple Intelligence tracks AppEntity identity across calls, session, and reboots, and with iOS 27’s SyncableEntity across the user’s devices8. The entity model gives the system persistent references the user can chain across intents. MCP itself is a stateful protocol with lifecycle management and session IDs in Streamable HTTP, but durable domain identity is a server-side responsibility rather than a first-class protocol contract; the host model does not get an AppEntity-equivalent identifier from the protocol surface. MCP’s resources concept and the 2025-11-25 spec’s experimental tasks support persistent reference data and durable requests, but both operate at the same server-owned layer6.
Are there capabilities I should not expose through either protocol?
Yes. UI-state capabilities (open this tab, scroll to here), capabilities that require a person’s body in the loop (camera capture, biometric auth, sensitive entry), and operations that span multiple users’ data should stay behind the app’s UI — neither protocol carries the trust signals required to operate safely across users. Long-running background work is the exception that moved: iOS 27’s LongRunningIntent and MCP’s tasks extension give it real primitives now, so expose it deliberately through that machinery and keep only open-ended, richer-than-a-progress-bar work behind your own UI.
References
-
Apple Developer, “App Intents framework”. Surface for declaring intents, entities, parameters, and queries that Apple Intelligence, Siri, Shortcuts, and Spotlight can route. ↩
-
Anthropic, “Model Context Protocol”. Open protocol for typed tool exposure across LLM hosts. Transport is stdio or Streamable HTTP;
.mcpbis a packaging format that commonly ships a local stdio server. Specification coverstools/list,tools/call,resources, and prompts. ↩ -
Apple Developer, “Creating your first app intent” and “AppEntity”.
AppIntentprotocol,@Parametermacro,func perform()entry point, andAppEntityfor persistent identity. ↩↩ -
Anthropic, “MCP Specification: Tools (2025-06-18)”, “MCP Architecture”, and “Transports (2025-06-18)”. JSON-RPC method definitions for
tools/listandtools/call,inputSchemaand optionaloutputSchema, host responsibilities, lifecycle management, and stdio / Streamable HTTP transports. ↩↩ -
Author’s analysis in App Intents Are Apple’s New API to Your App and Two Agent Ecosystems, One Shopping List. The dual-adapter pattern (one Swift domain method, two protocol wrappers) is described in both posts at the implementation level for Water and Get Bananas respectively. ↩
-
Model Context Protocol, “Key Changes” changelog for the 2025-11-25 revision. Major changes since 2025-06-18 include OpenID Connect Discovery support, tool/resource/prompt icons, tool-name guidance, tool calling in sampling via
toolsandtoolChoice, OAuth Client ID Metadata Documents, “experimental support for tasks to enable tracking durable requests with polling and deferred result retrieval,” and JSON Schema 2020-12 as the default schema dialect. The revision also formalizes MCP’s governance structure. ↩↩↩↩↩ -
Model Context Protocol blog, “The 2026-07-28 MCP Specification Release Candidate”. The release candidate delivers “a stateless core that scales on ordinary HTTP infrastructure,” moves tasks from an experimental core feature to a Tasks extension for long-running work, adds server-rendered UI through MCP Apps, and hardens authorization around OAuth 2.0 and OpenID Connect. Finalization is expected July 28, 2026; at the time of writing it remains a release candidate. ↩↩↩
-
Apple Developer, “LongRunningIntent” (iOS 27 beta), declared
protocol LongRunningIntent : ProgressReportingIntent, which extends an intent’s background runtime past the standard 30-second limit viaperformBackgroundTask(options:operation:)on the condition of progress reporting, and “SyncableEntity” (iOS 27 beta), which declares that anAppEntitycarries an identifier consistent across a user’s devices. Both are covered in depth in App Intents in iOS 27: Background, Sync, Spotlight. ↩↩↩