For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navigation principale
Theme
Vue d’ensemble Documentation utilisateur de ChatGPT et Codex Cas d’usage

Référence

Référence des extensions d’interface et des métadonnées propres à ChatGPT.

Commencez par le standard ouvert. Consultez la

spécification MCP Apps

pour connaître les champs d’interface et les méthodes de passerelle communs. Les extensions OpenAI sont facultatives. Vous pouvez y accéder via window.openai si vous avez besoin de fonctionnalités propres à ChatGPT.

Passerelle window.openai pour les composants

ChatGPT fournit window.openai pour prendre en charge les alias de compatibilité et les extensions facultatives de ChatGPT. Pour toute nouvelle interface, utilisez la passerelle MCP Apps chaque fois que la spécification commune propose une fonctionnalité équivalente. Réservez window.openai aux fonctionnalités propres à ChatGPT.

Consultez le guide Créer une interface pour ChatGPT pour suivre les étapes de mise en œuvre.

Si votre outil exige une confirmation, il est normal que toolInput soit absent au départ. ChatGPT ne renseigne pas les valeurs du widget avec les arguments soumis à approbation tant que l’utilisateur n’a pas donné son accord. L’hôte les transmet via ui/notifications/tool-input dès que l’utilisateur approuve l’appel.

Fonctionnalités

Fonctionnalité API Utilisation courante
État et données window.openai.toolInput Arguments fournis lors de l’appel de l’outil. Pour les outils soumis à approbation, cette valeur peut rester null jusqu’à ce que l’hôte envoie ui/notifications/tool-input après approbation.
État et données window.openai.toolOutput Contenu de votre champ structuredContent. Gardez les champs concis, car le modèle les lit tels quels.
État et données window.openai.toolResponseMetadata Métadonnées de référence du résultat de l’outil, réservées au widget. Dans ChatGPT, elles comprennent status, call_tool_result et mcp_tool_result et conservent l’intégralité de l’enveloppe du résultat MCP, y compris le champ masqué _meta.
État et données window.openai.widgetState Instantané de l’état de l’interface conservé d’un rendu à l’autre.
État et données window.openai.setWidgetState(state) Enregistre un nouvel instantané de manière synchrone. Appelez cette méthode après chaque interaction significative avec l’interface.
API d’exécution du widget window.openai.callTool(name, args) Appelez un autre outil MCP depuis le widget, comme le ferait le modèle.
API d’exécution du widget window.openai.sendFollowUpMessage({ prompt, scrollToBottom }) Demandez à ChatGPT de publier un message rédigé par le composant. Le paramètre facultatif scrollToBottom vaut true par défaut. Définissez-le sur false pour empêcher le défilement automatique.
API d’exécution du widget window.openai.uploadFile(file, { library?: boolean }) Importez un fichier sélectionné par l’utilisateur pour obtenir un fileId. Passez { library: true } pour enregistrer également le fichier dans la bibliothèque de fichiers ChatGPT de l’utilisateur, si elle est disponible.
API d’exécution du widget window.openai.selectFiles() Ouvre le sélecteur de la bibliothèque de fichiers ChatGPT et renvoie les fichiers auxquels le plugin est autorisé à accéder sous la forme { fileId, fileName, mimeType }[]. Vérifiez que cette fonction est disponible avant de l’appeler, car certains utilisateurs n’ont pas accès à la bibliothèque.
API d’exécution du widget window.openai.getFileDownloadUrl({ fileId }) Obtenez une URL de téléchargement temporaire pour un fichier importé par le widget, sélectionné dans la bibliothèque, transmis comme paramètre de fichier ou renvoyé par un outil sous forme de référence.
API d’exécution du widget window.openai.requestDisplayMode(...) Demandez un affichage en incrustation (PiP) ou en plein écran.
API d’exécution du widget window.openai.requestModal({ params, template }) Ouvrez une fenêtre modale gérée par ChatGPT. Omettez template pour utiliser le modèle d’interface actuel ou passez l’URI d’un modèle enregistré pour changer le contenu de la fenêtre.
API d’exécution du widget window.openai.requestClose() Demandez à ChatGPT de fermer le widget actuel.
API d’exécution du widget window.openai.notifyIntrinsicHeight(...) Signalez les variations de hauteur du widget pour éviter que son contenu soit tronqué lors du défilement.
API d’exécution du widget window.openai.openExternal({ href, redirectUrl }) Ouvrez un lien externe vérifié dans le navigateur de l’utilisateur. Pour les destinations de redirection approuvées, ChatGPT ajoute ?redirectUrl=... par défaut. Définissez redirectUrl: false pour désactiver cet ajout.
API d’exécution du widget window.openai.setOpenInAppUrl({ href }) Vous pouvez remplacer la destination externe affichée en plein écran. Si vous ne la définissez pas, ChatGPT ouvre par défaut le chemin actuel de l’iframe du composant.
Contexte window.openai.theme, window.openai.displayMode, window.openai.maxHeight, window.openai.safeArea, window.openai.view, window.openai.userAgent, window.openai.locale Informations sur l’environnement que vous pouvez lire ou dont vous pouvez suivre les changements via useOpenAiGlobal pour adapter l’apparence et le texte de l’interface.

