ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MCPApp 新规范实战:用 iframe + PostMessage 在 Vue 中接入 TaoToken 富媒体交互

MCPApp 新规范实战:用 iframe + PostMessage 在 Vue 中接入 TaoToken 富媒体交互 1. 为什么要在 Vue 里折腾 MCPApp 的 iframe 通信MCPApp 是 MCP 协议在 2025 年 7 月 28 日那版规范里正式落地的官方 UI 扩展核心思路一句话就能说清MCP Server 的工具不再只返回一段文本而是可以额外附带一个交互式 HTML 界面由 Host 客户端塞进沙盒 iframe 里渲染。对用户来说图表、仪表盘、视频播放器、多步骤表单这些富媒体内容可以直接在对话窗口里操作不用每换一个视图就重新发一轮对话。但真到落地环节坑基本都集中在两件事上一是 iframe 里的前端页面怎么和宿主 Vue 应用通信二是这个页面里的模型调用怎么统一走一条可控的 Key/API 通道。前者靠 PostMessage 解决后者我用 TaoToken 的 OpenAI 兼容接口来兜底一个 Key 就能覆盖对话、富媒体生成这类请求省得在 iframe 里再散落一堆密钥。这篇适合已经在写 Vue、想接 MCPApp 富媒体交互的前端同学也适合做 MCP Server 想验证 UI 资源渲染的后端同学。下面给的是能直接复制跑的骨架Vue 侧 iframe 容器 PostMessage 双向通信 鉴权配置 本地验证步骤最后附上我踩过的几个典型报错。2. TaoToken 前置把 Key 和 API 通道准备好MCPApp 的 iframe 页面里如果要调模型最忌讳把 Key 硬编码进前端。我的做法是让 iframe 只负责发请求意图真正的模型调用走宿主 Vue 应用转发或者 iframe 内用短期票据。不管哪种底层都统一指向 TaoToken 的 API 通道。先拿到 Key打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制出来形如sk-xxxx。这个 Key 只在服务端或宿主侧使用不要写进 iframe 的 HTML 里。TaoToken 的接口是 OpenAI 兼容格式Base URL 用https://taotoken.net/api对话补全的路径就是/v1/chat/completions。你可以先用 curl 验证通道是否通curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: gpt-4o-mini, messages: [{role: user, content: 只回复两个字通了}] }返回里能看到choices[0].message.content就说明通道没问题。这一步很关键因为后面 iframe 里的富媒体请求如果失败你要能快速区分是通信问题还是 Key/通道问题。如果你后面要做长期编码或 Agent 类场景可以顺带看下 Coding Planhttps://taotoken.net/coding-plan 它更适合高频调用只是验证模型能力的话直接用模型对话页 https://taotoken.net/models 更快。3. 可复制配置Vue 侧 iframe 容器与 PostMessage 骨架MCPApp 的通信是双通道MCP 协议通道负责工具调用PostMessage 通道负责 UI 交互。我们这里聚焦后者因为 iframe 渲染和父子通信是前端最容易卡住的地方。3.1 父窗口Vue 组件里创建 iframe 并监听消息先写一个 Vue 3 的组合式组件负责挂载 iframe、发送初始化消息、接收子窗口回传。template div classmcp-app-host iframe refappFrame :srcappUrl sandboxallow-scripts allow-same-origin stylewidth: 100%; height: 480px; border: 1px solid #e5e7eb; border-radius: 8px loadonFrameLoad / p v-iflastMessage子窗口最新消息{{ lastMessage }}/p /div /template script setup import { ref, onMounted, onBeforeUnmount } from vue const appFrame ref(null) const appUrl ref(/mcp-app/index.html) // 你的 MCPApp 页面地址 const lastMessage ref() // 只接受来自我们 iframe 的消息避免其他窗口伪造 const ALLOWED_ORIGIN window.location.origin function onFrameLoad() { // iframe 加载完成后父窗口主动发一次握手 appFrame.value?.contentWindow?.postMessage( { type: host:init, payload: { theme: light, locale: zh-CN } }, ALLOWED_ORIGIN ) } function handleMessage(event) { if (event.origin ! ALLOWED_ORIGIN) return const { type, payload } event.data || {} if (type app:ready) { lastMessage.value 子窗口已就绪 } if (type app:request-model) { // 子窗口请求模型能力父窗口转发到 TaoToken callModel(payload).then((result) { appFrame.value?.contentWindow?.postMessage( { type: host:model-result, payload: result }, ALLOWED_ORIGIN ) }) } } async function callModel(payload) { const res await fetch(/api/taotoken/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload) }) return res.json() } onMounted(() window.addEventListener(message, handleMessage)) onBeforeUnmount(() window.removeEventListener(message, handleMessage)) /script这里有两个细节值得强调。第一sandbox属性我保留了allow-scripts allow-same-origin因为 MCPApp 的 View 需要跑脚本同时要能访问自身资源但不要加allow-top-navigation否则子窗口能劫持父页面。第二handleMessage里第一行就校验event.origin这是防伪造消息的基本功别省。3.2 子窗口MCPApp 页面里的握手与请求iframe 里的页面也就是 MCP Server 通过ui://资源下发的 HTML需要主动告诉父窗口自己准备好了并在需要模型能力时发请求。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / titleMCPApp View/title /head body div idchart/div script const HOST_ORIGIN window.location.origin // 通知父窗口我准备好了 window.parent.postMessage({ type: app:ready }, HOST_ORIGIN) // 监听父窗口的初始化与结果消息 window.addEventListener(message, (event) { if (event.origin ! HOST_ORIGIN) return const { type, payload } event.data || {} if (type host:init) { console.log(收到宿主初始化参数, payload) // 这里可以按主题、语言渲染 UI } if (type host:model-result) { renderChart(payload) } }) // 需要模型能力时向父窗口发请求而不是自己拿 Key 调 function requestModel(prompt) { window.parent.postMessage( { type: app:request-model, payload: { prompt } }, HOST_ORIGIN ) } function renderChart(data) { document.getElementById(chart).textContent 图表数据 JSON.stringify(data) } // 模拟一次请求 requestModel(生成一组销售趋势数据) /script /body /html这套骨架的关键在于职责分离iframe 不碰 Key只发意图父窗口持有鉴权逻辑统一走 TaoToken。这样即使 iframe 内容来自 MCP Server也不会泄露凭证。3.3 服务端转发把请求打到 TaoToken父窗口里那个/api/taotoken/chat需要你后端实现Node 示例// server.js (Express) import express from express const app express() app.use(express.json()) app.post(/api/taotoken/chat, async (req, res) { const r await fetch(https://taotoken.net/api/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${process.env.TAOTOKEN_API_KEY} }, body: JSON.stringify({ model: gpt-4o-mini, messages: [{ role: user, content: req.body.prompt }] }) }) const data await r.json() res.json(data.choices?.[0]?.message?.content ?? ) }) app.listen(3000)Key 放在环境变量TAOTOKEN_API_KEY里前端永远看不到。4. 验证请求本地跑通 PostMessage 往返与富媒体渲染配置写完别急着接真实 MCP Server先在本地把通信链路验证通。我一般分三步。第一步起一个静态服务托管 iframe 页面比如npx serve public确认/mcp-app/index.html能单独打开。第二步在 Vue 页面里打开控制台观察消息流。正常顺序是iframeload触发父窗口发host:init子窗口收到后打印初始化参数同时子窗口发app:ready父窗口把lastMessage更新为「子窗口已就绪」。如果lastMessage一直是空说明消息没到先查 origin 是否一致。第三步验证模型往返。子窗口调用requestModel后父窗口应该发出/api/taotoken/chat请求返回结果再通过host:model-result回传子窗口的renderChart被触发。你可以在 Network 面板看到对taotoken.net/api/v1/chat/completions的请求状态 200 且响应里有内容就说明整条链路通了。富媒体渲染的验证更直观把renderChart换成真实的图表库比如 ECharts数据灌进去后 iframe 里应该出现图表。如果图表不显示但数据到了问题在渲染层如果数据没到回到第二步查消息。5. 本篇常见错排查报错一Blocked a frame with origin ... from accessing a cross-origin frame这是同源策略不是 PostMessage 的问题。检查 iframe 的src和父页面是否同源。如果 MCPApp 页面部署在不同域名postMessage的第二个参数要写子窗口的真实 origin不能再用window.location.origin。同时子窗口发消息时targetOrigin也要写父窗口的真实 origin。报错二消息发出去了但收不到九成是event.origin校验写错。父窗口收到的是子窗口的 origin子窗口收到的是父窗口的 origin两边别写反。调试时可以先临时打印event.origin确认。报错三iframe 里请求 TaoToken 返回 401说明 Key 没带上或带错了。检查后端转发时Authorization头是不是Bearer sk-xxx以及环境变量有没有加载。注意不要在 iframe 前端直接调https://taotoken.net/api那样 Key 会暴露而且容易触发跨域。报错四sandbox太严导致脚本不执行如果 iframe 白屏且控制台报脚本被阻止检查sandbox是否漏了allow-scripts。但别为了省事直接去掉sandbox那等于放弃隔离MCPApp 的安全模型就废了。报错五MCP Server 下发的 HTML 里资源 404MCPApp 的 UI 资源通过ui://协议注册Host 读取后渲染。如果你本地直接拿文件路径测相对路径的资源会找不到。确认资源路径是相对于 iframe 页面本身的或者用绝对路径。6. 继续往下走把通道和文档用起来通信骨架跑通后下一步就是接真实的 MCP Server 工具调用。这时候建议先把 API Key 管理好不同环境用不同 Key方便排查和限额https://taotoken.net/api-keys 。接入细节和参数说明看文档https://taotoken.net/doc 里面有 OpenAI 兼容接口的完整字段。如果你只是想先验证某个模型在富媒体场景下的输出质量直接去模型对话页试https://taotoken.net/models 。而如果你要做的是长期编码、Agent 编排这类高频场景Coding Plan 会更划算https://taotoken.net/coding-plan 。最后提醒一句MCPApp 的 iframe 通信看着简单真正难的是边界处理origin 校验、消息类型收敛、错误回传。我建议你在handleMessage里加一个switch白名单只处理已知的type未知消息直接丢弃这样后期加功能时不会因为一条脏消息把整个宿主搞崩。
返回列表