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

Referenz

Referenz für ChatGPT-spezifische UI-Erweiterungen und Metadaten.

Beginne mit dem offenen Standard. Verwende die

MCP Apps-Spezifikation

für gemeinsame UI-Felder und Bridge-Methoden. OpenAI-Erweiterungen sind optional und stehen in window.openai bereit, wenn du ChatGPT-spezifische Funktionen benötigst.

Komponenten-Bridge window.openai

ChatGPT stellt window.openai für Kompatibilitätsaliase und optionale ChatGPT-Erweiterungen bereit. Neue Benutzeroberflächen sollten die MCP Apps-Bridge verwenden, wenn die gemeinsame Spezifikation eine entsprechende Funktion bietet. Verwende window.openai dann nur für ChatGPT-spezifische Funktionen.

Schritt-für-Schritt-Anleitungen zur Implementierung findest du unter Eine ChatGPT-Benutzeroberfläche entwickeln.

Wenn dein Tool eine Bestätigung erfordert, ist es normal, dass toolInput anfangs fehlt. ChatGPT lädt genehmigungspflichtige Argumente erst nach der Genehmigung in die Widget-Werte. Der Host übermittelt sie über ui/notifications/tool-input, sobald die nutzende Person den Aufruf genehmigt.

Funktionen

FunktionFunktionsweiseTypische Verwendung
Zustand & Datenwindow.openai.toolInputArgumente, die beim Aufruf des Tools übergeben wurden. Bei genehmigungspflichtigen Tools kann dieser Wert null bleiben, bis der Host nach der Genehmigung ui/notifications/tool-input sendet.
Zustand & Datenwindow.openai.toolOutputDein structuredContent. Halte die Feldinhalte knapp; das Modell liest sie wortwörtlich.
Zustand & Datenwindow.openai.toolResponseMetadataKanonische Metadaten zum Tool-Ergebnis, die nur dem Widget zur Verfügung stehen. In ChatGPT umfassen sie status, call_tool_result und mcp_tool_result. Dabei bleibt die vollständige MCP-Ergebnisstruktur einschließlich des verborgenen Feldes _meta erhalten.
Zustand & Datenwindow.openai.widgetStateMomentaufnahme des UI-Zustands, die zwischen Rendervorgängen gespeichert bleibt.
Zustand & Datenwindow.openai.setWidgetState(state)Speichert synchron eine neue Momentaufnahme. Rufe diese Funktion nach jeder relevanten UI-Interaktion auf.
APIs der Widget-Laufzeitwindow.openai.callTool(name, args)Rufe ein anderes MCP-Tool aus dem Widget auf (entspricht den vom Modell initiierten Aufrufen).
APIs der Widget-Laufzeitwindow.openai.sendFollowUpMessage({ prompt, scrollToBottom })Fordere ChatGPT auf, eine von der Komponente verfasste Nachricht zu senden. scrollToBottom ist optional und hat den Standardwert true. Setze den Wert auf false, um automatisches Scrollen zu verhindern.
APIs der Widget-Laufzeitwindow.openai.uploadFile(file, { library?: boolean })Lade eine von der nutzenden Person ausgewählte Datei hoch und erhalte eine fileId. Übergib { library: true }, um die hochgeladene Datei auch in ihrer ChatGPT-Dateibibliothek zu speichern, sofern diese verfügbar ist.
APIs der Widget-Laufzeitwindow.openai.selectFiles()Öffne den Auswahldialog der ChatGPT-Dateibibliothek und gib die für das Plug-in autorisierten Dateien als { fileId, fileName, mimeType }[] zurück. Prüfe, ob diese Hilfsfunktion verfügbar ist, da die Dateibibliothek möglicherweise nicht allen Nutzenden zur Verfügung steht.
APIs der Widget-Laufzeitwindow.openai.getFileDownloadUrl({ fileId })Rufe eine temporäre Download-URL für eine Datei ab, die vom Widget hochgeladen, aus der Dateibibliothek ausgewählt, über Dateiparameter übergeben oder über Dateireferenzen eines Tools zurückgegeben wurde.
APIs der Widget-Laufzeitwindow.openai.requestDisplayMode(...)Fordere den PiP- oder Vollbildmodus an.
APIs der Widget-Laufzeitwindow.openai.requestModal({ params, template })Öffne einen von ChatGPT verwalteten modalen Dialog. Lass template weg, um die aktuelle Vorlage zu verwenden, oder übergib die URI einer registrierten Vorlage, um den Dialoginhalt zu wechseln.
APIs der Widget-Laufzeitwindow.openai.requestClose()Fordere ChatGPT auf, das aktuelle Widget zu schließen.
APIs der Widget-Laufzeitwindow.openai.notifyIntrinsicHeight(...)Melde dynamische Änderungen der Widget-Höhe, damit beim Scrollen keine Inhalte abgeschnitten werden.
APIs der Widget-Laufzeitwindow.openai.openExternal({ href, redirectUrl })Öffne einen geprüften externen Link im Browser der nutzenden Person. Bei genehmigten Weiterleitungszielen hängt ChatGPT standardmäßig ?redirectUrl=... an. Setze redirectUrl: false, um das zu verhindern.
APIs der Widget-Laufzeitwindow.openai.setOpenInAppUrl({ href })Überschreibe optional das im Vollbildmodus angezeigte externe Ziel. Ohne diese Angabe behält ChatGPT das Standardverhalten bei und öffnet den aktuellen iframe-Pfad der Komponente.
Kontextwindow.openai.theme, window.openai.displayMode, window.openai.maxHeight, window.openai.safeArea, window.openai.view, window.openai.userAgent, window.openai.localeUmgebungssignale, die du über useOpenAiGlobal auslesen oder abonnieren kannst, um Darstellung und Texte anzupassen.

