ui:// resource. Allternit renders it inline in the conversation and connects it to the host with a JSON-RPC bridge over postMessage. This page follows the MCP Apps specification version 2026-01-26 and lists what Allternit’s host implements.
Linking a tool to a View
Set_meta.ui.resourceUri on the tool. The value must start with ui://.
_meta["ui/resourceUri"] and the output-template key some hosts use (see Porting), so a server written for another host is found without changes. registerAppTool from @modelcontextprotocol/ext-apps/server writes both ui.resourceUri and ui/resourceUri.
Serve the resource with:
- URI starting with
ui:// - MIME type
text/html;profile=mcp-app(Allternit also accepts plaintext/html) - HTML in the
textfield, at most 2 MiB
Resource metadata
Set these under_meta.ui on the resource content:
The bridge
The View talks to the host with JSON-RPC 2.0 messages sent towindow.parent. You can write these by hand (the quickstart does) or use the App class from @modelcontextprotocol/ext-apps, which wraps them.
Lifecycle
- The View sends the request
ui/initializewithprotocolVersion,appInfo, andappCapabilities. - The host replies with its capabilities and
hostContext(theme, styles, display mode, and so on). - The View sends the notification
ui/notifications/initialized. - The host sends
ui/notifications/tool-input(the arguments) andui/notifications/tool-result(the result).
Messages the host sends to the View
Messages the View can send to the host
Any other method is rejected with JSON-RPC error
-32601. ui/download-file is not implemented.
The host advertises these capabilities at initialize: openLinks, serverTools, serverResources, logging, and x-allternit. It also advertises sandbox with the csp and permissions your resource declared.
Handling a tool call from the View
Host context
hostContext is sent in the ui/initialize result and again in ui/notifications/host-context-changed.
Display modes
Views startinline. Request fullscreen with ui/request-display-mode when a task needs the space:
inline and fullscreen exist. Picture-in-picture is not supported. Any other requested mode resolves to inline. The View is not reloaded when the mode changes. Handle host-context-changed to adapt the layout.
Theming
The host resolves its design tokens to concrete colors and sends them as CSS variables inhostContext.styles.variables. Apply them to your document root and use them in your CSS, with fallbacks for hosts that send none:
When the user changes theme, the host sends
ui/notifications/host-context-changed with new values. Re-apply them.
Colors are sent as resolved values, not var() references, so they are safe to use inside your View’s own stylesheet.
Content Security Policy
The host builds the View’s CSP from thecsp object on your resource. The default is closed: no network access, no nested frames.
The resulting policy is:
- Each entry must be an origin:
scheme://host[:port], optionally with a leading*.wildcard, for examplehttps://api.example.comorhttps://*.example.com. - Entries that contain whitespace,
;,,, or quotes, or that are bare keywords, a lone*, or bare schemes likedata:orblob:, are dropped. - Declare only what the View needs. The review scan warns on wildcards, non-TLS schemes, more than 10 domains in total, and any
frameDomains, which need a written justification.
<meta> element placed before your document’s own content. Inline scripts and styles are allowed, so a single self-contained HTML file works. Because <script src> and stylesheet links resolve against an opaque origin, use absolute URLs on a declared resourceDomains entry, or inline the code.
CSP violations are reported to the host, which logs them. They do not break the View. If something is silently blocked, look in the browser console for Content Security Policy messages.
Sandbox rules
The View runs inside two layers:- The sandbox proxy is a static page on a separate origin from Allternit. Allternit’s web client embeds it, sends it your HTML over
postMessage, and refuses to render Views if no sandbox origin is configured. - Inside the proxy, your HTML runs in a nested iframe whose
sandboxattribute does not includeallow-same-origin.
- Opaque origin.
localStorage,sessionStorage,document.cookie, and IndexedDB are unavailable or throw.fetchfrom the View carries no cookies and sees no ambient credentials. - No access to the host. The View cannot read the host page, the proxy page, or the user’s tokens.
- Nested frames are blocked unless you declare
frameDomains. - Navigation. The sandbox allows scripts, forms, popups (which escape the sandbox), downloads, and modals. It never allows top-level navigation. To send the user elsewhere, call
ui/open-link. - Permissions. Camera, microphone, geolocation, and clipboard-write are available only if you declare them in
permissions. - One View per proxy document. A View cannot replace itself with another.
State guidance
Because the View has no storage, treat it as a pure function of what the host gives it.- Render from
tool-inputandtool-result. Do not assume they arrive before your first paint; render a placeholder first. - Keep durable state on your server. Save it with a tool that has
visibility: ["app"]and read it back withtools/callon load. - Keep model-relevant state out of the View. Use
ui/update-model-contextto tell the model what matters (for example, which row the user selected) andui/messageto add a message to the conversation. - Expect the View to be torn down and re-created when the conversation is reloaded. Rebuild it from the tool result.
- Report your content height with
ui/notifications/size-changedafter every layout change, or the inline frame will not fit your content.