Interactive tool views (MCP Apps)

Show the interactive views that MCP servers attach to their tools, sandboxed in web chat and the playground, and opened from a card in Teams.

After reading this page you will be able to:
  • Know when a tool shows a view
  • Explain how a view is sandboxed
  • Control which tools the model and the view can call
  • Turn the feature off

Some MCP servers attach an interactive view to a tool: a chart, a form or an issue card. This uses the MCP Apps extension (io.modelcontextprotocol/ui). The tool names a ui:// resource in its _meta.ui.resourceUri. After the agent calls the tool, Liyagent reads that resource and shows the view to the person who asked, beside the agent's answer.

The model still reads the tool's ordinary result. The view is extra, and it never changes what the agent is told. A server without UI resources works exactly as before.

Where views appear

  • Web chat and the agent playground show the view inline, in a sandboxed frame.
  • Teams cannot run a server's HTML. The card names the view and has an Open button. It opens the view on a Liyagent page, where you sign in as usual. The button only appears when TICKETIQ_PUBLIC_BASE_URL is an https address.

A view is kept for 7 days. Only the person the run was for can open it. A view from a run that had no person (a trigger, for example) can be opened by the tenant's agent readers.

How a view is sandboxed

A view's HTML comes from a third-party server, so it never runs with your Liyagent session:

  1. The console frames a small sandbox proxy page with sandbox="allow-scripts". Without allow-same-origin, the proxy has an opaque origin, so it has no cookies, no storage and no access to the console.
  2. The proxy frames the view's own HTML, also with sandbox="allow-scripts". It adds allow-forms only when the server asks for that permission. The proxy passes messages between the view and the console.
  3. Liyagent serves the view's HTML under a Content Security Policy built from the _meta.ui.csp its server declared. By default the view cannot connect, frame or load anything outside itself. Only declared https:// origins are added. Any other entry (http:, a path, a stray directive) is dropped and recorded in the audit log.
  4. The frame URLs carry a signed token that expires after 10 minutes. Without that token, neither page is served.

Liyagent trusts a message by the window it came from, never by its origin, because both origins are opaque.

What a view can do

A view talks to Liyagent with the MCP Apps JSON-RPC messages:

MessageWhat Liyagent does
ui/initializeAnswers with the host's capabilities and context (theme, locale).
ui/notifications/initializedSends the tool's arguments (tool-input) and its result (tool-result).
tools/callRuns a tool from the same server, but only one whose visibility includes "app". The call goes through the agent's ordinary governed path: its policies, data rules and audit.
ui/open-linkOpens https:// links in a new tab. Other links are refused.
ui/messageHands the message to the page. It is never sent to the model.
ui/notifications/size-changedResizes the frame, within limits.

Any other request gets -32601 (method not found).

Tool visibility

A tool's _meta.ui.visibility lists who may call it: "model", "app" or both. Both is the default.

  • A tool without "model" is never offered to the agent's model. The run notes say why.
  • A view's tools/call for a tool without "app" is refused with 403 and audited.

Audit

ActionWhen
agent.mcp_app.viewA view was made: the server, tool, ui:// URI, the HTML's SHA-256 and the CSP that applied.
agent.mcp_app.refusedThe resource was not a valid MCP App (wrong MIME type, not ui://, too large). The agent's answer still stands.
agent.mcp_app.callA view called a tool, with the result ok, error or refused.

Turn it off

TICKETIQ_MCP_APPS_ENABLED is on by default. When it is off, Liyagent does not advertise the extension, offers every tool to the model as before, and makes no views.

API

  • GET /api/mcp-apps/{id} returns a view's sandbox URLs (each with a fresh token), the tool's arguments and its result. It needs agents.read.
  • POST /api/mcp-apps/{id}/call takes {name, arguments} and returns a CallToolResult. It needs agents.run and the Cedar invoke on the agent.