For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
主要導覽

參考資料

ChatGPT 專屬 UI 擴充功能與中繼資料的參考資料。

從開放標準開始。使用

MCP Apps 規格

定義的共用 UI 欄位與橋接方法。 OpenAI 擴充功能為選用功能,位於 window.openai, 可在需要 ChatGPT 專屬能力時使用。

window.openai 元件橋接介面

ChatGPT 透過 window.openai 提供相容性別名與選用的 ChatGPT 擴充功能。開發新的 UI 時,只要共用規格 提供對應功能,就應使用 MCP Apps 橋接介面,僅在需要 ChatGPT 專屬能力時才使用 window.openai

如需逐步實作說明,請參閱建置 ChatGPT UI

如果工具需要確認,初始 toolInput 缺失是預期行為。 ChatGPT 不會在核准前,將需要核准的引數載入小工具的值中; 主機會等到使用者核准呼叫後,才透過 ui/notifications/tool-input 傳送這些引數。

能力

能力功能說明典型用途
狀態與資料window.openai.toolInput呼叫工具時提供的引數。對於需要核准的工具,此值可能會一直維持為 null,直到核准後主機傳送 ui/notifications/tool-input 為止。
狀態與資料window.openai.toolOutput你的 structuredContent。請保持欄位內容精簡;模型會逐字讀取。
狀態與資料window.openai.toolResponseMetadata僅供小工具使用的標準工具結果中繼資料。在 ChatGPT 中,這包括 statuscall_tool_resultmcp_tool_result,並保留完整的 MCP 結果封裝,包含隱藏的 _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 })可選擇覆寫全螢幕中顯示的外部目標。若未設定,ChatGPT 會維持預設行為,開啟元件目前的 iframe 路徑。
上下文window.openai.themewindow.openai.displayModewindow.openai.maxHeightwindow.openai.safeAreawindow.openai.viewwindow.openai.userAgentwindow.openai.locale可透過 useOpenAiGlobal 讀取或訂閱的環境訊號,用來調整視覺呈現與文案。

useOpenAiGlobal 輔助函式

許多 ChatGPT UI 專案會將 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]
  );
}

關閉 UI

呼叫 window.openai.requestClose(),要求 ChatGPT 關閉目前的 UI。

要求使用其他呈現模式

使用 window.openai.requestDisplayMode 要求以內嵌、子母畫面 或全螢幕方式呈現:

await window.openai?.requestDisplayMode({ mode: "fullscreen" });
// On mobile, picture-in-picture may be presented as fullscreen.

開啟模態視窗

使用 window.openai.requestModal 開啟由主機控制的模態視窗。請提供 同一個 MCP 伺服器註冊的另一個 UI 範本 URI,或省略 template 以開啟目前的範本:

await window.openai.requestModal({
  template: "ui://widget/checkout.html",
});

檔案 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() 傳回的檔案 已獲授權供目前的外掛程式使用。將傳回的 fileId 用於 window.openai.getFileDownloadUrl({ fileId }),或用於採用 檔案參數的工具輸入。

上傳使用者選取的檔案:

const { fileId } = await window.openai.uploadFile(file, {
  library: true,
});

選取使用者已上傳至 ChatGPT 的檔案:

if (window.openai?.selectFiles) {
  const files = await window.openai.selectFiles();
  // [{ fileId, fileName, mimeType }]
}

偵測 window.openai.selectFiles 是否可用,並在檔案庫無法使用時 改用 window.openai.uploadFile

請求暫時下載 URL:

const { downloadUrl } = await window.openai.getFileDownloadUrl({ fileId });

定義檔案輸入

若要讓 ChatGPT 將檔案傳給工具,請在 _meta["openai/fileParams"] 中列出每個頂層檔案輸入。列出的每個欄位都必須解析為檔案物件或 檔案物件陣列。

每個檔案物件的結構描述都必須宣告全部四個支援的屬性:

屬性型別properties 中宣告納入 required
download_urlstring
file_idstring
mime_typestring
file_namestring

mime_typefile_name 的值可省略,但仍必須在結構描述中 宣告這兩個屬性。若檔案結構描述有下列任一情況, 掃描工具 步驟和外掛程式提交作業都會拒絕接受: 遺漏四個屬性中的任何一個、未將 download_urlfile_id 設為必填、將任一選填屬性設為必填,或 要求填寫 download_urlfile_id 以外的屬性。你可以 宣告額外的選填屬性。

以下完整的工具描述元接受一個必填的檔案輸入:

{
  "name": "analyze_file",
  "title": "Analyze file",
  "description": "Analyzes a user-provided file without modifying it.",
  "inputSchema": {
    "type": "object",
    "$defs": {
      "OpenAIFile": {
        "type": "object",
        "properties": {
          "download_url": { "type": "string" },
          "file_id": { "type": "string" },
          "mime_type": { "type": "string" },
          "file_name": { "type": "string" }
        },
        "required": ["download_url", "file_id"],
        "additionalProperties": false
      }
    },
    "properties": {
      "file": { "$ref": "#/$defs/OpenAIFile" }
    },
    "required": ["file"]
  },
  "annotations": {
    "readOnlyHint": true,
    "openWorldHint": false,
    "destructiveHint": false
  },
  "_meta": {
    "openai/fileParams": ["file"]
  }
}

