Skip to main content

MCP App Resources

The text/html;profile=mcp-app MIME type renders HTML content as an MCP (Model Context Protocol) application. The content runs in a sandboxed iframe managed by the MCP Apps SDK, which provides a standardized communication bridge for tool calls, link navigation, and host messaging.

When to Use

Use text/html;profile=mcp-app when you are building a UI that follows the MCP Apps SDK conventions, for example when:

  • You have an existing MCP App that you want to host within Symphony.
  • You want a standardized protocol for communicating between your UI and the host environment.
  • Your application is designed to work across multiple MCP-compatible hosts, not only Symphony.

For Symphony-specific functionality like NATS messaging and KV storage, use text/html+symphony or text/symphony-jsx instead.

How It Works

When Symphony receives a text/html;profile=mcp-app resource, it:

  1. Renders the content using the AppFrame component from the @mcp-ui/client SDK.
  2. Establishes a sandboxed iframe with a proxy page that manages the MCP communication channel.
  3. Creates an AppBridge instance that handles tool calls, link navigation, and messaging between the iframe and Symphony.

Registration

mcp_app = """
<!DOCTYPE html>
<html>
<head>
<title>My MCP App</title>
</head>
<body>
<div id="app">
<h1>MCP Application</h1>
<button id="openLink">Go to Settings</button>
</div>
<script type="module">
// MCP Apps SDK communication is established automatically
// by the host's AppFrame/AppBridge

document.getElementById('openLink').addEventListener('click', () => {
window.parent.postMessage({
type: 'link',
payload: { url: '/settings' }
}, '*')
})
</script>
</body>
</html>
"""

ext.add_resource("ui://my_ext/app", "text/html;profile=mcp-app", mcp_app)
ext.add_route("/my_ext", "ui://my_ext/app")
Resource app = new Resource()
.uri("ui://my_ext/app")
.mimeType(MimeType.TEXT_HTML_PROFILE_MCP_APP)
.text(appHtml);

Communication Protocol

MCP Apps communicate with the host using the standard MCP Apps SDK message types:

Navigate the host to a URL:

window.parent.postMessage({
type: 'link',
payload: { url: '/my_ext/detail' }
}, '*')

Receiving Render Data

The host sends initialization and theme data:

window.addEventListener('message', (event) => {
if (event.data?.type === 'ui-lifecycle-iframe-render-data') {
const { mode } = event.data.payload.renderData
// Apply theme: mode is 'light' or 'dark'
}
})

Lifecycle

Signal that the app is ready:

window.parent.postMessage({ type: 'ui-lifecycle-iframe-ready' }, '*')

Limitations

  • No direct NATS access—MCP Apps do not have access to the Symphony NATS connection. Communication is limited to the MCP protocol (tool calls, links, messages).
  • No KV storage—JetStream KV buckets are not available through the MCP bridge. If you need persistent storage, use text/html+symphony instead.
  • Sandbox proxy—The iframe is loaded through a sandbox proxy page, which adds a small amount of initialization overhead.

See Also