当前位置: 首页 > news >正文

[MCP] iframe-based UI components

While raw HTML and Remote DOM provide great ways to create UI components, they have limitations when it comes to building truly complex, interactive applications. Raw HTML requires you to write all styling from scratch, and Remote DOM, while consistent, can be cumbersome for complex layouts and interactions. For sophisticated applications that need rich functionality, responsive design, and complex state management, you need a more powerful approach.
The solution is iframe-based UI components - embedding full web applications (though normally just widgets rather than fully routed solutions) as UI resources in MCP. This approach lets you leverage entire frameworks like React, Vue, or any other web technology to create rich, interactive interfaces that can communicate with the host application through a standardized protocol.
Example:
import { createUIResource } from '@mcp-ui/server'// Create a UI resource with an iframe for a task management widget
const resource = createUIResource({uri: `ui://task-dashboard/${Date.now()}`,content: {type: 'externalUrl',iframeUrl: 'https://example.com/widgets/task-dashboard',},encoding: 'text',uiMetadata: {'preferred-frame-size': ['1200px', '800px'],},
})

Iframe-based UI components use the externalUrl content type to embed full web applications. The uiMetadata allows you to specify preferred sizing for the iframe, giving the host application hints about optimal dimensions.

 Here's how this works in practice. When an AI assistant needs to show a complex journal viewer with interactive features like deleting entries, viewing details, and summarizing content, instead of trying to build this with raw HTML or Remote DOM, it can embed a full React application that handles all the complexity.

The iframe approach provides several key advantages:
  1. Full Framework Support: Use any web framework. If it can serve a web page, it can be embedded in an iframe.
  2. Complex State Management: Handle sophisticated application state with libraries like Redux, Zustand, or Context
  3. Rich Interactions: Build complex user interactions with full access to browser APIs
  4. Responsive Design: Leverage CSS frameworks and responsive design patterns
  5. Communication Protocol: Standardized postMessage API for iframe-to-host communication
Key message types include:
  • ui-lifecycle-iframe-ready - Notify host that iframe is ready to receive messages
  • ui-size-change - Inform host when iframe dimensions change
  • tool - Request host to execute an MCP tool
  • prompt - Request host to send a prompt to the AI
  • link - Request host to navigate to a URL

 

Request Info

In the MCP TypeScript SDK, tools can receive two arguments:
  1. args - the arguments passed to the tool
  2. extra - extra information about the request
