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.
Navigation gérée par l’hôte
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 dewindow.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 },
};
}
);