Lots of people might come to MCP Apps w/ an existing iframe embed, not wanting to reimplement everything.
But some hosts might balk at allowing nested external iframes (which can't be audited easily).
Here is a rough suggestion on how to turn an iframe embed app (supposedly taking its parameters from the URL query params) into an MCP App (which just takes its parameters from the tool outputs, or inputs).
Taking the following embed.ts (normally iframed as https://myhost.com/embed.html?q=some+search), we can have initialization logic that fetches params from the MCP App's tool results instead of from its query params, if we're not iframed.
(Jan 27th 2026: edited to match 1.x APIs)
import{App}from"@modelcontextprotocol/ext-apps";constisMcpApp=()=>window.location.host!=='myhost.com';// Returns the query params when embedded, or the MCP tool result' structuredContentasyncfunctiongetParameters(): Promise<Record<string,string>>{if(isMcpApp()){constapp=newMcpApp({name: 'Our App',version: ''});awaitapp.connect();returnnewPromise(resolve=>{app.ontoolresult=({structuredContent})=>resolve(structuredContent);// Note: inputs are available too.});}else{returnObject.fromEntries([...newURL(window.location.href).searchParams])}}asyncfunctionmain(){const{q: query}=awaitgetParameters();
...
}main().catch(...)import{registerAppTool,registerAppResource}from"@modelcontextprotocol/ext-apps/server";import{McpServer}from"@modelcontextprotocol/sdk/...";import{StdioServerTransport}from"@modelcontextprotocol/sdk/...";constserver=newMcpServer({name: 'Example Server',version: '1.0.0'});constuiHtml=awaitfetch("dist/embed.html").then(r=>t.text());constresourceUri='ui://example';registerAppResource(server,{uri: resourceUri,
....,content: uiHtml,_meta: {ui: {csp: {connectDomains: ['api.myhost.com']}}}})registerAppTool(server,'show-embed',{inputSchema: z.object({q: z.string()}).shape,_meta: {ui: { resourceUri }}},({message})=>{return{content: [],structuredContent: {q: 'the query'`}}})server.server.connect(newStdioServerTransport()).then(()=>console.error('Server is running');
Lots of people might come to MCP Apps w/ an existing iframe embed, not wanting to reimplement everything.
But some hosts might balk at allowing nested external iframes (which can't be audited easily).
Here is a rough suggestion on how to turn an iframe embed app (supposedly taking its parameters from the URL query params) into an MCP App (which just takes its parameters from the tool outputs, or inputs).
Taking the following
embed.ts(normally iframed ashttps://myhost.com/embed.html?q=some+search), we can have initialization logic that fetches params from the MCP App's tool results instead of from its query params, if we're not iframed.(Jan 27th 2026: edited to match 1.x APIs)