Fonction utilitaire useOpenAiGlobal

De nombreux projets d’interface pour ChatGPT encapsulent l’accès à window.openai dans de petites fonctions utilitaires pour que les vues restent faciles à tester. La fonction ci-dessous écoute les événements openai:set_globals de l’hôte et permet aux composants React de s’abonner aux changements d’une seule valeur globale.

import { useSyncExternalStore } from "react";

type WebplusGlobals = {
  widgetState: { selectedId?: string | null; [key: string]: unknown } | null;
  toolInput: Record<string, unknown>;
  toolOutput: Record<string, unknown> | null;
  theme: "light" | "dark";
};
const SET_GLOBALS_EVENT_TYPE = "openai:set_globals";

export function useOpenAiGlobal<K extends keyof WebplusGlobals>(
  key: K
): WebplusGlobals[K] {
  return useSyncExternalStore(
    (onChange) => {
      const handleSetGlobal = (event: Event) => {
        if (!(event instanceof CustomEvent)) return;
        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);
      };
    },
    () => {
      const globals = (window as Window & { openai?: WebplusGlobals }).openai;
      if (!globals) throw new Error("This hook requires a ChatGPT app host.");
      return globals[key];
    }
  );
}

Fermez l’interface

Appelez window.openai.requestClose() pour demander à ChatGPT de fermer l’interface actuelle.

Demandez un autre mode d’affichage

Utilisez window.openai.requestDisplayMode pour demander un affichage intégré, en incrustation ou en plein écran.

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

Ouvrez une fenêtre modale

Utilisez window.openai.requestModal pour ouvrir une fenêtre modale gérée par l’hôte. Fournissez l’URI d’un autre modèle d’interface enregistré par le même serveur MCP ou omettez template pour ouvrir le modèle actuel.

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

API de gestion des fichiers

ChatGPT propose des fonctions utilitaires d’importation et de téléchargement de fichiers sous forme d’extensions facultatives de window.openai.

API Fonction Remarques
window.openai.uploadFile(file, { library?: boolean }) Importe un fichier sélectionné par l’utilisateur et renvoie un fileId. Passez { library: true } pour enregistrer également le fichier dans la bibliothèque de fichiers ChatGPT de l’utilisateur actuel, si celui-ci y a accès.
window.openai.selectFiles() Ouvre le sélecteur de la bibliothèque pour choisir des fichiers existants. Renvoie [{ fileId, fileName, mimeType }]. Vérifiez que cette fonction est disponible avant de l’appeler, car certains utilisateurs n’ont pas accès à la bibliothèque.
window.openai.getFileDownloadUrl({ fileId }) Demande une URL de téléchargement temporaire pour un fichier. Fonctionne pour les fichiers importés par le widget, sélectionnés dans la bibliothèque, transmis comme paramètres de fichier ou renvoyés par un outil sous forme de références.

La bibliothèque de fichiers ChatGPT est facultative et certains utilisateurs peuvent ne pas y avoir accès. Lorsque window.openai.selectFiles() est disponible, le plugin actuel est déjà autorisé à accéder aux fichiers que cette fonction renvoie. Utilisez le fileId renvoyé avec window.openai.getFileDownloadUrl({ fileId }) ou dans une entrée d’outil qui accepte des paramètres de fichier.

