Primary navigation

Reference

API and SDK reference for Apps SDK.

window.openai component bridge

See build a custom UX

Tool descriptor parameters

By default, a tool description should include the fields listed here.

_meta fields on tool descriptor

We have also require the following _meta fields on the tool descriptor:

KeyPlacementTypeLimitsPurpose
_meta["securitySchemes"]Tool descriptorarrayBack-compat mirror for clients that only read _meta.
_meta["openai/outputTemplate"]Tool descriptorstring (URI)Resource URI for component HTML template (text/html+skybridge).
_meta["openai/widgetAccessible"]Tool descriptorbooleandefault falseAllow component→tool calls through the client bridge.
_meta["openai/toolInvocation/invoking"]Tool descriptorstring≤ 64 charsShort status text while the tool runs.
_meta["openai/toolInvocation/invoked"]Tool descriptorstring≤ 64 charsShort status text after the tool completes.

Example:

server.registerTool(
  "search",
  {
    title: "Public Search",
    description: "Search public documents.",
    inputSchema: {
      type: "object",
      properties: { q: { type: "string" } },
      required: ["q"]
    },
    securitySchemes: [
      { type: "noauth" },
      { type: "oauth2", scopes: ["search.read"] }
    ],
    _meta: {
      securitySchemes: [
        { type: "noauth" },
        { type: "oauth2", scopes: ["search.read"] }
      ],
      "openai/outputTemplate": "ui://widget/story.html",
      "openai/toolInvocation/invoking": "Searching…",
      "openai/toolInvocation/invoked": "Results ready"
    }
  },
  async ({ q }) => performSearch(q)
);

Annotations

To label a tool as “read-only”, please use the following annotation on the tool descriptor:

KeyTypeRequiredNotes
readOnlyHintbooleanOptionalSignal that the tool is read-only (helps model planning).

Example:

server.registerTool(
  "list_saved_recipes",
  {
    title: "List saved recipes",
    description: "Returns the user’s saved recipes without modifying them.",
    inputSchema: { type: "object", properties: {}, additionalProperties: false },
    annotations: { readOnlyHint: true }
  },
  async () => fetchSavedRecipes()
);

Component resource _meta fields

Set these keys on the resource template that serves your component (registerResource). They help ChatGPT describe and frame the rendered iframe without leaking metadata to other clients.

KeyPlacementTypePurpose
_meta["openai/widgetDescription"]Resource contentsstringHuman-readable summary surfaced to the model when the component loads, reducing redundant assistant narration.
_meta["openai/widgetPrefersBorder"]Resource contentsbooleanHint that the component should render inside a bordered card when supported.
_meta["openai/widgetCSP"]Resource contentsobjectDefine connect_domains and resource_domains arrays for the component’s CSP snapshot.
_meta["openai/widgetDomain"]Resource contentsstring (origin)Optional dedicated subdomain for hosted components (defaults to https://web-sandbox.oaiusercontent.com).

Example:

server.registerResource("html", "ui://widget/widget.html", {}, async () => ({
  contents: [
    {
      uri: "ui://widget/widget.html",
      mimeType: "text/html",
      text: componentHtml,
      _meta: {
        "openai/widgetDescription": "Renders an interactive UI showcasing the zoo animals returned by get_zoo_animals.",
        "openai/widgetPrefersBorder": true,
        "openai/widgetCSP": {
          connect_domains: [],
          resource_domains: ["https://persistent.oaistatic.com"],
        },
        "openai/widgetDomain": "https://chatgpt.com",
      },
    },
  ],
}));

Tool results

Tool results can contain the following fields. Notably:

KeyTypeRequiredNotes
structuredContentobjectOptionalSurfaced to the model and the component. Must match the declared outputSchema, when provided.
contentstring or Content[]OptionalSurfaced to the model and the component.
_metaobjectOptionalDelivered only to the component. Hidden from the model.

Only structuredContent and content appear in the conversation transcript. _meta is forwarded to the component so you can hydrate UI without exposing the data to the model.

Example:

server.registerTool(
  "get_zoo_animals",
  {
    title: "get_zoo_animals",
    inputSchema: { count: z.number().int().min(1).max(20).optional() },
    _meta: { "openai/outputTemplate": "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]))
      }
    };
  }
);

Error tool result

To return an error on the tool result, use the following _meta key:

KeyPurposeTypeNotes
_meta["mcp/www_authenticate"]Error resultstring or string[]RFC 7235 WWW-Authenticate challenges to trigger OAuth.

_meta fields the client provides

KeyWhen providedTypePurpose
_meta["openai/locale"]Initialize + tool callsstring (BCP 47)Requested locale (older clients may send _meta["webplus/i18n"]).
_meta["openai/userAgent"]Tool callsstringUser agent hint for analytics or formatting.
_meta["openai/userLocation"]Tool callsobjectCoarse location hint (city, region, country, timezone, longitude, latitude).
_meta["openai/subject"]Tool callsstringAnonymized user id sent to MCP servers for the purposes of rate limiting and identification

Operation-phase _meta["openai/userAgent"] and _meta["openai/userLocation"] are hints only; servers should never rely on them for authorization decisions and must tolerate their absence.

Example:

server.registerTool(
  "recommend_cafe",
  {
    title: "Recommend a cafe",
    inputSchema: { type: "object" }
  },
  async (_args, { _meta }) => {
    const locale = _meta?.["openai/locale"] ?? "en";
    const location = _meta?.["openai/userLocation"]?.city;

    return {
      content: [{ type: "text", text: formatIntro(locale, location) }],
      structuredContent: await findNearbyCafes(location)
    };
  }
);