Hilfsfunktion useOpenAiGlobal

Viele Projekte für ChatGPT-Benutzeroberflächen kapseln den Zugriff auf window.openai in kleinen Hilfsfunktionen, damit die Ansichten testbar bleiben. Die Hilfsfunktion in diesem Beispiel reagiert auf openai:set_globals-Ereignisse des Hosts und ermöglicht React-Komponenten, einen einzelnen globalen Wert zu abonnieren:

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

Benutzeroberfläche schließen

Rufe window.openai.requestClose() auf, um ChatGPT aufzufordern, die aktuelle Benutzeroberfläche zu schließen.

Einen anderen Darstellungsmodus anfordern

Verwende window.openai.requestDisplayMode, um eine Inline-, Bild-in-Bild- oder Vollbilddarstellung anzufordern:

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

Einen modalen Dialog öffnen

Verwende window.openai.requestModal, um einen vom Host gesteuerten modalen Dialog zu öffnen. Gib die URI einer anderen UI-Vorlage an, die auf demselben MCP-Server registriert ist, oder lass template weg, um die aktuelle Vorlage zu öffnen:

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

Datei-APIs

ChatGPT unterstützt Hilfsfunktionen zum Hoch- und Herunterladen von Dateien als optionale Erweiterungen von window.openai.

APIZweckHinweise
window.openai.uploadFile(file, { library?: boolean })Lade eine von der nutzenden Person ausgewählte Datei hoch und erhalte eine fileId.Übergib { library: true }, um die hochgeladene Datei auch in der ChatGPT-Dateibibliothek der nutzenden Person zu speichern, sofern diese ihr zur Verfügung steht.
window.openai.selectFiles()Öffne den Auswahldialog der Dateibibliothek für vorhandene Dateien.Gibt [{ fileId, fileName, mimeType }] zurück. Prüfe, ob diese Hilfsfunktion verfügbar ist, da die Dateibibliothek möglicherweise nicht allen zur Verfügung steht.
window.openai.getFileDownloadUrl({ fileId })Eine temporäre Download-URL für eine Datei anfordern.Funktioniert für Dateien, die vom Widget hochgeladen, aus der Dateibibliothek ausgewählt, über Dateiparameter übergeben oder über Dateireferenzen eines Werkzeugs zurückgegeben wurden.

