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:
- Renders the content using the
AppFramecomponent from the@mcp-ui/clientSDK. - Establishes a sandboxed iframe with a proxy page that manages the MCP communication channel.
- Creates an
AppBridgeinstance 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:
Link Navigation
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+symphonyinstead. - Sandbox proxy—The iframe is loaded through a sandbox proxy page, which adds a small amount of initialization overhead.
See Also
- User Interfaces—Overview of all resource types
- HTML + Symphony Bridge—HTML with full Symphony API access
- MCP Apps SDK documentation—MCP specification and SDK reference