App Intents vs MCP: The Routing Question

Genre: frontier-essay. The piece names a routing rule for agentic Apple development. 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 .mcpb as a packaging format that commonly ships a local stdio server; the host loads tools at session time.
  • Both protocols converge on a typed schema, an entity → action → result shape, 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 (with the 2025-06-18 spec adding optional outputSchema for structured returns).4 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 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.

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 through NSUbiquitousKeyValueStore, 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 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, but identity remains a server-side responsibility rather than a first-class protocol contract.4

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. App Intents include ProgressReportingIntent for surfacing progress, and MCP includes progress notifications and task primitives, but neither protocol’s rendering layer is built for sustained progress on consumer flows. The host model in an MCP session usually times out reasoning chains long before a multi-minute tool finishes. For consumer-facing long-running work, expose the operation through the app’s own UI; let the protocols return the request was queued and link to a status screen.

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.

  1. 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.

  2. 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. 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 supports persistent reference data but operates at the same server-owned layer.

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), long-running background work, and operations that span multiple users’ data should all stay behind the app’s UI. Both protocols have weak primitives for these, and neither carries the trust signals required to operate safely across users.

References


  1. Apple Developer, “App Intents framework”. Surface for declaring intents, entities, parameters, and queries that Apple Intelligence, Siri, Shortcuts, and Spotlight can route. 

  2. Anthropic, “Model Context Protocol”. Open protocol for typed tool exposure across LLM hosts. Transport is stdio or Streamable HTTP; .mcpb is a packaging format that commonly ships a local stdio server. Specification covers tools/list, tools/call, resources, and prompts. 

  3. Apple Developer, “Creating your first app intent” and “AppEntity”. AppIntent protocol, @Parameter macro, func perform() entry point, and AppEntity for persistent identity. 

  4. Anthropic, “MCP Specification: Tools (2025-06-18)”, “MCP Architecture”, and “Transports (2025-06-18)”. JSON-RPC method definitions for tools/list and tools/call, inputSchema and optional outputSchema, host responsibilities, lifecycle management, and stdio / Streamable HTTP transports. 

  5. 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. 

Related Posts

Two Agent Ecosystems, One Shopping List: An MCP Server Living Alongside an iOS App

Get Bananas runs on iOS, macOS, watchOS, visionOS. It also lives inside Claude Desktop as an MCP server. The bridge is i…

19 min read

Foundation Models On-Device LLM: The Tool Protocol

iOS 26's Foundation Models framework puts a 3B-parameter LLM on every Apple Intelligence device. The Tool protocol is th…

15 min read

Your Agent Has a Middleman You Didn't Vet

Researchers tested 28 LLM API routers. 17 touched AWS canary credentials. One drained ETH from a private key. The router…

15 min read