Importez un fichier sélectionné par l’utilisateur.

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

Sélectionnez des fichiers que l’utilisateur a déjà importés dans ChatGPT.

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

Vérifiez que window.openai.selectFiles est disponible et utilisez window.openai.uploadFile si la bibliothèque de fichiers est indisponible.

Demandez une URL de téléchargement temporaire.

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

Définissez les entrées de type fichier

Pour permettre à ChatGPT de transmettre des fichiers à un outil, indiquez dans _meta["openai/fileParams"] chaque champ d’entrée de premier niveau qui représente un fichier. La valeur de chaque champ répertorié doit être un objet fichier ou un tableau d’objets fichier.

Le schéma de chaque objet fichier doit déclarer les quatre propriétés prises en charge.

Propriété Type À déclarer dans properties À inclure dans required
download_url string Oui Oui
file_id string Oui Oui
mime_type string Oui Non
file_name string Oui Non

Les valeurs mime_type et file_name sont facultatives, mais vous devez déclarer ces propriétés dans le schéma. À l’étape Analyser les outils comme lors de la soumission du plugin, le schéma de fichier est rejeté s’il omet l’une des quatre propriétés, s’il n’exige pas à la fois download_url et file_id, s’il rend obligatoire l’une des deux propriétés facultatives ou s’il exige une propriété autre que download_url ou file_id. Vous pouvez déclarer d’autres propriétés facultatives.

Ce descripteur d’outil complet accepte un fichier en entrée et le rend obligatoire.

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

Pour accepter plusieurs fichiers, définissez le champ de premier niveau comme un tableau et utilisez le même schéma d’objet fichier dans items. L’outil peut rendre le champ de premier niveau obligatoire indépendamment des propriétés exigées dans chaque objet fichier.

À l’exécution, ChatGPT transmet les valeurs des fichiers avec des noms de champs en snake_case.

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

ChatGPT fournit toujours download_url et file_id, mais peut omettre mime_type et file_name. Utilisez file_id comme valeur de fileId dans window.openai.getFileDownloadUrl({ fileId }) lorsqu’un widget a besoin d’une nouvelle URL de téléchargement temporaire.

Lorsque vous enregistrez l’état du widget, utilisez le format structuré (modelContent, privateContent, imageIds) si vous souhaitez que le modèle ait accès aux identifiants des images lors des échanges suivants.

L’environnement d’exécution du bac à sable répercute l’historique de navigation de l’iframe dans l’interface de ChatGPT. Utilisez les API de routage habituelles, comme celles de React Router. L’hôte synchronise ses commandes de navigation avec votre interface.

Configuration du routeur avec le composant BrowserRouter de React Router

import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  useParams,
} from "react-router-dom";
function PizzaListPlugin() {
  const { placeId } = useParams();
  return (
    <main>
      <Link to="/place/pizzeria">Example Pizzeria</Link>
      <p>{placeId ?? "Choose a place"}</p>
    </main>
  );
}

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

Navigation par programmation

import { createElement } from "react";
import { BrowserRouter, useNavigate } from "react-router-dom";
function NavigationExample() {
  const navigate = useNavigate();

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

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

  return createElement(
    "div",
    null,
    createElement(
      "button",
      { onClick: () => openDetails("pizzeria") },
      "Open details"
    ),
    createElement("button", { onClick: closeDetails }, "Close details")
  );
}

Paramètres du descripteur d’outil

Par défaut, la description d’un outil devrait inclure les champs répertoriés dans la spécification MCP.

Déclarez outputSchema pour tout outil qui renvoie structuredContent. Le schéma doit décrire précisément l’objet renvoyé par l’outil pour que les clients puissent valider les résultats et que le modèle puisse déterminer quels outils appeler ensuite.

Champs _meta du descripteur d’outil

Utilisez les champs _meta suivants dans le descripteur d’outil. Pour associer un outil à un modèle d’interface, privilégiez la clé standard _meta.ui.resourceUri de MCP Apps. ChatGPT prend en charge des métadonnées propres à OpenAI à des fins de compatibilité et pour les extensions facultatives.

