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

[MCP-UI] Interactive

  While iframe-based UI components provide rich, visual interfaces, they need a way to communicate back to the host application to trigger actions, request data, or request links be opened. Without this communication, iframes would be isolated islands that can't integrate with the broader application ecosystem.
The solution is the MCP UI communication protocol - a standardized way for iframe-based UI components to send messages to their host application using the postMessage API. This enables rich interactions like calling MCP tools, sending prompts to AI assistants, navigating to external links, and requesting data from the host.
// Send a link navigation request to the host
await sendLinkMcpMessage('https://www.example.com/snowboarding/')// Call an MCP tool from within the iframe
await sendMcpMessage('tool', {toolName: 'generate_haiku',params: { theme: 'quantum computing', mood: 'playful' },
})// Send a prompt to the AI assistant
await sendMcpMessage('prompt', {prompt:'Create a recipe for a fusion dish combining Japanese and Mexican cuisine',
})

The MCP UI communication protocol uses postMessage to enable secure, bidirectional communication between iframe-based UI components and their host application. This allows iframes to trigger actions in the host while maintaining security boundaries.

 

 

Here's how this works in practice. When a user clicks a "Watch Launch" button in your space exploration dashboard iframe, instead of opening a new tab or trying to navigate directly, the iframe sends a link message to the host application. The host then handles the navigation, ensuring it follows the application's routing and security policies.
The communication protocol supports several key message types:
  1. link - Request host to navigate to a URL
  2. tool - Request host to execute an MCP tool
  3. prompt - Request host to send a prompt to the AI assistant
  4. intent - Express user intent for the host to act upon
  5. notify - Notify host of side effects from user interactions
intent and notify are not used in this exercise as they are not relevant for general use AI agent apps and typically require specific server-client integration.

 

The message structure follows a consistent pattern:

type Message = {type: stringmessageId?: string // optional, used for tracking the messagepayload: Record<string, unknown>
}
  • 📜 MCP UI Embeddable UI Documentation

 

Since iframes can't directly navigate their parent window, we need to communicate with the host application. The pattern is:
  1. Generate unique ID - Create a message ID to match requests with responses
  2. Send message - Use window.parent.postMessage() with type 'link' and the URL
  3. Listen for response - Handle the 'ui-message-response' from the host
  4. Clean up - Remove event listener when done
