主导航

参考

Apps SDK 的 API 和 SDK 参考。

一次构建,多处运行。 ChatGPT 实现了用于 UI 集成的 MCP Apps 标准,该标准借鉴了我们在构建 ChatGPT Apps 时的经验。Apps SDK 的支持将持续存在——我们没有计划弃用它。请默认使用 MCP Apps 标准字段和 ui/* 网桥。OpenAI 扩展是可选的,当您需要 ChatGPT 特定功能时,它们存在于 window.openai 中。

MCP Apps UI 网桥

UI 集成使用基于 postMessage 的 JSON-RPC 2.0,并通过 ui/* 方法和通知进行通信。

常用消息

类别MCP Apps 方法/通知目的
工具输入ui/notifications/tool-input调用 UI 的最新工具输入。
工具结果ui/notifications/tool-result最新的工具结果(包括 structuredContentcontent_meta)。
工具调用tools/call直接从 UI 调用 MCP 工具。
后续消息ui/message请求宿主发布消息。
模型上下文ui/update-model-context从 UI 状态更新模型可见的上下文。

有关概览及 Apps SDK API 的映射指南,请参阅 ChatGPT 中的 MCP Apps 兼容性

window.openai 组件网桥

ChatGPT 提供 window.openai 作为 Apps SDK 兼容层和一组可选的 ChatGPT 扩展。

请参阅 构建 ChatGPT UI 获取实现指南。

能力

能力它做什么典型用法
状态与数据window.openai.toolInput调用工具时提供的参数。
状态与数据window.openai.toolOutput您的 structuredContent。请保持字段简洁;模型会逐字阅读它们。
状态与数据window.openai.toolResponseMetadata_meta 负载;只有小部件能看到它,模型永远无法看到。
状态与数据window.openai.widgetState在渲染之间持久化的 UI 状态快照。
状态与数据window.openai.setWidgetState(state)同步存储新的快照;在每次重要的 UI 交互后调用它。
小部件运行时 APIwindow.openai.callTool(name, args)从组件调用另一个 MCP 工具(镜像模型发起的调用)。
小部件运行时 APIwindow.openai.sendFollowUpMessage({ prompt, scrollToBottom })请求 ChatGPT 发布由组件撰写的消息。scrollToBottom 是可选的,默认为 true,可以设置为 false 以防止自动滚动。
小部件运行时 APIwindow.openai.uploadFile(file, { library?: boolean })上传用户选择的文件并接收 fileId。传入 { library: true } 以在可用时将上传文件同时保存到用户的 ChatGPT 文件库中。
小部件运行时 APIwindow.openai.selectFiles()打开 ChatGPT 的文件库选择器,并以 { fileId, fileName, mimeType }[] 的格式返回应用授权的文件。请对此辅助函数进行特性检测,因为文件库可能并非对所有用户可用。
小部件运行时 APIwindow.openai.getFileDownloadUrl({ fileId })检索由组件上传、从文件库选择、通过文件参数传递或由工具文件引用返回的文件临时下载 URL。
小部件运行时 APIwindow.openai.requestDisplayMode(...)请求画中画/全屏模式。
小部件运行时 APIwindow.openai.requestModal({ params, template })生成一个由 ChatGPT 拥有的模态框。省略 template 以使用当前模板,或传入已注册的模板 URI 以切换模态框内容。
小部件运行时 APIwindow.openai.requestClose()请求 ChatGPT 关闭当前小部件。
小部件运行时 APIwindow.openai.notifyIntrinsicHeight(...)上报动态小部件高度以避免滚动裁剪。
小部件运行时 APIwindow.openai.openExternal({ href, redirectUrl })在用户的浏览器中打开已审核的外部链接。对于允许重定向的目标,ChatGPT 默认会附加 ?redirectUrl=...;设置 redirectUrl: false 可跳过此操作。
小部件运行时 APIwindow.openai.setOpenInAppUrl({ href })可选地覆盖全屏“在 <App> 中打开”的目标。如果未设置,ChatGPT 将保持默认行为并打开小部件当前的 iframe 路径。
上下文window.openai.theme, window.openai.displayMode, window.openai.maxHeight, window.openai.safeArea, window.openai.view, window.openai.userAgent, window.openai.locale您可以读取(或通过 useOpenAiGlobal 订阅)的环境信号,以适配视觉效果和文案。

useOpenAiGlobal 辅助工具

许多 Apps SDK 项目将 window.openai 访问封装在小的辅助函数中,以便视图保持可测试性。此示例辅助工具会监听宿主 openai:set_globals 事件,并允许 React 组件订阅单个全局值。

export function useOpenAiGlobal<K extends keyof WebplusGlobals>(
  key: K
): WebplusGlobals[K] {
  return useSyncExternalStore(
    (onChange) => {
      const handleSetGlobal = (event: SetGlobalsEvent) => {
        const value = event.detail.globals[key];
        if (value === undefined) {
          return;
        }

        onChange();
      };

      window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal, {
        passive: true,
      });

      return () => {
        window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
      };
    },
    () => window.openai[key]
  );
}

文件 API

ChatGPT 支持作为可选 window.openai 扩展的文件上传/下载辅助工具。

API目的备注
window.openai.uploadFile(file, { library?: boolean })上传用户选择的文件并接收 fileId传入 { library: true } 以在文件库对当前用户可用时,同时将其保存到 ChatGPT 文件库。
window.openai.selectFiles()为现有文件打开文件库选择器。返回 [{ fileId, fileName, mimeType }]。请对此辅助函数进行特性检测,因为文件库可能并非对所有用户可用。
window.openai.getFileDownloadUrl({ fileId })请求文件的临时下载 URL。适用于由组件上传、从文件库选择、通过文件参数传递或由工具文件引用返回的文件。

ChatGPT 文件库是可选的,可能并非对每个用户都可用。当辅助函数可用时,从 window.openai.selectFiles() 返回的文件已获授权给当前应用。请在 window.openai.getFileDownloadUrl({ fileId }) 或使用文件参数的工具输入中使用返回的 fileId

工具文件引用使用蛇形命名法(snake case)字段

{
  "download_url": "https://...",
  "file_id": "file_...",
  "mime_type": "image/png",
  "file_name": "input.png"
}

download_urlfile_id 是必需的。mime_typefile_name 是可选的。当组件需要新的临时下载 URL 时,请将 file_id 用作 window.openai.getFileDownloadUrl({ fileId })fileId 值。

在持久化组件状态时,如果您希望模型在后续对话中看到图像 ID,请使用结构化形状(modelContent, privateContent, imageIds)。

工具描述符参数

需要更多关于这些字段的背景信息?请查看 MCP 服务器指南的高级部分

默认情况下,工具描述应包含此处列出的字段。

为任何返回 structuredContent 的工具声明 outputSchema。该模式应准确描述您的工具返回的对象,以便客户端验证结果,并使模型能够推断后续工具调用。

工具描述符上的 _meta 字段

请在工具描述符上使用这些 _meta 字段。优先使用 MCP Apps 标准键 _meta.ui.resourceUri 来关联工具与 UI 模板。ChatGPT 支持特定于 OpenAI 的元数据以实现兼容性和可选扩展。

键 (Key)位置类型限制目的
_meta["securitySchemes"]工具描述符数组为仅读取 _meta 的客户端提供向后兼容镜像。
_meta.ui.resourceUri工具描述符字符串 (URI)UI 模板的标准资源 URI。
_meta.ui.visibility工具描述符字符串数组默认 ["model", "app"]控制工具是对模型、UI (app) 还是两者都可用。
_meta["openai/outputTemplate"]工具描述符字符串 (URI)针对 ChatGPT 中 _meta.ui.resourceUri 的 OpenAI 特定可选/兼容性别名。
_meta["openai/widgetAccessible"]工具描述符boolean默认 false现有 Apps SDK 应用使用的 OpenAI 特定兼容性字段;优先使用 _meta.ui.visibility + tools/call
_meta["openai/visibility"]工具描述符stringpublic (默认) 或 private现有 Apps SDK 应用使用的 OpenAI 特定兼容性字段;优先使用 _meta.ui.visibility
_meta["openai/toolInvocation/invoking"]工具描述符string≤ 64 字符工具运行时的简短状态文本。
_meta["openai/toolInvocation/invoked"]工具描述符string≤ 64 字符工具完成后显示的简短状态文本。
_meta["openai/fileParams"]工具描述符字符串数组表示文件的顶级输入字段列表。每个字段接收 { download_url, file_id, mime_type?, file_name? }

示例

import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
import { z } from "zod";

registerAppTool(
  server,
  "search",
  {
    title: "Public Search",
    description: "Search public documents.",
    inputSchema: { q: z.string() },
    outputSchema: {
      results: z.array(
        z.object({
          id: z.string(),
          title: z.string(),
          url: z.string(),
        })
      ),
    },
    securitySchemes: [
      { type: "noauth" },
      { type: "oauth2", scopes: ["search.read"] },
    ],
    _meta: {
      securitySchemes: [
        { type: "noauth" },
        { type: "oauth2", scopes: ["search.read"] },
      ],
      ui: { resourceUri: "ui://widget/story.html" },
      // Optional compatibility alias (ChatGPT only):
      // "openai/outputTemplate": "ui://widget/story.html",
      "openai/toolInvocation/invoking": "Searching…",
      "openai/toolInvocation/invoked": "Results ready",
    },
  },
  async ({ q }) => {
    const results = await performSearch(q);

    return {
      structuredContent: { results },
      content: [{ type: "text", text: `Found ${results.length} results.` }],
    };
  }
);

注释

要将工具标记为“只读”,请在工具描述符上使用以下 注释

键 (Key)类型必需备注
readOnlyHintboolean必需表示该工具仅检索或计算信息,不会在 ChatGPT 之外创建、更新、删除或发送数据。
destructiveHintboolean必需声明该工具可能会删除或覆盖用户数据,以便 ChatGPT 在此之前请求明确批准。
openWorldHintboolean必需声明该工具发布内容或触及当前用户账户之外的内容,提示客户端在请求批准前总结其影响。
idempotentHintboolean可选声明使用相同参数调用该工具对其环境没有额外影响。

这些提示仅影响 ChatGPT 如何向用户表述工具调用;服务器必须始终强制执行自己的授权逻辑。

示例

import { z } from "zod";

server.registerTool(
  "list_saved_recipes",
  {
    title: "List saved recipes",
    description: "Returns the user’s saved recipes without modifying them.",
    inputSchema: {},
    outputSchema: {
      recipes: z.array(
        z.object({
          id: z.string(),
          title: z.string(),
        })
      ),
    },
    annotations: { readOnlyHint: true },
  },
  async () => ({
    structuredContent: { recipes: await fetchSavedRecipes() },
  })
);

需要更多关于这些字段的背景信息?请查看 MCP 服务器指南的高级部分

组件资源 _meta 字段

关于这些资源设置的更多详细信息,请参见 MCP 服务器指南的高级部分

在为您组件提供服务的资源模板 (registerResource) 上设置这些键。它们有助于 ChatGPT 描述和框架化渲染的 iframe,而不会将元数据泄漏给其他客户端。

键 (Key)位置类型目的
_meta.ui.prefersBorder资源内容boolean提示组件在支持的情况下应在带边框的卡片内渲染。
_meta.ui.csp资源内容object标准小部件 CSP 字段的首选元数据界面:connectDomainsresourceDomains 以及可选的 frameDomains
_meta.ui.domain资源内容字符串 (origin)托管组件的专用源(提交应用时必需;每个应用必须唯一)。默认值为 https://web-sandbox.oaiusercontent.com
_meta["openai/widgetDescription"]资源内容string组件加载时向模型展示的人类可读摘要,可减少冗余的助手叙述。
_meta["openai/widgetPrefersBorder"]资源内容booleanChatGPT 中针对 _meta.ui.prefersBorder 的 OpenAI 特定兼容性别名。
_meta["openai/widgetCSP"]资源内容object旧版 ChatGPT 小部件 CSP 元数据的兼容性键。标准 CSP 字段已被 _meta.ui.csp 取代,但 redirect_domains 对于受信任的 openExternal 目标仍然是必需的。
_meta["openai/widgetDomain"]资源内容字符串 (origin)ChatGPT 中针对 _meta.ui.domain 的 OpenAI 特定兼容性别名。

ChatGPT 支持具有以下 snake_case 字段名的旧版 _meta["openai/widgetCSP"] 兼容性键

  • connect_domains: string[]
  • resource_domains: string[]
  • frame_domains?: string[]
  • redirect_domains?: string[]。针对 window.openai.openExternal 重定向目标的 ChatGPT 扩展。

标准 _meta.ui.csp 对象通常更适合新应用并支持

  • connectDomains: string[]。小部件可通过 fetch/XHR 联系的域。
  • resourceDomains: string[]。静态资产(图像、字体、脚本、样式)的域。
  • frameDomains?: string[]。允许 iframe 嵌入的源列表(可选)。默认情况下,小部件不能渲染子框架;添加 frameDomains 表示启用 iframe 使用,并会触发更严格的应用审核。

然而,_meta.ui.csp 不支持 window.openai.openExternal(...) 链接的 redirect_domains。要将重定向目标加入白名单,您仍然必须设置 _meta["openai/widgetCSP"].redirect_domains

工具结果

MCP 服务器指南的高级部分提供了关于调整这些响应字段的更多指导。

工具结果可以包含以下字段。值得注意的是

键 (Key)类型必需备注
structuredContentobject可选向模型和组件展示。提供时,必须与声明的 outputSchema 匹配。
content字符串或 Content[]可选向模型和组件展示。
_metaobject可选仅发送给组件,对模型隐藏。

只有 structuredContentcontent 会出现在对话记录中。宿主将 _meta 转发给组件,以便您可以水合 UI 而无需将数据暴露给模型。

宿主提供的工具结果元数据

键 (Key)位置类型目的
_meta["openai/widgetSessionId"]工具结果 _meta (来自宿主)string当前挂载的小部件实例的稳定 ID;在小部件卸载前,使用它来关联日志和工具调用。

示例

import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
import { z } from "zod";

registerAppTool(
  server,
  "get_zoo_animals",
  {
    title: "get_zoo_animals",
    inputSchema: { count: z.number().int().min(1).max(20).optional() },
    outputSchema: {
      animals: z.array(
        z.object({
          id: z.string(),
          name: z.string(),
          species: z.string(),
        })
      ),
    },
    _meta: { ui: { resourceUri: "ui://widget/widget.html" } },
  },
  async ({ count = 10 }) => {
    const animals = generateZooAnimals(count);

    return {
      structuredContent: { animals },
      content: [{ type: "text", text: `Here are ${animals.length} animals.` }],
      _meta: {
        allAnimalsById: Object.fromEntries(
          animals.map((animal) => [animal.id, animal])
        ),
      },
    };
  }
);

错误工具结果

要在工具结果上返回错误,请使用以下 _meta

键 (Key)目的类型备注
_meta["mcp/www_authenticate"]错误结果字符串或字符串数组触发 OAuth 的 RFC 7235 WWW-Authenticate 挑战。

客户端提供的 _meta 字段

有关这些客户端提供的提示的更广泛背景,请参阅 MCP 服务器指南的高级部分

键 (Key)提供时类型目的
_meta["openai/locale"]初始化 + 工具调用字符串 (BCP 47)请求的区域设置(旧版客户端可能会发送 _meta["webplus/i18n"])。
_meta["openai/userAgent"]工具调用string用于分析或格式化的用户代理提示。
_meta["openai/userLocation"]工具调用object粗略位置提示(city, region, country, timezone, longitude, latitude)。
_meta["openai/subject"]工具调用string出于速率限制和识别目的发送给 MCP 服务器的匿名用户 ID。
_meta["openai/session"]工具调用string用于关联同一 ChatGPT 会话内工具调用的匿名对话 ID。
_meta["openai/organization"]工具调用string在可用时,与当前 ChatGPT 组织相关联的匿名组织 ID。

操作阶段的 _meta["openai/userAgent"]_meta["openai/userLocation"] 仅为提示;服务器绝不应依赖它们进行授权决策,并且必须容忍它们的缺失。

示例

import { z } from "zod";

server.registerTool(
  "recommend_cafe",
  {
    title: "Recommend a cafe",
    inputSchema: {},
    outputSchema: {
      cafes: z.array(
        z.object({
          name: z.string(),
          address: z.string(),
        })
      ),
    },
  },
  async (_args, { _meta }) => {
    const locale = _meta?.["openai/locale"] ?? "en";
    const location = _meta?.["openai/userLocation"]?.city;
    const cafes = await findNearbyCafes(location);

    return {
      content: [{ type: "text", text: formatIntro(locale, location) }],
      structuredContent: { cafes },
    };
  }
);
© . This website operates independently and is not affiliated with or endorsed by OpenAI, Inc. All brand names, logos, and trademarks are the property of their respective owners.