MCP Apps
MCP Apps
MCP Apps turn a compatible tool result into an optional interactive view. Holaryn implements the
stable MCP Apps extension, io.modelcontextprotocol/ui version 2026-01-26, while treating every
server, resource, and bridge message as untrusted. The interactive view is an enhancement: the
text or structured tool result always remains available.
Enable and open an app
Approve the apps capability for a server in Settings → MCP, or add it to the server manifest:
approved_capabilities = ["tools", "prompts", "resources", "apps"]
This is an authority ceiling, not a trust declaration. A server may then advertise tool metadata
that references a ui:// resource. Holaryn records that reference during discovery but does not
fetch or execute it. After the tool finishes, the conversation shows an app card:
- Load app fetches, validates, pins, and opens the exact resource.
- Open as structure shows the bounded text or structured result without running scripts.
- Inspect shows the server/tool identity, trust, resource URI, origin, requested permissions
and network origins, integrity digest, size, and data sent/received. - Refresh pinned resource intentionally fetches a newer copy.
- Close returns keyboard focus to the opener.
- Revoke deletes the pinned resource and prevents the view from reopening.
If a pinned file disappears or is corrupt, the card becomes unavailable and the structured
fallback remains usable. Refresh is explicit; Holaryn never silently substitutes a changed resource.
The security boundary
An app never shares the Holaryn document origin. A trusted, fixed proxy runs on
mcp-app.localhost; the server-supplied HTML runs inside a second iframe with an opaque origin.
The inner frame has scripts but not allow-same-origin. It receives no cookies, bearer tokens,
provider credentials, local storage shared with Holaryn, parent DOM access, referrer, direct
filesystem access, clipboard access, popups, or top-level navigation.
Holaryn validates the source window, origin, app-session identifier, JSON shape, byte limit, and
monotonic action sequence on every bridge message. The resource CSP always denies objects, forms,
ancestors, base URLs, and navigation. Declared HTTPS origins and browser permissions are requests,
not grants; the host policy can only narrow them. This release grants no app network origin.
App-originated tool calls may target only the same MCP server and tools visible to app. They run
through the same consequence classification, approval prompt, canonical events, result bounds,
and error handling as model-originated calls. Opening a link, requesting a download, writing the
clipboard, selecting a file, or sharing data is a typed host action and is ask-first. Unsupported
or unavailable actions fail closed.
A link request that you refuse opens nothing and the card says nothing; the app is told it was
refused. Holaryn opens an approved link only if it is a web page on another site whose address has
no control characters and is not too long for the desktop app to take (8192 bytes); any other
address is refused, with no Copy link. It opens in your web browser from the desktop app, or in a
new tab from a browser. The app is told whether it opened (when Holaryn cannot confirm it, as in a
desktop app connected to a host on another computer, that is not reported as an error). The card
shows the result in host wording, naming the app and never the website, and speaks it in the
conversation: an opened link, or one Holaryn could not confirm, only if the Holaryn window still
has focus a moment later; a link that did not open at once. A link that did not open, or could
not be confirmed, offers Copy link, and Open link after a browser blocked the new tab;
Open link opens the link you already allowed, without asking again. The sentence stays next to
those buttons, and screen readers hear them as "… from the tool app". The result never moves
keyboard focus unless the button you were on goes away; then focus moves to Copy link or the
card's heading.
Accessibility and display
The app card and its host controls are semantic HTML with visible focus. They work at 200% zoom,
support reflow and high contrast through the normal Holaryn tokens, and honor reduced-motion
preferences. App-requested height is clamped. Because third-party app markup may not be accessible,
Open as structure is always available and does not rely on the app script.
Screen-reader users should inspect the identity and permissions before loading unfamiliar UI.
The structured fallback is the supported recovery path if a third-party app traps focus, fails to
initialize, or exposes inaccessible controls.
Developer contract
A server advertises the extension during initialization, places
_meta.ui.resourceUri (or the compatibility key _meta["ui/resourceUri"]) on a tool, and serves
that URI as text/html;profile=mcp-app. Tool visibility defaults to both model and app; use
_meta.ui.visibility to narrow it. Resource metadata may declare prefersBorder, CSP origins, and
browser permissions, but declarations never create host authority.
The host bridge uses JSON-RPC over postMessage via the official AppBridge package. It provides
theme, locale, timezone, bounded dimensions, tool input/result, resizing, follow-up tool calls,
links, downloads, messages, and model-context updates. Unknown methods, cross-server calls,
replayed sequences, malformed payloads, and oversized messages are rejected.
Use the repository fixture under tests/fixtures/mcp_apps/ and the checks in
docs/mcp-apps-a11y-checklist.md when developing or reviewing an app. The upstream protocol is
documented in the MCP Apps overview
and versioned specification.