The communication pattern looks like:
// Send request with unique ID
window.parent.postMessage({type: 'link',messageId: uniqueId,payload: { url },},'*',
)// Listen for matching response
function handleMessage(event) {if (event.data.type === 'ui-message-response' &&event.data.messageId === uniqueId) {// Handle success/error}
}
 
Code:
查看代码
export async function sendLinkMcpMessage(url: string) {const messageId = crypto.randomUUID()return new Promise((resolve, reject) => {function handleMessage(event: MessageEvent) {if (event.data.type !== 'ui-message-response') returnif (event.data.messageId !== messageId) returnconst { response, error } = event.data.payloadif (error) return reject(error)window.removeEventListener('message', handleMessage)return resolve(response)}window.parent.postMessage({ type: 'link', messageId, payload: { url } },'*',)window.addEventListener('message', handleMessage)})
}

 

utils:

查看代码
import { useEffect } from 'react'
import { type z } from 'zod'export function useMcpUiInit(rootRef: React.RefObject<HTMLDivElement | null>) {useEffect(() => {window.parent.postMessage({ type: 'ui-lifecycle-iframe-ready' }, '*')if (!rootRef.current) returnconst height = rootRef.current.clientHeightconst width = rootRef.current.clientWidthwindow.parent.postMessage({ type: 'ui-size-change', payload: { height, width } },'*',)}, [rootRef])
}type MessageOptions = { schema?: z.ZodSchema }type McpMessageReturnType<Options> = Promise<Options extends { schema: z.ZodSchema } ? z.infer<Options['schema']> : unknown
>type McpMessageTypes = {tool: { toolName: string; params: Record<string, unknown> }prompt: { prompt: string }link: { url: string }
}type McpMessageType = keyof McpMessageTypesfunction sendMcpMessage<Options extends MessageOptions>(type: 'tool',payload: McpMessageTypes['tool'],options?: Options,
): McpMessageReturnType<Options>function sendMcpMessage<Options extends MessageOptions>(type: 'prompt',payload: McpMessageTypes['prompt'],options?: Options,
): McpMessageReturnType<Options>function sendMcpMessage<Options extends MessageOptions>(type: 'link',payload: McpMessageTypes['link'],options?: Options,
): McpMessageReturnType<Options>function sendMcpMessage(type: McpMessageType,payload: McpMessageTypes[McpMessageType],options: MessageOptions = {},
): McpMessageReturnType<typeof options> {const { schema } = optionsconst messageId = crypto.randomUUID()return new Promise((resolve, reject) => {if (!window.parent || window.parent === window) {console.log(`[MCP] No parent frame available. Would have sent message:`, {type,messageId,payload,})reject(new Error('No parent frame available'))return}window.parent.postMessage({ type, messageId, payload }, '*')function handleMessage(event: MessageEvent) {if (event.data.type !== 'ui-message-response') returnif (event.data.messageId !== messageId) returnwindow.removeEventListener('message', handleMessage)const { response, error } = event.data.payloadif (error) return reject(error)if (!schema) return resolve(response)const parseResult = schema.safeParse(response)if (!parseResult.success) return reject(parseResult.error)return resolve(parseResult.data)}window.addEventListener('message', handleMessage)})
}export { sendMcpMessage }
http://www.jsqmd.com/news/361462/

相关文章:

  • 必收藏!RAG(检索增强生成)核心解析,小白程序员也能轻松吃透的大模型刚需技术
  • 2026年2月江苏水泥管/顶管/预制检查井/企口管/承插管厂家哪家好?综合选购推荐与行业深度解析 - 2026年企业推荐榜
  • 解码RS485与Modbus通信及CRC16校验
  • mbedtls之实现des mac_xor算法
  • 收藏级!大模型底层原理详解(从极简到初级,小白程序员必看)
  • 解决JSP框架的程序无法找到前端页面的问题
  • 万物皆有意义:活出个体精彩与意义信仰的实践框架
  • AbMole小讲堂丨Luteolin(木犀草素):一种具有抗炎、抗氧化、抗肿瘤活性的天然产物及其科研应用
  • 收藏备用|Java程序员转AI大模型指南:零弯路转型,解锁职场新赛道
  • 2026年玻璃极窄门TOP5品牌综合评测与选型指南 - 2026年企业推荐榜
  • 2000-2024年 上市公司-重污染行业分组数据 (+文献)
  • 如何通过 5 种有效方法同步 Android 和 Mac
  • 意义视角下的终极追问:善恶、命运与存在的深层逻辑
  • 云翼超算 全球领先自主知识产权新一代非线性数字化仿真软件
  • 2026 年 IT 转行别再选错!网络安全才是真正的黄金赛道
  • 2026年靠谱的心理咨询室仪器/心理咨询室产品系统热门型号选购指南 - 行业平台推荐
  • 如何安全轻松地出售损坏的 iPhone
  • 研究生救星!2026实测AI论文生成软件榜单,这5款直接封神
  • 揭秘CANN算子仓库:从基础算子到AIGC性能突破的实战之路
  • 新手也能上手!降AIGC软件 千笔AI VS 云笔AI,本科生专属神器
  • 协助医疗机构,提升医疗行业界面水平
  • 2026年2月四川石灰/生石灰/熟石灰/氢氧化钙/氧化钙生产厂家可靠性评估及核心推荐指南 - 2026年企业推荐榜
  • 基于vue+springboot的在线教育平台 课程作业考试系统的设计与实现
  • 2026年口碑好的伺服压力机/伺服螺旋压力机怎么选直销厂家价格参考 - 行业平台推荐
  • 零基础如何学网络安全?超详细攻略,带你从入门到精通
  • 2026年评价高的304不锈钢压力桶/大容量压力桶销售厂家推荐哪家好(真实参考) - 行业平台推荐
  • HBuilderX 安装教程(非常详细),零基础从入门到精通,一篇就够了
  • 2026年热门的恩施装修全屋定制/恩施装修设计怎么联系供应商推荐 - 行业平台推荐
  • 基于vue+springboot的电影推荐和评分系统的设计与实现
  • 2026年遂宁生态涂料/乳胶漆/石膏/腻子粉/装饰线条/防水材料行业TOP5品牌评选报告 - 2026年企业推荐榜