MCP Apps let any connected MCP server ship interactive HTML and JavaScript into the user's AI conversation, which means a third-party server now controls a piece of web UI that sits beside the user's chat, can call tools and can write into the model's context. The MCP Apps extension (SEP-1865, identifier io.modelcontextprotocol/ui, stable specification dated 2026-01-26) answers this with mandatory sandboxed iframes, a declared Content Security Policy, opt-in browser permissions and an auditable JSON-RPC channel over postMessage. Those controls are only as good as the host's implementation and the server author's choices. This guide explains the trust model, the controls on each side, what to log and a hardening baseline, using the extension specification and the official overview checked on 27 October 2026. It is for teams building MCP hosts, teams publishing MCP servers with UI, and security reviewers approving them. The general MCP risk picture is in the MCP Security Guide.
Key takeaways
- A View is untrusted web content. The specification's threat model assumes a malicious server may deliver harmful HTML, try to escape the sandbox, call tools without authority, exfiltrate host data or phish the user.
- The host enforces the boundary. Views MUST run in sandboxed iframes; web hosts MUST use a sandbox proxy on a different origin, and MUST build CSP from declared domains without ever allowing undeclared ones.
- Default CSP is closed to the network. If a server declares no CSP, the host MUST apply
default-src 'none'andconnect-src 'none'; every external origin the server needs must be declared and is visible for review. - UI-initiated actions need policy. Views can request
tools/call, post chat messages and update model context; hosts may block these or require user approval, and should log them all. - App-only tools are still tools.
visibility: ["app"]hides a tool from the model but not from the View, so server-side authorisation must not assume a human clicked a button.
How identity and access work in MCP Apps
MCP Apps combine two MCP primitives. A tool declares a UI in its metadata (_meta.ui.resourceUri pointing at a ui:// resource). The host fetches that resource with resources/read; the content MUST use the text/html;profile=mcp-app MIME type and be a valid HTML5 document. The host renders it as a View inside a sandboxed iframe and talks to it over postMessage using a JSON-RPC dialect of MCP. Conceptually the View is an MCP client and the host is the MCP server it connects to, proxying permitted requests to the real MCP server. The extension is optional and must be negotiated through the extension capabilities mechanism.
| Party | What it controls | What it must not be trusted with |
|---|---|---|
| MCP server (author of the View) | HTML template, declared CSP domains, requested permissions, optional dedicated origin, tool visibility | Access to the host page, cookies or other servers' tools |
| Host (Claude, VS Code GitHub Copilot, Microsoft 365 Copilot and others listed by the project) | Sandbox, CSP enforcement, permission grants, which View requests are forwarded, user consent | Assumed to be trusted by the user; it holds the user's connections |
| View (running iframe) | Rendering and requests over postMessage | Anything beyond what the host forwards |
| User | Interactions, consent prompts | Detecting deception unaided |
The View's reach is defined by a small set of methods: standard tools/call and resources/read, notifications/message for logging, and app-specific requests including ui/open-link, ui/message (send content to the chat), ui/update-model-context (context used in future turns) and ui/request-display-mode. Tool visibility adds a second axis: "model" tools appear to the agent, "app" tools are callable only by Views from the same server connection. Hosts MUST NOT list app-only tools to the agent, MUST reject View calls to tools without "app" visibility, and cross-server calls to app-only tools are always blocked.
Admin roles and privileged access
In MCP Apps the privileged decisions sit with whoever configures the host, because the host decides how much of the server's request to honour.
- Capability restriction: the official overview notes that a host can restrict which tools an app may call or disable capabilities such as opening links. Enterprise administrators should look for host settings that disable MCP Apps for unapproved servers, restrict UI-initiated tool calls and require approval for links and chat messages.
- Browser permissions: Views may request
camera,microphone,geolocationandclipboardWrite, mapped to Permission Policy features on the iframeallowattribute. Hosts MAY honour these, report what they granted inhostContext.sandbox.permissions, and Views SHOULD NOT assume a grant. Default to denying all four and grant per server only with a business reason. - External domains: hosts SHOULD warn users when a UI requires external domain access and MAY keep global domain allowlists or blocklists. Use them: a corporate host should allow only domains on an approved list.
- Template review: because UI resources are predeclared, hosts can review HTML before rendering. The specification lists reviewing content for obvious malicious patterns, generating a hash or signature, warning on suspicious content and allowlisting or blocklisting by resource hash.
Decision guidance: a small organisation using a mainstream host should rely on its defaults, restrict MCP Apps to servers it already trusts, and keep permissions off. An organisation building its own host carries the full set of MUST requirements below and should treat the sandbox proxy as a security-critical component with its own review and tests.
Integrations, API tokens and service accounts
Views should hold no long-lived secrets. The design routes data access through MCP: a View calls tools via the host, and the host's connection to the MCP server carries the server's authorisation, so the View never needs the user's token.
- No credentials in templates: anything in the HTML is readable by the host, by reviewers and by anyone who can call
resources/read. Never embed API keys; serve data through tools that authorise the user server-side. - Direct API calls: if a View must call an external API itself, declare that origin in
connectDomains(mapped toconnect-src), and use short-lived, narrowly scoped tokens delivered through tool results rather than static keys. - Dedicated origins: the optional
_meta.ui.domainfield requests a stable sandbox origin for OAuth callbacks, CORS policies or API key allowlists. Its format is host-specific (the specification gives hash-based subdomain examples), and if omitted the host uses a default origin, typically per conversation. Only request one when an upstream integration genuinely needs a fixed origin, and bind upstream allowlists to that exact origin. - App-only tools as API endpoints: refresh buttons and form submissions often use
visibility: ["app"]tools. Authorise and validate them like any API: the call reaches the server with the user's authorisation, but the arguments came from server-supplied JavaScript running in a sandbox, and a compromised or injected View can send anything.
The upstream credentials the MCP server uses to serve these tools are covered in the Remote MCP Server Hardening Guide.
Logging and detection
The extension builds auditability in: all View-to-host communication is JSON-RPC, and the specification asks hosts to validate all incoming messages, reject malformed message types, log View-initiated RPC calls, and log CSP configurations for security review.
| Event | Why it matters | Alert on |
|---|---|---|
| UI resource fetched (URI, server, hash, declared CSP and permissions) | Baseline for template review and change detection | Hash change without a server release; new external domains |
View-initiated tools/call | Actions taken from UI, not by the model | High-risk tools; call bursts; calls rejected for visibility |
ui/message and ui/update-model-context | Content entering the conversation from server-supplied code | Instruction-like content; large or frequent updates |
ui/open-link | Navigation outside the sandbox | Domains not on the allowlist; non-https schemes |
| CSP violation reports and blocked connections | Attempts to reach undeclared origins | Any repeat from the same View |
| Permission grants | Camera, microphone, geolocation, clipboard | Grants to servers outside the approved list |
Metrics:
- Servers with MCP Apps enabled versus approved (target: no unapproved).
- Views whose CSP declares wildcard or broad CDN domains (target falling to zero).
- Permission grants per server, reviewed quarterly.
- Percentage of View-initiated high-risk tool calls that went through user confirmation (target 100%).
- Template hash changes reviewed before rendering, for hosts that pin hashes.
Hardening baseline
Host requirements (from the specification)
- Sandbox everything: all View content MUST be rendered in sandboxed iframes with restricted permissions.
- Use a sandbox proxy on web hosts: the host and the sandbox MUST have different origins; the sandbox iframe MUST have
allow-scriptsandallow-same-origin, and loads the View in an inner iframe. The host MUST NOT send requests to the View before it receives theinitializednotification, and the sandbox SHOULD NOT originate requests of its own. - Build CSP from metadata: the host MUST construct CSP from declared domains, MUST block connections to undeclared domains and MAY restrict further but MUST NOT loosen. Without metadata, apply
default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:; connect-src 'none';. Useframe-src 'none'unlessframeDomainsis declared,base-uri 'self'unlessbaseUriDomainsis declared, and alwaysobject-src 'none'. - Enforce visibility: hide app-only tools from the agent and reject View calls to model-only tools.
- Gate UI-initiated actions: the host MAY block View messages or subject them to user approval, and MAY request consent for
ui/message. Require confirmation for high-risk tools, validateui/open-linkURLs to an https allowlist, and treatui/update-model-contextcontent as untrusted input to the model. - Mark the boundary: the specification notes that UI can still display misleading content, so hosts should clearly indicate sandboxed UI boundaries, and should impose CPU and memory limits against resource exhaustion.
Server author practices
- Declare the minimum: bundle scripts and styles into the template where practical so
resourceDomainscan stay empty; list exact origins rather than wildcard subdomains (which the specification permits, for examplehttps://*.example.com). - Pin third-party code: if you load from a CDN, use exact versions so a changed library cannot alter a reviewed View.
- Escape data: tool results pushed through
ui/notifications/tool-resultare data; render them as text, not HTML, to avoid XSS inside your own View. See the Cross-Site Scripting (XSS) Prevention Guide. - Do not rely on partial input: streamed partial tool arguments MUST NOT be relied on for critical operations.
- Keep the text fallback honest: tools MUST return meaningful content even when UI is available, and that content is what non-UI hosts and the model see.
Phased rollout for organisations
- Inventory: list connected servers that declare
_meta.ui.resourceUrion any tool, using the extension capability or tool metadata. - Review: for each, record the template hash, declared CSP domains and permissions; reject servers that ask for permissions without need.
- Pilot: enable MCP Apps for approved servers in one group, with confirmation required for View-initiated tool calls and links.
- Expand: widen to more users once logs show expected behaviour; keep hash monitoring on.
- Roll back: disable the extension per server (the tool falls back to text) rather than disconnecting the server entirely.
Framework mapping
| Control | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | Threat addressed |
|---|---|---|---|
| Sandboxed iframe and separate origin | SC-39 Process Isolation, SC-18 Mobile Code | PR.PS-01 | T1185 Browser Session Hijacking |
| CSP from declared domains | AC-4 Information Flow Enforcement, SC-7 | PR.IR-01 | T1567 Exfiltration Over Web Service |
| Visibility and consent for UI tool calls | AC-3, AC-6 | PR.AA-05 | AML.T0053 AI Agent Tool Invocation |
Treat ui/message and context updates as untrusted | SI-10 | PR.PS-06 | AML.T0051 LLM Prompt Injection; OWASP LLM01 Prompt Injection |
| Template hashing and review | CM-14 Signed Components, SR-4 Provenance | PR.PS-02 | T1195.002 Compromise Software Supply Chain |
| Logging View-initiated calls and CSP | AU-2, AU-12 | PR.PS-04, DE.CM-09 | Supports detection across the above |
OWASP LLM05 Improper Output Handling applies where a View renders tool or model output as HTML. In ISO/IEC 27001:2022 Annex A, 8.26 Application security requirements and 8.23 Web filtering are the closest fits. See the Content Security Policy Guide for CSP fundamentals.
Common misconfigurations
- Same-origin rendering: a custom web host renders the View in an iframe on its own origin with
allow-same-origin, so View scripts can reach host storage and the DOM. The specification's different-origin sandbox proxy exists to prevent this. - Loosened CSP: a host adds a permissive fallback such as allowing any https origin when a View breaks, violating the no-loosening rule.
- Auto-approved UI tool calls: a button in a View triggers a destructive tool with no confirmation, because the host treats View requests as user intent.
- Model context as an injection channel:
ui/update-model-contextcontent is passed to the model as if it were trusted, letting a View steer later turns. See the Prompt Injection Guide. - Over-broad declarations: server authors declare wildcard CDN domains and all four permissions "just in case", which reviewers then approve without challenge.
- Unchecked link opening:
ui/open-linkforwards any URL to the browser, turning a View into a phishing launcher.
Practitioner checklist
- Enable MCP Apps only for approved servers and record each template's hash, CSP declaration and permissions.
- Render Views in sandboxed iframes; on web hosts, use a sandbox proxy on a separate origin.
- Build CSP strictly from declared domains, use the restrictive default when none is declared, and never loosen it.
- Deny camera, microphone, geolocation and clipboard permissions unless a reviewed need exists.
- Enforce tool visibility rules and require user confirmation for View-initiated high-risk tool calls.
- Validate
ui/open-linktargets and treatui/messageandui/update-model-contextas untrusted model input. - Log View-initiated RPC calls, CSP configurations and permission grants, and alert on template hash changes.
- As a server author, bundle assets, declare exact origins, keep secrets out of templates and authorise app-only tools server-side.
Standards and references
- Model Context Protocol: MCP Apps extension specification (stable 2026-01-26)
- Model Context Protocol: SEP-1865 MCP Apps: Interactive User Interfaces for MCP
- Model Context Protocol: MCP Apps overview
- Model Context Protocol: Specification (Security and Trust and Safety principles)
- Model Context Protocol: Security Best Practices
- OWASP: Top 10 for LLM Applications 2025
- OWASP: MCP Top 10
Related NHI Mgmt Group resources: MCP Security Guide · MCP Sampling and Elicitation Security Guide · MCP Security Best Practices Explained · Content Security Policy Guide · Browser and Computer-Use Agent Guide