Die ChatGPT-Dateibibliothek ist optional und steht möglicherweise nicht allen zur Verfügung. Wenn die Hilfsfunktion verfügbar ist, sind die von window.openai.selectFiles() zurückgegebenen Dateien bereits für das aktuelle Plug-in autorisiert. Verwende die zurückgegebene fileId mit window.openai.getFileDownloadUrl({ fileId }) oder in einer Werkzeugeingabe, die Dateiparameter verwendet.

Eine von der nutzenden Person ausgewählte Datei hochladen:

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

Dateien auswählen, die die nutzende Person bereits in ChatGPT hochgeladen hat:

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

Prüfe, ob window.openai.selectFiles verfügbar ist, und greife auf window.openai.uploadFile zurück, wenn die Dateibibliothek nicht verfügbar ist.

Eine temporäre Download-URL anfordern:

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

Dateieingaben definieren

Damit ChatGPT Dateien an ein Werkzeug übergeben kann, liste jede Dateieingabe auf oberster Ebene in _meta["openai/fileParams"] auf. Jedes aufgeführte Feld muss ein Dateiobjekt oder ein Array von Dateiobjekten ergeben.

Jedes Schema für Dateiobjekte muss alle vier unterstützten Eigenschaften deklarieren:

EigenschaftTypIn properties deklarierenIn required aufnehmen
download_urlstringJaJa
file_idstringJaJa
mime_typestringJaNein
file_namestringJaNein

mime_type und file_name sind optionale Werte, aber du musst ihre Eigenschaften im Schema deklarieren. Beim Schritt Werkzeuge scannen und beim Einreichen des Plug-ins wird ein Dateischema abgelehnt, wenn eine der vier Eigenschaften fehlt, wenn download_url und file_id nicht beide als erforderlich markiert sind, wenn eine der beiden optionalen Eigenschaften als erforderlich markiert ist oder wenn eine andere Eigenschaft als download_url oder file_id erforderlich ist. Du kannst zusätzliche optionale Eigenschaften deklarieren.

Dieser vollständige Werkzeugdeskriptor akzeptiert eine erforderliche Dateieingabe:

{
  "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"]
  }
}

Um mehrere Dateien zu akzeptieren, definiere das Feld auf oberster Ebene als Array und verwende dasselbe Schema für Dateiobjekte in items. Das Werkzeug kann das Dateifeld auf oberster Ebene unabhängig von den erforderlichen Eigenschaften innerhalb der einzelnen Dateiobjekte als erforderlich festlegen.

Zur Laufzeit übergibt ChatGPT Dateiwerte mit Feldnamen in Snake Case:

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

ChatGPT übergibt immer download_url und file_id; mime_type und file_name können fehlen. Verwende file_id als Wert für fileId in window.openai.getFileDownloadUrl({ fileId }), wenn ein Widget eine neue temporäre Download-URL benötigt.

Verwende beim dauerhaften Speichern des Widget-Zustands die strukturierte Form (modelContent, privateContent, imageIds), wenn das Modell in nachfolgenden Gesprächsrunden Bild-IDs sehen soll.

Vom Host unterstützte Navigation

Die Sandbox-Laufzeitumgebung spiegelt den Navigationsverlauf aus dem iframe in die ChatGPT-Oberfläche. Verwende Standard-Routing-APIs wie React Router. Der Host hält dann seine Navigationselemente mit deiner Benutzeroberfläche synchron.

Router-Setup mit BrowserRouter von React Router:

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

Programmgesteuerte Navigation:

const navigate = useNavigate();

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

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

