Govern MCP Apps (interactive tool UIs)
With MCP Apps, a tool can show an interactive page: a chart, a form, a dashboard. The client draws the page in a sandboxed frame next to the chat, and the page can call the server's tools. That is useful, and it is also a new way in for hidden instructions and a new way out for data. The page is HTML and script written by the server, and it can ask the client to let it reach other websites.
DVARA, the AI governance platform, governs these pages. The DVARA MCP Gateway carries MCP Apps between your clients and your MCP servers only where a workspace allows it. It serves only the page an operator approved, it limits the websites and browser permissions a page may ask for, it scans the page for personal data and prompt injection, and it records each page it serves in the tamper-evident audit trail.
Not included in DVARA Open Source.
What are MCP Apps?
MCP Apps is an MCP extension, named io.modelcontextprotocol/ui. Like every
MCP extension, it is off until both sides ask for it.
- A server offers a page as a resource with a
ui://address and the typetext/html;profile=mcp-app. - A tool points to its page in
_meta.ui.resourceUri. Its_meta.ui.visibilitysays who may call it: the model, the page (app), or both. - The page's own
_meta.ui.csplists the websites it needs (for data, scripts, images, frames), and_meta.ui.permissionsthe browser features it wants (camera, microphone, location, clipboard). - The client draws the page and builds its content security policy from that list. The Gateway does not draw anything. It decides what reaches the client.
When does the Gateway relay MCP Apps?
All of these must be true:
- The client is on MCP
2026-07-28and declares the extension on the request. - The workspace allows the extension (see Control which MCP extensions a client may use). A workspace policy can still deny it.
- The MCP server declares the extension, on
2026-07-28.
Otherwise the client sees ordinary tools with no page. MCP Apps is off by
default. To allow it for one workspace, add io.modelcontextprotocol/ui on
Agents → MCP Extensions in Flightdeck. To allow it everywhere:
DVARA_MCP_GATEWAY_EXTENSIONS_ALLOWED=io.modelcontextprotocol/ui
Approve a page once, serve only that page
When a workspace allows MCP Apps, a tool sync reads each tool's page and pins its fingerprint with the tool's definition. Later:
- The Gateway serves a page only while it matches the approved fingerprint.
- If the server changes a page, the next sync holds the change for approval, the same way it holds a changed tool description. The server is quarantined until someone approves it, and the approved page stays in use until then.
- A tool whose page could not be read is not shown with a page until a sync reads it.
Turning MCP Apps on for a workspace whose tools were already approved asks for approval once for each tool that has a page: its definition gained a part.
Limit what a page may reach
A page can only ask the client to allow the websites and browser permissions on the workspace's lists. Both lists are empty by default, so a page that loads nothing from outside and asks for no permission is served, and any other is refused until you allow what it needs.
| What | Workspace setting | Fleet default |
|---|---|---|
| Websites a page may name | mcp.apps.allowed-domains | DVARA_MCP_GATEWAY_APPS_ALLOWED_DOMAINS |
| Browser permissions a page may ask for | mcp.apps.allowed-permissions | DVARA_MCP_GATEWAY_APPS_ALLOWED_PERMISSIONS |
| Largest page | none | DVARA_MCP_GATEWAY_APPS_MAX_RESOURCE_BYTES (1 MiB) |
A website is written as an origin, such as https://api.example.com, or with a
wildcard for its subdomains, such as https://*.example.com. The permissions
are camera, microphone, geolocation and clipboardWrite, the four that
MCP Apps defines.
Set the workspace's lists in one of these places:
| Where | Who |
|---|---|
| Agents → MCP Extensions in Flightdeck, the MCP Apps card | Workspace admin |
| The workspace form's MCP extensions → MCP Apps section in Flightdeck | Platform owner |
| The workspace's metadata, in a GitOps import | Whoever runs the import |
Leave a list blank to use the fleet default, or enter none to allow nothing.
All three places check what you enter and save nothing if one entry is wrong:
a website that is not an origin, a path after the host, a wildcard over a whole
top-level domain such as https://*.com, or a permission that is not one of
the four (clipboard-write is refused; the name is clipboardWrite). A change
in Flightdeck is recorded in the audit trail.
workspaces:
- name: analytics
metadata:
mcp.extensions.allowed: io.modelcontextprotocol/ui
mcp.apps.allowed-domains: https://api.weather.example, https://*.jsdelivr.net
Turn MCP Apps off for one server or one tool
Allowing MCP Apps is a workspace decision. Rules can then turn it off for one server or one tool, without touching the rest. Write one rule per line:
| Rule | Means |
|---|---|
deny github | No App for any tool of the server github |
deny github/delete_repo | No App for that one tool |
allow github/create_chart | An App for that tool, even if its server is denied |
deny * | No App on any server, unless a narrower rule allows one |
The most specific rule decides: a rule for the tool, then a rule for its
server, then *. So a tool's deny beats its server's allow, and
deny *
allow github/create_chart
keeps MCP Apps for one tool only. A rule never turns MCP Apps on where the workspace does not allow it.
A tool whose App is turned off still works as a plain tool: the client gets no
page link for it, a tool meant only for the page is not listed, and a read of
its page is refused with MCP_APP_REFUSED and the reason policy_denied. Set
the rules in the same places as the lists; in GitOps the key is
mcp.apps.rules.
See each page on the server's page
From 1.9.0 an MCP server's page in Flightdeck has an App pages card. Platform roles see it, and so do a workspace's admin and developers. For each tool with a page it shows:
- the page address and who may see the tool (
model,app); - the approved version the tool is pinned to, or not pinned when the last sync could not read the page;
- the websites and permissions the page declares, with any that the workspace's lists do not allow marked;
- the rule that decides: one for the tool, one for the server, one for every server, or none, so the workspace decides;
- whether the Gateway serves the page, and why not when it does not.
The card shows only what DVARA records about a page, never the page itself. When the workspace sets no list of its own, the card says the Gateway's default decides, because those defaults are set on the Gateway, not in Flightdeck. A server whose tools have no page says so.
What else the Gateway checks
- Personal data and prompt injection. The page's HTML and scripts go through the workspace's PII and injection scans, as any tool result does. A page sent as base64 is decoded first, so it cannot hide from them.
- App-only tools. A tool meant only for the page (visibility
app) is left out of the tool list for a client without MCP Apps, so it never reaches a model, and a call to it from such a client is refused. - Calls the page makes. A page calls tools through the client, so each call is an ordinary tool call through the Gateway: policy, budgets, approvals, scans and audit all apply.
What is recorded
| Event | When |
|---|---|
MCP_APP_REFUSED | A page or an app-only tool call is refused. The reason says why: not_negotiated, not_pinned, changed, domain_not_allowed, permission_not_allowed, too_large, wrong_mime_type, malformed, app_only_tool, policy_denied (a rule turned the App off) or policy_error (the rules could not be read). |
MCP_APP_RESOURCE_SERVED | A page was served: its approved fingerprint, the fingerprint of what was sent, whether a scan changed it, its websites and permissions, and the tools that point to it. |
MCP_APP_TEMPLATES_SYNCED | A tool sync read the pages, with each one's fingerprint, size, websites and permissions, for review. |
Limits
- MCP Apps works with clients and servers on MCP
2026-07-28. Through an older server it is not relayed. - A server that builds a new page for each request cannot be served: its fingerprint never matches.
Where to go next
- Govern MCP Apps and Tasks, the order to turn on MCP Apps, Tasks and sampling, and what to set first.
- Move MCP clients and servers to 2026-07-28