Clé Emplacement Type Limites Fonction
_meta["securitySchemes"] Descripteur d’outil tableau Aucune Copie destinée à assurer la rétrocompatibilité avec les clients qui lisent uniquement _meta.
_meta.ui.resourceUri Descripteur d’outil chaîne (URI) Aucune URI standard de la ressource correspondant au modèle d’interface.
_meta.ui.visibility Descripteur d’outil string[] ["model", "app"] par défaut Détermine si le modèle, l’interface ou les deux peuvent utiliser l’outil. La valeur app est l’identifiant de l’interface dans le protocole MCP Apps.
_meta["openai/outputTemplate"] Descripteur d’outil chaîne (URI) Aucune Alias facultatif de _meta.ui.resourceUri, propre à OpenAI et pris en charge par ChatGPT à des fins de compatibilité.
_meta["openai/profile"] Descripteur d’outil booléen Facultatif. Seule la valeur true indique un outil de profil. Identifie l’outil authentifié en lecture seule qui renvoie le profil actuel. Implémentez-le pour aider les utilisateurs à reconnaître et à gérer leurs différents comptes connectés. Ils peuvent connecter plusieurs comptes même en son absence. Consultez la section Prise en charge de plusieurs comptes.
_meta["openai/widgetAccessible"] Descripteur d’outil booléen false par défaut Champ de compatibilité propre à OpenAI utilisé par les intégrations d’interface existantes. Privilégiez _meta.ui.visibility avec tools/call.
_meta["openai/visibility"] Descripteur d’outil chaîne public (par défaut) ou private Champ de compatibilité propre à OpenAI utilisé par les intégrations d’interface existantes. Privilégiez _meta.ui.visibility.
_meta["openai/toolInvocation/invoking"] Descripteur d’outil chaîne ≤ 64 caractères Court message d’état affiché pendant l’exécution de l’outil.
_meta["openai/toolInvocation/invoked"] Descripteur d’outil chaîne ≤ 64 caractères Court message d’état affiché une fois l’exécution de l’outil terminée.
_meta["openai/fileParams"] Descripteur d’outil string[] Aucune Liste des champs d’entrée de premier niveau qui représentent des fichiers. Chaque champ reçoit { download_url, file_id, mime_type?, file_name? }.

Exemple

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "docs-example", version: "1.0.0" });

// Local example data; replace this with your application data.
async function performSearch(query: string) {
  const documents = [
    {
      id: "intro",
      title: "Introduction to Acme",
      url: "https://example.com/intro",
    },
  ];
  return documents.filter((document) =>
    document.title.toLowerCase().includes(query.toLowerCase())
  );
}

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

Annotations

Pour indiquer qu’un outil est « en lecture seule », utilisez les champs de ToolAnnotations ci-dessous dans son descripteur.

Clé Type Obligatoire Remarques
readOnlyHint booléen Oui Indiquez que l’outil se limite à récupérer des informations ou à effectuer des calculs et qu’il ne crée, ne modifie, ne supprime ni n’envoie de données en dehors de la conversation.
destructiveHint booléen Oui Indiquez que l’outil peut supprimer ou écraser des données de l’utilisateur afin que l’hôte lui demande au préalable son approbation explicite.
openWorldHint booléen Oui Indiquez que l’outil accède à l’Internet public ou interagit avec des entités externes sans périmètre prédéfini, y compris par des actions en lecture seule comme la recherche web. Le seul fait qu’un compte privé ou un espace de travail au périmètre délimité soit hébergé à l’extérieur n’en fait pas un environnement ouvert.
idempotentHint booléen Non Indiquez que des appels répétés avec les mêmes arguments n’ont aucun effet supplémentaire sur l’environnement de l’outil.

Ces indications influent uniquement sur la façon dont ChatGPT ou Codex présente l’appel d’outil à l’utilisateur. Les serveurs doivent toujours appliquer leurs propres règles d’autorisation.

Exemple

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "docs-example", version: "1.0.0" });

// Local example data; replace this with your application data.
async function fetchSavedRecipes() {
  return [{ id: "pasta", title: "Tomato pasta" }];
}

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 () => ({
    content: [],
    structuredContent: { recipes: await fetchSavedRecipes() },
  })
);

Champs _meta de la ressource du composant