If the tool does not have an inputSchema, the first argument will be the "extra" object which includes the requestInfo object.
When constructing iframe URLs, we need to use the origin from the request headers.
Here's how to access the origin from the request headers in the tool handler:
agent.server.registerTool('get_dashboard',{title: 'Get Dashboard',description: 'Get the dashboard for a project',// no inputSchema means the first argument to our tool handler will be the "extra" object which includes the requestInfo object},// In your tool handler, the requestInfo object contains the request headersasync ({ requestInfo }) => {const origin = requestInfo.headers['x-origin']// origin would be something like https://example.com// ...},
)
We need to add a custom x-origin header to the MCP request so our tool handler knows where to set the full iframe URL. This header is added in the worker's fetch handler before forwarding the request to the MCP server.

 

The worker sets up the custom header like this:

// In worker/index.ts
if (url.pathname === '/mcp') {// clone the request headersconst headers = new Headers(request.headers)// add the custom headerheaders.set('x-origin', url.origin)// clone the request with the new headersconst newRequest = new Request(request, { headers })return EpicMeMCP.serve('/mcp', {binding: 'EPIC_ME_MCP_OBJECT',// pass the newRequest instead of request}).fetch(newRequest, env, ctx)
}

 

This ensures that when our tool handler receives the request, it has access to the origin information needed to construct the proper iframe URL.
  • 📜 MCP UI Embeddable UI Documentation.

 

Code:

worker/index.ts

import { createRequestHandler } from 'react-router'
import { db } from './db.ts'
import { EpicMeMCP, type Props as McpProps } from './mcp/index.ts'const requestHandler = createRequestHandler(() => import('virtual:react-router/server-build'),import.meta.env.MODE,
)export default {fetch: async (request: Request,env: Env,ctx: ExecutionContext<McpProps>,) => {const url = new URL(request.url)// Tell the MCP agent what the base URL for the MCP server isif (url.pathname === '/mcp') {// Passing the base URL to the MCP agent allows it to construct URLs for UI resourcesctx.props.baseUrl = url.originreturn EpicMeMCP.serve('/mcp', {binding: 'EPIC_ME_MCP_OBJECT',}).fetch(request, env, ctx)}return requestHandler(request, {db,cloudflare: { env, ctx },})},
}export { EpicMeMCP }

 

worker/mcp/index.ts:

export class EpicMeMCP extends McpAgent<Env, State, Props> {db!: DBClientserver = new McpServer(...)async init() {this.db = dbawait initializeTools(this)await initializeResources(this)await initializePrompts(this)}// Helper function for MCP UI to get the baseUrlrequireBaseUrl() {const { baseUrl } = this.props ?? {}invariant(baseUrl, 'Unexpected: baseUrl not set on agent')return baseUrl}
}

 

worker/mcp/tools.ts

	agent.server.registerTool('view_journal',{title: 'View Journal',description: 'View the journal visually',annotations: {readOnlyHint: true,openWorldHint: false,} satisfies ToolAnnotations,},async () => {const baseUrl = agent.requireBaseUrl()// Visit our full-stack application on this pathconst iframeUrl = new URL('/ui/journal-viewer', baseUrl)return {content: [await createUIResource({uri: `ui://view-journal/${Date.now()}`,content: {type: 'externalUrl',iframeUrl: iframeUrl.toString(),},encoding: 'text',uiMetadata: {'preferred-frame-size': ['600px', '800px'],},}),],}},)

 

http://www.jsqmd.com/news/321839/

相关文章:

  • 第二届人工智能赋能数字创意设计国际学术会议(AIEDCD 2026)意大利会场
  • Python学习第一天:保留字和标识符 - 教程
  • Python 常用的内置模块
  • .NET 10 与智能体时代的架构演进:以 File-Based Apps 为核心的 C\# 生态重塑与战略分析
  • 在数字时代铸造你的“意义货币”:个人价值资产化的3步实践指南
  • 揭秘淮安多家摄影机构真实服务链,小众审美与无隐形消费如何选择
  • 2026年无锡营销策划公司推荐与排名:全域智能时代的企业增长伙伴深度评测
  • 区块链智能合约开发入门:Solidity编程指南
  • 2026年最热门的测试框架集成:软件测试从业者的专业指南
  • 鲜花:不会说明你有抑郁症4
  • 2026年无锡营销策划公司甄选指南:AI整合营销及全域增长落地全景解析
  • 2026年年会/活动/会议/会展策划公司五大优选 专业机构助力活动焕新升级
  • 2026年 广东抖音短视频运营/阿里运营/1688代运营服务商推荐榜:专业团队助力企业电商增长与AI推广
  • 2026年技巧:测试数据生成的AI优化术
  • 网络安全入门:常见攻击手段与防御策略详解
  • 2026年武汉营销策划公司排名:技术特性与成本效益评测,应对线上竞争与运营复杂痛点
  • 国标GB/T4857.5-1992跌落测试,GB/T4857.5跌落试验标准讲解
  • 2026年无锡营销策划公司推荐:制造业与B2B场景深度评价,解决增长与品牌脱节痛点
  • 临沧市英语雅思培训辅导机构推荐、2026权威出国雅思课程中心学校口碑排行榜
  • 震惊!免费工具让测试覆盖率提升150%:专业指南与实战策略
  • 使用工具Postman快速导出python接口测试脚本
  • 详细介绍:[Java EE] 网络原理(1)
  • 农业机械动力输出轴市场稳健增长,期间复合增长率(CAGR)达5.3%
  • Python自动化测试:线上流量回放
  • 移动端跨平台开发框架选型:React Native vs Flutter
  • 实现鸿蒙(HarmonyOS)视频聊天、屏幕分享
  • 教你如何使用Pytest测试框架开展性能基准测试
  • 增长迅猛!农业化学品咨询服务业预计2032年市场规模达63.47亿元
  • 2026年英国留学中介排名,值得信赖的机构全面解析
  • 临沧市英语雅思培训辅导机构推荐;2026权威出国雅思课程中心学校口碑排行榜