应用

useMcpApp() 桥接

来自 iframe 内部的 data、hostContext、sendPrompt、callTool 和 openLink。

useMcpApp()

唯一的客户端组合式函数,会自动导入到每个 MCP App SFC 中。它返回 iframe 与主机通信所需的一切:

const {
  data,         // Ref<T | null>          — 从 structuredContent 水合而来,由 callTool 刷新
  loading,      // Ref<boolean>           — 在首个有效载荷到达前始终为 true
  error,        // Ref<Error | null>      — 桥接 / 传输 / 有效载荷错误
  pending,      // Ref<boolean>           — 当 callTool() 正在执行时为 true
  hostContext,  // Ref<HostContext | null> — 主题、显示模式、区域设置,…
  callTool,     // (name, params?) => Promise<T | null> — 重新调用任意 MCP 工具
  sendPrompt,   // (prompt: string) => void              — 向聊天中发送一条消息
  openLink,     // (url: string) => void                 — 请求主机打开一个 URL
} = useMcpApp<MyPayload>()

将你的有效载荷类型作为泛型传入,以便下游获得完整推断。

dataloading

当处理程序返回 structuredContent 时,data首次渲染时就已经填充loading 初始为 true,在首个有效载荷到达后变为 false。对于进行中的 callTool() 刷新,请使用 pending

<template>
  <section v-if="loading" class="skeleton" />
  <section v-else-if="data" class="content">
    {{ data.swatches.length }} 个色板,来自 {{ data.base }}
  </section>
</template>

hostContext

ui/initialize 握手期间,主机会向 iframe 传递一个上下文对象。用它来适配深色模式、全屏或固定的 iframe 尺寸

interface HostContext {
  theme?: 'light' | 'dark'
  displayMode?: 'inline' | 'fullscreen' | 'pip'
  containerDimensions?: { width?: number, height?: number, maxWidth?: number, maxHeight?: number }
  locale?: string
  timeZone?: string
  platform?: 'web' | 'desktop' | 'mobile'
}
<script setup lang="ts">
const { hostContext } = useMcpApp()
const isDark = computed(() => hostContext.value?.theme === 'dark')
const isFullscreen = computed(() => hostContext.value?.displayMode === 'fullscreen')
</script>

<template>
  <main :data-theme="isDark ? 'dark' : 'light'" :data-mode="isFullscreen ? 'fullscreen' : 'inline'">
  </main>
</template>
hostContext 在第一次绘制时为 null,会在握手后填充(通常 <50 ms)。请始终在模板中使用回退值。

sendPrompt(prompt) — 后续跟进

将消息发送到聊天中,就像用户亲自输入了一样。然后 LLM 会像处理任何其他请求一样路由它——包括调用另一个 MCP App:

<button @click="sendPrompt(`Use ${swatch.name} (${swatch.hex}) as the brand colour.`)">
  使用此颜色
</button>

主机会像用户输入了一样接收该提示。LLM 可能会回复、调用另一个工具,或者响应时打开另一个 MCP App——应用到应用的工作流 就由这个原语自然实现。

后续跟进是尽力而为。实现了 ui/message 的主机可以顺畅转发提示。ChatGPT 会确认请求,但并不总是将下一个工具以内联方式重新渲染(这是上游限制)。

callTool(name, params) — 原地刷新

从 iframe 中重新调用任意 MCP 工具。结果会自动替换 data

<script setup lang="ts">
const { data, pending, callTool } = useMcpApp<PalettePayload>()

async function refresh(base: string) {
  await callTool('color-picker', { base })
}
</script>

可将其用于筛选、分页、刷新按钮——任何无需完整聊天往返即可更改查询的操作。

openLink(url)

沙盒 iframe 不能打开窗口。openLink 会请求主机替你完成此操作(例如在新的浏览器标签页中打开预订确认页面):

<button @click="openLink(`https://example.com/colors/${swatch.hex.slice(1)}`)">
  在网页中查看
</button>
如果你还需要从 iframe 中 fetch() 该目标,请将目标主机添加到 csp.connectDomains