Parameter des Werkzeugdeskriptors

Eine Werkzeugbeschreibung sollte standardmäßig die hier aufgeführten Felder enthalten.

Deklariere outputSchema für jedes Werkzeug, das structuredContent zurückgibt. Das Schema sollte das vom Werkzeug zurückgegebene Objekt exakt beschreiben, damit Clients die Ergebnisse validieren und das Modell darauf aufbauend weitere Werkzeugaufrufe planen kann.

_meta-Felder im Werkzeugdeskriptor

Verwende diese _meta-Felder im Werkzeugdeskriptor. Verwende vorzugsweise den im MCP Apps-Standard definierten Schlüssel _meta.ui.resourceUri, um ein Werkzeug mit einer UI-Vorlage zu verknüpfen. ChatGPT unterstützt OpenAI-spezifische Metadaten für Kompatibilität und optionale Erweiterungen.

SchlüsselPositionTypBeschränkungenZweck
_meta["securitySchemes"]WerkzeugdeskriptorarrayKeineSpiegelung zur Abwärtskompatibilität für Clients, die nur _meta lesen.
_meta.ui.resourceUriWerkzeugdeskriptorstring (URI)KeineStandard-Ressourcen-URI für die UI-Vorlage.
_meta.ui.visibilityWerkzeugdeskriptorstring[]Standardwert: ["model", "app"]Steuert, ob ein Werkzeug dem Modell, der Benutzeroberfläche oder beiden zur Verfügung steht. Der Wert app ist die Kennung für die Benutzeroberfläche im MCP Apps-Protokoll.
_meta["openai/outputTemplate"]Werkzeugdeskriptorstring (URI)KeineOpenAI-spezifischer optionaler Kompatibilitätsalias für _meta.ui.resourceUri in ChatGPT.
_meta["openai/profile"]WerkzeugdeskriptorbooleanOptional; nur true kennzeichnet ein Profil-ToolKennzeichnet das authentifizierte Tool ohne Schreibzugriff, das das aktuelle Profil zurückgibt. Implementiere es, damit Nutzende mehrere verbundene Konten leichter erkennen und verwalten können. Nutzende können auch ohne dieses Tool mehrere Konten verbinden. Siehe Unterstützung mehrerer Konten.
_meta["openai/widgetAccessible"]Tool-DeskriptorbooleanStandardwert: falseOpenAI-spezifisches Kompatibilitätsfeld, das bestehende UI-Integrationen verwenden; verwende vorzugsweise _meta.ui.visibility + tools/call.
_meta["openai/visibility"]Werkzeugdeskriptorstringpublic (Standard) oder privateOpenAI-spezifisches Kompatibilitätsfeld, das bestehende UI-Integrationen verwenden. Verwende bevorzugt _meta.ui.visibility.
_meta["openai/toolInvocation/invoking"]Tool-Deskriptorstring≤ 64 ZeichenKurzer Statustext während der Ausführung des Tools.
_meta["openai/toolInvocation/invoked"]Tool-Deskriptorstring≤ 64 ZeichenKurzer Statustext nach Abschluss der Tool-Ausführung.
_meta["openai/fileParams"]Tool-Deskriptorstring[]KeineListe der Eingabefelder auf oberster Ebene, die Dateien darstellen. Jedes Feld erhält { download_url, file_id, mime_type?, file_name? }.

Beispiel:

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

Annotationen

Um ein Tool als „ohne Schreibzugriff“ zu kennzeichnen, verwende die folgenden Felder von ToolAnnotations im Tool-Deskriptor:

SchlüsselTypErforderlichHinweise
readOnlyHintbooleanErforderlichGib an, dass das Tool nur Informationen abruft oder berechnet und außerhalb der Unterhaltung keine Daten erstellt, aktualisiert, löscht oder sendet.
destructiveHintbooleanErforderlichGib an, dass das Tool Nutzerdaten löschen oder überschreiben kann, damit der Host weiß, dass er zuerst eine ausdrückliche Genehmigung einholen muss.
openWorldHintbooleanErforderlichGib an, dass das Tool auf das öffentliche Internet oder einen nicht klar abgegrenzten Kreis externer Stellen zugreift. Das umfasst auch Aktionen ohne Schreibzugriff wie die Websuche. Ein abgegrenztes privates Konto oder ein abgegrenzter privater Workspace gilt nicht allein deshalb als offene Umgebung, weil es beziehungsweise er extern gehostet wird.
idempotentHintbooleanOptionalGib an, dass ein erneuter Aufruf des Tools mit denselben Argumenten keine zusätzlichen Auswirkungen auf seine Umgebung hat.

Diese Hinweise beeinflussen nur, wie ChatGPT oder Codex den Tool-Aufruf gegenüber den Nutzenden darstellt. Server müssen weiterhin ihre eigene Autorisierungslogik durchsetzen.

Beispiel:

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-Felder der Komponentenressource

Setze diese Schlüssel in der Ressourcenvorlage, die deine Komponente bereitstellt (registerResource). Sie helfen ChatGPT, den gerenderten iframe zu beschreiben und darzustellen, ohne Metadaten an andere Clients weiterzugeben.

SchlüsselPositionTypZweck
_meta.ui.prefersBorderRessourceninhaltebooleanGib an, dass die Komponente innerhalb einer umrandeten Karte dargestellt werden soll, sofern dies unterstützt wird.
_meta.ui.cspRessourceninhalteobjectBevorzugter Ort für die standardmäßigen CSP-Metadatenfelder des Widgets: connectDomains, resourceDomains und optional frameDomains.
_meta.ui.domainRessourceninhaltestring (Origin)Eigene Origin für gehostete Komponenten (beim Einreichen eines Plug-ins mit UI erforderlich; muss für jedes Plug-in eindeutig sein). Der Standardwert ist https://web-sandbox.oaiusercontent.com.
_meta["openai/widgetDescription"]RessourceninhaltestringFür Menschen lesbare Zusammenfassung, die dem Modell beim Laden der Komponente bereitgestellt wird und überflüssige Erläuterungen des Assistenten reduziert.
_meta["openai/widgetPrefersBorder"]RessourceninhaltebooleanOpenAI-spezifischer Kompatibilitätsalias für _meta.ui.prefersBorder in ChatGPT.
_meta["openai/widgetCSP"]RessourceninhalteobjectÄlterer ChatGPT-Kompatibilitätsschlüssel für CSP-Metadaten des Widgets. Die standardmäßigen CSP-Felder werden durch _meta.ui.csp ersetzt, aber redirect_domains ist weiterhin für vertrauenswürdige Ziele von openExternal erforderlich.
_meta["openai/widgetDomain"]Ressourceninhaltestring (Origin)OpenAI-spezifischer Kompatibilitätsalias für _meta.ui.domain in ChatGPT.

ChatGPT unterstützt den älteren Kompatibilitätsschlüssel _meta["openai/widgetCSP"] mit den folgenden Feldnamen in snake_case:

  • connect_domains: string[]
  • resource_domains: string[]
  • frame_domains?: string[]
  • redirect_domains?: string[]. ChatGPT-Erweiterung für Weiterleitungsziele von window.openai.openExternal.

Für neue Benutzeroberflächen wird im Allgemeinen das standardisierte Objekt _meta.ui.csp empfohlen. Es unterstützt:

  • connectDomains: string[]. Domains, die das Widget über fetch/XHR kontaktieren darf.
  • resourceDomains: string[]. Domains für statische Ressourcen (Bilder, Schriftarten, Skripte, Styles).
  • frameDomains?: string[]. Optionale Liste der für iframe-Einbettungen zulässigen Origins. Standardmäßig können Widgets keine untergeordneten Frames rendern. Plug-ins können gemäß der iframe-Richtlinie Inhalte ihrer eigenen Domain einbetten, darunter vorhandene Editoren und Verwaltungsoberflächen. Bei der Einreichung ist eine Begründung erforderlich. Die Verwendung von iframes kann ein zusätzliches Review erfordern oder die Genehmigung verzögern.

