@modelcontextprotocol/ext-apps JavaScript SDK for host communication, and FastMCP’s AppConfig for resources and CSP.
How it works
An MCP App has two parts:- A tool that does the work and returns data
- A
ui://resource containing the HTML that renders that data
AppConfig. When the host calls the tool, it also fetches the linked resource, renders it in a sandboxed iframe, and pushes the tool result into the app via postMessage. The app can also call tools back, enabling interactive workflows.
AppConfig
AppConfig controls how a tool or resource participates in the Apps extension. Import it from fastmcp.apps:
resource_uri to point to the UI resource:
Tool visibility
Thevisibility field controls where a tool appears:
["model"]— visible to the LLM (the default behavior)["app"]— callable from within the app UI, kept out of the LLM’s tool list["model", "app"]— both
tools/list the host does the filtering — the division the MCP Apps specification defines. Every tool is advertised carrying its visibility metadata, which is also what lets a proxy or gateway forward it: an intermediary can only route to a tool it can see.
That division assumes a host stands between the server and the model. Where one doesn’t, FastMCP applies the declaration itself. Tool search and code mode reach the model as ordinary tool output rather than as an advertised listing, and their call-tool proxies execute a name the model supplies — nothing downstream can filter either, so app-only tools are excluded from both. The app’s own UI still reaches its backends, because a UI calling by identity is not the model.
AppConfig fields
On resources,
resource_uri and visibility must not be set — the resource is the UI. Use AppConfig on resources only for csp, permissions, and other display settings.UI resources
Resources using theui:// scheme are automatically served with the MIME type text/html;profile=mcp-app. No need to set it manually.
postMessage channel for communication.
Writing the app HTML
Your HTML app communicates with the host using the@modelcontextprotocol/ext-apps JavaScript SDK. The simplest approach is to load it from a CDN:
App object provides:
app.ontoolresult— callback that receives tool results pushed by the hostapp.callServerTool({name, arguments})— call a tool on the server from within the appapp.onhostcontextchanged— callback for host context changes (e.g., safe area insets)app.getHostContext()— get current host context
If your HTML loads external scripts, styles, or makes API calls, you need to declare those domains in the CSP configuration. See Security below.
Security
Apps run in sandboxed iframes with a deny-by-default Content Security Policy. By default, only inline scripts and styles are allowed — no external network access.Content Security Policy
If your app needs to load external resources (CDN scripts, API calls, embedded iframes), declare the allowed domains withResourceCSP:
Permissions
If your app needs browser capabilities like camera or clipboard access, request them viaResourcePermissions:
Example: a QR code server
This example creates a tool that generates QR codes and an app that renders them as images. It’s based on the official MCP Apps example. Requires theqrcode[pil] package.
generate_qr, the QR code appears in an interactive frame inside the conversation.