若要接受多個檔案,請將頂層欄位定義為陣列,並在 items 中使用相同的檔案物件結構描述。工具可將頂層檔案欄位設為必填, 此設定與各檔案物件內部屬性的必填設定互相獨立。

執行時,ChatGPT 會透過採用蛇形命名法的欄位傳遞檔案值:

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

ChatGPT 一律包含 download_urlfile_id,但可能省略 mime_typefile_name。當小工具需要新的暫時下載 URL 時, 請將 file_id 作為 fileId 的值, 用於 window.openai.getFileDownloadUrl({ fileId })

持久保存小工具狀態時,若希望模型在後續回合中看見圖像 ID,請使用結構化格式(modelContentprivateContentimageIds)。

由主機支援的導覽

沙盒執行環境會將 iframe 的導覽歷程同步至 ChatGPT 的 UI。使用 React Router 等標準路由 API,主機就會讓其 導覽控制項與你的 UI 保持同步。

使用 React Router 的 BrowserRouter 設定路由:

export default function PizzaListRouter() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<PizzaListPlugin />}>
          <Route path="place/:placeId" element={<PizzaListPlugin />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

透過程式碼導覽:

const navigate = useNavigate();

function openDetails(placeId: string) {
  navigate(`place/${placeId}`, { replace: false });
}

function closeDetails() {
  navigate("..", { replace: true });
}

工具描述元參數

預設情況下,工具說明應包含此處列出的欄位。

請為所有會傳回 structuredContent 的工具宣告 outputSchema。 結構描述應準確描述工具傳回的物件,讓用戶端能夠 驗證結果,並讓模型能針對後續工具呼叫進行推理。

工具描述元上的 _meta 欄位

在工具描述元上使用下列 _meta 欄位。將工具連結至 UI 範本時,請優先使用 MCP Apps 標準 鍵 _meta.ui.resourceUri。ChatGPT 支援 OpenAI 專用中繼資料,以提供相容性與選用擴充功能。

放置位置型別限制用途
_meta["securitySchemes"]工具描述元array為僅讀取 _meta 的用戶端提供向後相容的鏡像資料。
_meta.ui.resourceUri工具描述元string (URI)UI 範本的標準資源 URI。
_meta.ui.visibility工具描述元string[]預設為 ["model", "app"]控制工具是供模型、UI,還是兩者使用。app 值是 MCP Apps 協定中代表 UI 的識別碼。
_meta["openai/outputTemplate"]工具描述元string (URI)ChatGPT 中 _meta.ui.resourceUri 的 OpenAI 專用選用別名,用於相容性。
_meta["openai/profile"]工具描述元boolean選填;只有 true 才表示這是帳戶資料工具標示需經身分驗證、用於傳回目前帳戶資料的唯讀工具。實作此工具可協助使用者辨識及管理多個已連線帳戶。即使未實作此工具,使用者仍可連線至多個帳戶。請參閱支援多個帳戶
_meta["openai/widgetAccessible"]工具描述元boolean預設為 false現有 UI 整合使用的 OpenAI 專用相容性欄位;請優先使用 _meta.ui.visibility + tools/call
_meta["openai/visibility"]工具描述元stringpublic(預設)或 private現有 UI 整合使用的 OpenAI 專用相容性欄位;建議優先使用 _meta.ui.visibility
_meta["openai/toolInvocation/invoking"]工具描述器string≤ 64 個字元工具執行時顯示的簡短狀態文字。
_meta["openai/toolInvocation/invoked"]工具描述器string≤ 64 個字元工具執行完成後顯示的簡短狀態文字。
_meta["openai/fileParams"]工具描述器string[]代表檔案的頂層輸入欄位清單。每個欄位會接收 { 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.` }],
    };
  }
);

註記

若要將工具標示為「唯讀」,請在工具描述器中 使用下列 ToolAnnotations 欄位

型別是否必填備註
readOnlyHintboolean必填表示工具只會擷取或計算資訊,不會在對話之外建立、更新、刪除或傳送資料。
destructiveHintboolean必填宣告工具可能會刪除或覆寫使用者資料,讓主機知道必須先取得明確核准。
openWorldHintboolean必填宣告工具會存取公開網際網路或範圍不受限的外部實體,包括透過網頁搜尋等唯讀動作進行存取。範圍明確的私人帳戶或工作區,不會僅因託管於外部就被視為開放世界。
idempotentHintboolean選填宣告以相同引數呼叫工具不會對其環境產生額外影響。

這些提示只會影響 ChatGPT 或 Codex 向使用者說明工具呼叫的方式;伺服器仍須強制執行自己的授權邏輯。

範例:

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() },
  })
);

元件資源的 _meta 欄位

在提供元件的資源範本(registerResource)上設定這些鍵。這些鍵可協助 ChatGPT 描述及呈現已渲染的 iframe,同時避免將中繼資料洩漏給其他用戶端。

設定位置型別用途
_meta.ui.prefersBorder資源內容boolean提示主機在支援時,應將元件渲染於有邊框的卡片內。
_meta.ui.csp資源內容object建議用來設定標準小工具 CSP 欄位的中繼資料位置:connectDomainsresourceDomains,以及選填的 frameDomains
_meta.ui.domain資源內容string(來源)託管元件的專用來源(提交含 UI 的外掛程式時為必填;每個外掛程式的來源都必須唯一)。預設為 https://web-sandbox.oaiusercontent.com
_meta["openai/widgetDescription"]資源內容string元件載入時提供給模型的人類可讀摘要,可減少助理的重複說明。
_meta["openai/widgetPrefersBorder"]資源內容booleanChatGPT 中 _meta.ui.prefersBorder 的 OpenAI 專用相容性別名。
_meta["openai/widgetCSP"]資源內容object用於小工具 CSP 中繼資料的舊版 ChatGPT 相容性鍵。標準 CSP 欄位已改由 _meta.ui.csp 取代,但設定受信任的 openExternal 目的地時,仍須使用 redirect_domains
_meta["openai/widgetDomain"]資源內容string(來源)ChatGPT 中 _meta.ui.domain 的 OpenAI 專用相容性別名。

ChatGPT 支援舊版相容性鍵 _meta["openai/widgetCSP"],並使用下列 snake_case 欄位名稱:

  • connect_domains: string[]
  • resource_domains: string[]
  • frame_domains?: string[]
  • redirect_domains?: string[]。用於指定 window.openai.openExternal 重新導向目標的 ChatGPT 擴充功能。

新建 UI 時,一般建議優先使用標準的 _meta.ui.csp 物件,其支援以下欄位:

  • connectDomains: string[]。小工具可透過 fetch/XHR 連線的網域。
  • resourceDomains: string[]。提供靜態資源(圖片、字型、指令碼、樣式)的網域。
  • frameDomains?: string[]。允許嵌入 iframe 的來源清單,為選填項目。預設情況下,小工具無法呈現子框架。外掛程式可依據 iframe 政策嵌入自身網域的內容,包括現有的編輯器和管理介面。提交時必須說明使用理由,且使用 iframe 可能需要額外審查,或導致核准時間延長。

不過,_meta.ui.csp 不支援用於 window.openai.openExternal(...) 連結的 redirect_domains。若要將重新導向目標加入允許清單,仍須設定 _meta["openai/widgetCSP"].redirect_domains

工具結果

工具結果可包含以下欄位。其中需注意:

型別是否必填備註
structuredContentobject選填提供給模型與元件。若已宣告 outputSchema,則必須符合該結構描述。
contentstring 或 Content[]選填提供給模型與元件。
_metaobject選填僅傳遞給元件,模型無法看見。

只有 structuredContentcontent 會出現在對話紀錄中。主機會將 _meta 轉送給元件,讓你能將資料填入 UI,而不向模型揭露這些資料。

主機提供的工具結果中繼資料:

放置位置型別用途
_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 鍵:

用途型別備註
_meta["mcp/www_authenticate"]錯誤結果string 或 string[]用於觸發 OAuth 的 RFC 7235 WWW-Authenticate 驗證挑戰。

用戶端提供的 _meta 欄位

提供時機型別用途
_meta["openai/locale"]初始化與工具呼叫string(BCP 47)要求的語言地區設定(舊版用戶端可能會傳送 _meta["webplus/i18n"])。
_meta["openai/userAgent"]工具呼叫string選填的使用者代理提示資訊,用於分析或格式設定;會盡可能提供,但不保證可用。
_meta["openai/userLocation"]工具呼叫object概略位置提示資訊(cityregioncountrytimezonelongitudelatitude)。
_meta["openai/subject"]工具呼叫string傳送至 MCP 伺服器的匿名化使用者 ID,用於速率限制與識別
_meta["openai/session"]工具呼叫string匿名化的對話 ID,用於關聯同一個 ChatGPT 工作階段中的工具呼叫。
_meta["openai/organization"]工具呼叫string與目前 ChatGPT 組織相關聯的匿名化組織 ID(若有提供)。

操作階段的 _meta["openai/userAgent"]_meta["openai/userLocation"] 僅供參考;伺服器絕不可依據這些資訊做出授權決策,且必須能在缺少這些資訊時正常運作。請將 _meta["openai/userAgent"] 視為選用、盡力提供的中繼資料,不要依賴它來穩定識別呼叫伺服器的主機介面。

範例:

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 },
    };
  }
);