Définissez ces clés dans le modèle de ressource qui fournit votre composant (registerResource). Elles aident ChatGPT à décrire l’iframe et à en adapter la présentation sans exposer les métadonnées aux autres clients.

Clé Emplacement Type Fonction
_meta.ui.prefersBorder Contenu de la ressource booléen Indique qu’il est préférable d’afficher le composant dans une carte avec bordure lorsque l’interface le permet.
_meta.ui.csp Contenu de la ressource objet Emplacement recommandé pour les champs CSP standard du widget, à savoir connectDomains, resourceDomains et, à titre facultatif, frameDomains.
_meta.ui.domain Contenu de la ressource chaîne (origine) Origine dédiée aux composants hébergés. Elle est obligatoire pour soumettre un plugin doté d’une interface et doit être unique pour chaque plugin. La valeur par défaut est https://web-sandbox.oaiusercontent.com.
_meta["openai/ui"].availableDisplayModes Contenu de la ressource string[] Modes d’affichage pris en charge : inline, fullscreen et pip. Permet à ChatGPT de choisir un mode d’affichage avant de charger le composant.
_meta["openai/widgetDescription"] Contenu de la ressource chaîne Résumé en langage naturel transmis au modèle lors du chargement du composant pour éviter que l’assistant répète des informations déjà affichées.
_meta["openai/widgetPrefersBorder"] Contenu de la ressource booléen Alias de compatibilité de _meta.ui.prefersBorder, propre à OpenAI et pris en charge dans ChatGPT.
_meta["openai/widgetCSP"] Contenu de la ressource objet Ancienne clé de compatibilité ChatGPT pour les métadonnées CSP du widget. Déclarez désormais les champs CSP standard dans _meta.ui.csp. Le champ redirect_domains reste obligatoire pour les destinations de confiance de openExternal.
_meta["openai/widgetDomain"] Contenu de la ressource chaîne (origine) Alias de compatibilité de _meta.ui.domain, propre à OpenAI et pris en charge dans ChatGPT.

ChatGPT prend en charge l’ancienne clé de compatibilité _meta["openai/widgetCSP"] avec les noms de champs suivants en snake_case.

  • connect_domains : string[]
  • resource_domains : string[]
  • frame_domains? : string[]
  • redirect_domains? : string[]. Extension ChatGPT pour les destinations de redirection de window.openai.openExternal.

Pour les nouvelles interfaces, privilégiez en règle générale l’objet standard _meta.ui.csp. Il prend en charge les champs suivants.

  • connectDomains : string[]. Domaines que le widget peut contacter via fetch ou XHR.
  • resourceDomains : string[]. Domaines des ressources statiques (images, polices, scripts, styles).
  • frameDomains? : string[]. Liste facultative des origines dont le contenu peut être intégré dans une iframe. Par défaut, les widgets ne peuvent pas afficher de cadres imbriqués. Les plugins peuvent intégrer du contenu de leur propre domaine, y compris des éditeurs et des interfaces d’administration existants, conformément aux règles relatives aux iframes. Vous devez justifier l’utilisation d’iframes lors de la soumission du plugin. Leur utilisation peut nécessiter un examen supplémentaire ou allonger le délai d’approbation.

Toutefois, _meta.ui.csp ne prend pas en charge redirect_domains pour les liens ouverts avec window.openai.openExternal(...). Pour autoriser des destinations de redirection, vous devez toujours définir _meta["openai/widgetCSP"].redirect_domains.

Déclarez les modes d’affichage avant le chargement de l’interface

Définissez _meta["openai/ui"].availableDisplayModes dans le contenu de la ressource renvoyé par votre serveur. Si le composant fonctionne uniquement en plein écran, ChatGPT peut ainsi l’ouvrir directement en plein écran sans afficher d’abord un indicateur de chargement en mode intégré.

Renvoyez l’objet suivant dans le tableau contents de la ressource, en affectant le code HTML de votre composant à html.

const resource = {
  uri: "ui://widget/viewer.html",
  mimeType: "text/html;profile=mcp-app",
  text: html,
  _meta: {
    "openai/ui": {
      availableDisplayModes: ["fullscreen"],
    },
  },
};