Allerdings unterstützt _meta.ui.csp kein redirect_domains für Links über window.openai.openExternal(...). Um Weiterleitungsziele auf die Zulassungsliste zu setzen, musst du weiterhin _meta["openai/widgetCSP"].redirect_domains festlegen.

Tool-Ergebnisse

Tool-Ergebnisse können die folgenden Felder enthalten. Besonders relevant sind:

SchlüsselTypErforderlichHinweise
structuredContentobjectOptionalWird dem Modell und der Komponente bereitgestellt. Muss dem deklarierten outputSchema entsprechen, sofern angegeben.
contentstring oder Content[]OptionalWird dem Modell und der Komponente bereitgestellt.
_metaobjectOptionalWird nur an die Komponente übermittelt. Für das Modell nicht sichtbar.

Nur structuredContent und content erscheinen im Gesprächsprotokoll. Der Host leitet _meta an die Komponente weiter, damit du die Benutzeroberfläche mit Daten versorgen kannst, ohne sie dem Modell offenzulegen.

Vom Host bereitgestellte Metadaten zu Tool-Ergebnissen:

SchlüsselPositionTypZweck
_meta["openai/widgetSessionId"]_meta im Tool-Ergebnis (vom Host)stringStabile ID der aktuell gemounteten Widget-Instanz. Verwende sie, um Logs und Tool-Aufrufe einander zuzuordnen, bis das Widget ausgehängt wird.

Beispiel:

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

Tool-Ergebnis mit Fehler

Um einen Fehler im Tool-Ergebnis zurückzugeben, verwende den folgenden Schlüssel in _meta:

SchlüsselZweckTypHinweise
_meta["mcp/www_authenticate"]Fehlerergebnisstring oder string[]WWW-Authenticate-Authentifizierungsaufforderungen gemäß RFC 7235 zum Auslösen von OAuth.

Vom Client bereitgestellte _meta-Felder

SchlüsselBereitgestellt beiTypZweck
_meta["openai/locale"]Initialisierung + Tool-Aufrufestring (BCP 47)Angefordertes Gebietsschema (ältere Clients senden möglicherweise _meta["webplus/i18n"]).
_meta["openai/userAgent"]Tool-AufrufestringOptionale, nach Möglichkeit bereitgestellte Angabe zum User Agent für Analysen oder Formatierung.
_meta["openai/userLocation"]Tool-AufrufeobjectUngefähre Standortangabe (city, region, country, timezone, longitude, latitude).
_meta["openai/subject"]Tool-AufrufestringAnonymisierte Nutzer-ID, die zur Durchsetzung von Ratenlimits und zur Identifizierung an MCP-Server gesendet wird
_meta["openai/session"]Tool-AufrufestringAnonymisierte Gesprächs-ID, um Tool-Aufrufe innerhalb derselben ChatGPT-Sitzung einander zuzuordnen.
_meta["openai/organization"]Tool-AufrufestringAnonymisierte Organisations-ID, die der aktuellen ChatGPT-Organisation zugeordnet ist, sofern verfügbar.

Die während der Ausführung übermittelten Werte _meta["openai/userAgent"] und _meta["openai/userLocation"] dienen nur als Hinweise. Server sollten sich bei Autorisierungsentscheidungen niemals darauf verlassen und müssen auch ohne diese Angaben funktionieren. Behandle _meta["openai/userAgent"] als optionale Metadaten, die nach Möglichkeit bereitgestellt werden, und nicht als zuverlässige Möglichkeit, die aufrufende Host-Oberfläche zu erkennen.

Beispiel:

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