Skip to main content
Core8 min1371 words

MCP Apps vs plain tool output: when an AI integration needs a UI

A practical choice between a standard text or structured MCP response and an interactive MCP App: value criteria, architecture, security, fallback behavior, testing, and rollout.

Article contents
  1. 01Short answer: use UI for interaction, not decoration
  2. 02How an MCP App complements the normal tool contract
  3. 03Decision matrix: reading, exploration, input, and execution
  4. 04A sandbox reduces risk but does not create trust
  5. 05Design fallback and portability before the first render
  6. 06Tests must cover protocol, accessibility, and consequences
  7. 07Rollout: one interaction slice and an explicit way back

Short answer: use UI for interaction, not decoration

Keep plain tool output when the result can be reliably read, cited, or handed to the next step as compact text or typed data. Choose an MCP App when the user needs to explore multidimensional data, manage form state, inspect rich media, or work through many objects in sequence. The official extension lets a tool declare an interactive UI resource that a compatible host renders inside the conversation.

An MCP App does not make a tool more accurate or grant it extra authority. It is a separate presentation and interaction layer over server capabilities. If a ten-row table and a clear recommendation already satisfy the intent, an iframe, JavaScript bundle, event protocol, and another security surface only add cost. Start with task analysis: what action cannot the person complete conveniently or safely through a normal response?

  • Short answer, citation, or machine-readable handoff → plain tool result.
  • Filters, drill-down, canvas, media controls, or multi-step review → candidate for an MCP App.
  • Consequential action → server-side policy and explicit confirmation regardless of UI.
  • Host does not support the extension → useful text or structured fallback.
  • No measurable interaction benefit → do not add an app layer.

How an MCP App complements the normal tool contract

In the basic pattern, the tool definition contains `_meta.ui.resourceUri` pointing to a `ui://` resource. The host retrieves the HTML resource, usually renders it in a sandboxed iframe, and delivers the tool result to the view. UI and host communicate through JSON-RPC over `postMessage`: the app can receive results, ask the host to invoke an allowed server tool, or update model context. The tool and its schema remain the canonical execution contract.

Separate three kinds of state. Authoritative domain state belongs in the system of record; the tool result is a versioned snapshot or handle; ephemeral view state contains the selected tab, filter, or unfinished field. Do not hide the only operation identifier in browser state. After refresh, rerender, or fallback, the user must be able to restore context through an explicit resource ID and a server-verified version.

Decision matrix: reading, exploration, input, and execution

For one fact, a list of conclusions, or a small set of records, plain output is easier to preserve in the transcript, easier for the model to inspect, and works across more clients. For exploration of a cohort heatmap, map, timeline, or large table, UI with local sort and filter can reduce repeated model calls. The app should send only meaningful user decisions back to the model, not every hover or scroll event.

For a few missing parameters, host-native elicitation or the next conversational turn is usually enough. An MCP App is justified for interdependent fields, live preview, or multi-step review. For writes, the UI prepares an exact-action proposal, but the server rechecks identity, tenant, object version, scope, and approval. A button labelled Approve is not proof of authorization, and a hidden role field is not a trusted claim.

  • One result and up to five simple fields → start with text, structured content, or a native form.
  • Large dataset with local exploration → app with a bounded snapshot and provenance.
  • Dependent configuration with preview → app, with validation repeated on the server.
  • Payment, publish, delete, or production change → proposal, policy gate, idempotency, and reconciliation.
  • Different client capabilities → progressive enhancement, not two business logics.

A sandbox reduces risk but does not create trust

The official model isolates the app from the parent DOM, host cookies, and local storage and routes communication through a controlled channel. Resource metadata can declare Content Security Policy origins and requested permissions. The host decides which capabilities to grant. The app should therefore use minimal connect, resource, and permission allowlists; microphone, camera, clipboard, or external navigation should not be requested just in case.

Treat the HTML or JavaScript resource, tool result, and data from other tools as separate untrusted inputs. The host validates resource URI, extension negotiation, message origin, method allowlist, payload size, and correlation ID. The server never relies on a disabled button or client-side validation. Secrets and bearer tokens do not enter model context or the view bundle; the app calls the server tool through the host while the credential boundary stays outside the iframe.

Design fallback and portability before the first render

MCP Apps are an opt-in extension, and support depends on the host and version. A tool should return a useful semantic result even when UI does not render: a concise content summary for the person, `structuredContent` for the client or model, and stable identifiers for the next call. Do not return only an instruction to open the widget, because extension negotiation or render failure then becomes functional loss.

Progressive enhancement means one server-side business operation with multiple presentation paths. The app must not receive a hidden privileged endpoint unavailable to the ordinary client flow. If rich interaction is inherently nonportable, define a minimum fallback such as a read-only summary, downloadable artifact, or safe link to a standalone product. Analytics should distinguish app, fallback, and unsupported-host outcomes without treating render as a completed business action.

Tests must cover protocol, accessibility, and consequences

The contract suite verifies tool metadata, the `ui://` resource, MIME profile, initialization, tool-result delivery, message validation, and graceful degradation without the extension. Browser tests cover sandboxing, CSP denial, slow bundles, refresh, duplicate events, stale snapshots, offline state, and two concurrent views. Security tests attempt to invoke an undeclared tool, substitute an object ID, introduce an external origin, and replay a consequential request.

Interaction quality should be tested with keyboard-only navigation, focus order, labels, error announcements, color contrast, zoom, and narrow viewports. A model eval separately checks whether the assistant chooses the right tool, explains the app, and uses only relevant user selections. The important product signals are task completion, correction rate, time to verified outcome, and fallback success; click count or render count alone does not prove value.

Rollout: one interaction slice and an explicit way back

Choose one read-heavy scenario with an obvious UI advantage, such as expense exploration with filters and drill-down. Record the plain-output baseline, implement the app as progressive enhancement, and replay both against identical snapshots. Limit the canary to test tenants and read-only tools; open writes only after negative tests, accessibility review, policy evidence, and idempotent reconciliation.

A feature flag should disable the app resource independently without disabling the base tool. Rollback stops new renders, restores the plain response, invalidates the problematic asset version, and reconciles unfinished operations. Audit links server, tool, resource version, host capability, view session, user action, policy verdict, and authoritative outcome without storing sensitive form fields. Remove the app after rollout if it does not improve the defined outcome or creates unacceptable operator burden.

Practical examples

Expense explorer without autonomous approval

The tool returns a bounded expense snapshot, currency, generatedAt, source references, and a structured summary. A compatible host renders an MCP App with filters, chart, and drill-down; fallback shows the main anomalies and IDs. When the user selects records for review, the app sends a typed proposal. A separate server tool rereads the current records, verifies tenant and version, and creates a review queue, but does not approve payment.

FAQ

Does an MCP App replace a web application?

Not always. It is useful for bounded interaction inside a conversation. A full product with its own navigation, account lifecycle, and complex workflows can remain a separate web app.

Can an MCP App be used without a text fallback?

That narrows portability and turns render failure into functional failure. Return a useful semantic result and stable identifiers even for a host without the extension.

Is a sandboxed iframe secure by default?

The sandbox is an important boundary, but the host still validates origins, messages, capabilities, and payloads, while the server reapplies authorization and domain policy.

When should elicitation be used instead of an MCP App?

For a few missing fields or a simple confirmation, native interaction is usually enough. An app fits rich preview, dependent fields, navigation, and repeated multi-item review better.

Related materials

Sources

  1. MCP Apps overview — Model Context Protocolofficial
  2. MCP Apps specification 2026-01-26primary
  3. MCP Apps API overviewofficial
  4. MCP Extensions support matrixofficial