Indiquez tous les modes pris en charge par votre composant, par exemple ["inline", "fullscreen"]. Continuez à déclarer availableDisplayModes lors de l’initialisation de MCP Apps. La déclaration côté serveur complète ce mécanisme, qui prend également en charge les modes d’affichage dynamiques.

Résultats des outils

Les résultats des outils peuvent contenir les champs définis dans la spécification MCP, notamment ceux du tableau suivant.

Clé Type Obligatoire Remarques
structuredContent objet Non Transmis au modèle et au composant. Doit respecter le schéma déclaré dans outputSchema, si celui-ci est fourni.
content chaîne ou Content[] Non Transmis au modèle et au composant.
_meta objet Non Transmis uniquement au composant. Masqué au modèle.

Seuls structuredContent et content figurent dans la transcription de la conversation. L’hôte transmet _meta au composant pour que vous puissiez alimenter l’interface en données sans les exposer au modèle.

Métadonnées du résultat de l’outil fournies par l’hôte

Clé Emplacement Type Fonction
_meta["openai/widgetSessionId"] Champ _meta du résultat de l’outil (fourni par l’hôte) chaîne Identifiant stable de l’instance du widget actuellement montée. Utilisez-le pour associer les entrées de journal aux appels d’outils jusqu’au démontage du widget.

Exemple

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "docs-example", version: "1.0.0" });

// Local example data; replace this with your application data.
function generateZooAnimals(count: number) {
  return Array.from({ length: count }, (_, index) => ({
    id: `animal-${index + 1}`,
    name: `Penguin ${index + 1}`,
    species: "Penguin",
  }));
}

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

Résultat d’outil en cas d’erreur

Pour renvoyer une erreur dans le résultat de l’outil, utilisez la clé _meta suivante.

Clé Fonction Type Remarques
_meta["mcp/www_authenticate"] Résultat d’erreur chaîne ou string[] Demandes d’authentification WWW-Authenticate conformes à la RFC 7235 pour déclencher le flux OAuth.

Champs _meta fournis par le client

Clé Moment de la transmission Type Fonction
_meta["openai/locale"] Initialisation et appels d’outils chaîne (BCP 47) Paramètres régionaux demandés (les anciens clients peuvent envoyer _meta["webplus/i18n"]).
_meta["openai/userAgent"] Appels d’outils chaîne Indication facultative sur l’agent utilisateur, fournie dans la mesure du possible à des fins d’analyse ou de mise en forme.
_meta["openai/userLocation"] Appels d’outils objet Indication approximative de la localisation (city, region, country, timezone, longitude, latitude).
_meta["openai/subject"] Appels d’outils chaîne Identifiant utilisateur anonymisé transmis aux serveurs MCP à des fins de limitation du débit et d’identification.
_meta["openai/session"] Appels d’outils chaîne Identifiant de conversation anonymisé permettant de relier les appels d’outils au sein d’une même session ChatGPT.
_meta["openai/organization"] Appels d’outils chaîne Identifiant d’organisation anonymisé associé à l’organisation ChatGPT actuelle, lorsqu’il est disponible.

Pendant l’exécution, _meta["openai/userAgent"] et _meta["openai/userLocation"] sont fournis à titre indicatif. Les serveurs doivent fonctionner en leur absence et ne doivent jamais s’y fier pour prendre une décision d’autorisation. Traitez _meta["openai/userAgent"] comme une métadonnée facultative fournie dans la mesure du possible. Elle ne permet pas d’identifier de manière fiable l’interface de l’hôte qui appelle votre serveur.

Exemple

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "docs-example", version: "1.0.0" });

// Local example data; replace this with your application data.
async function findNearbyCafes(city: unknown) {
  return city === "San Francisco"
    ? [{ name: "Example Cafe", address: "123 Example Street, San Francisco" }]
    : [];
}

function formatIntro(locale: unknown, city: unknown) {
  const location = typeof city === "string" ? city : "your area";
  return locale === "es" ? `Cafés en ${location}` : `Cafes in ${location}`;
}

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 userLocation = _meta?.["openai/userLocation"];
    const location =
      typeof userLocation === "object" &&
      userLocation !== null &&
      "city" in userLocation
        ? userLocation.city
        : undefined;
    const cafes = await findNearbyCafes(location);

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