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

Referencia

Referencia de extensiones de interfaz y metadatos específicos de ChatGPT.

Empieza por el estándar abierto. Usa la

especificación de MCP Apps

para los campos de interfaz y los métodos del puente compartidos. Las extensiones de OpenAI son opcionales y están en window.openai para cuando necesites capacidades específicas de ChatGPT.

Puente de componentes window.openai

ChatGPT proporciona window.openai para alias de compatibilidad y extensiones opcionales de ChatGPT. Las interfaces nuevas deben usar el puente de MCP Apps siempre que la especificación compartida ofrezca un equivalente, y usar window.openai solo para capacidades específicas de ChatGPT.

Consulta Crear una interfaz de ChatGPT para ver guías de implementación paso a paso.

Si tu herramienta requiere confirmación, considera normal que toolInput no esté disponible al principio. ChatGPT no carga en los valores del widget los argumentos que requieren aprobación antes de que esta se otorgue; el host los entrega a través de ui/notifications/tool-input una vez que el usuario aprueba la llamada.

Capacidades

CapacidadQué haceUso habitual
Estado y datoswindow.openai.toolInputArgumentos proporcionados al invocar la herramienta. En las herramientas que requieren aprobación, este valor puede permanecer en null hasta que el host envíe ui/notifications/tool-input después de la aprobación.
Estado y datoswindow.openai.toolOutputTu structuredContent. Mantén los campos concisos; el modelo los lee tal como están.
Estado y datoswindow.openai.toolResponseMetadataMetadatos canónicos del resultado de la herramienta, exclusivos del widget. En ChatGPT, esto incluye status, call_tool_result y mcp_tool_result, y conserva la estructura completa del resultado de MCP, incluido el campo oculto _meta.
Estado y datoswindow.openai.widgetStateInstantánea del estado de la interfaz que se conserva entre renderizados.
Estado y datoswindow.openai.setWidgetState(state)Guarda una nueva instantánea de forma síncrona; llama a este método después de cada interacción significativa con la interfaz.
API del entorno de ejecución del widgetwindow.openai.callTool(name, args)Invoca otra herramienta MCP desde el widget (replica las llamadas iniciadas por el modelo).
API del entorno de ejecución del widgetwindow.openai.sendFollowUpMessage({ prompt, scrollToBottom })Pide a ChatGPT que publique un mensaje redactado por el componente. scrollToBottom es opcional, su valor predeterminado es true y puede establecerse en false para evitar el desplazamiento automático.
API del entorno de ejecución del widgetwindow.openai.uploadFile(file, { library?: boolean })Carga un archivo seleccionado por el usuario y recibe un fileId. Pasa { library: true } para guardar también el archivo cargado en la biblioteca de archivos de ChatGPT del usuario cuando esa biblioteca esté disponible.
API del entorno de ejecución del widgetwindow.openai.selectFiles()Abre el selector de la biblioteca de archivos de ChatGPT y devuelve los archivos autorizados para el complemento como { fileId, fileName, mimeType }[]. Comprueba si esta función auxiliar está disponible, ya que la biblioteca de archivos podría no estar disponible para todos los usuarios.
API del entorno de ejecución del widgetwindow.openai.getFileDownloadUrl({ fileId })Obtén una URL de descarga temporal de un archivo cargado por el widget, seleccionado de la biblioteca de archivos, pasado mediante parámetros de archivo o devuelto mediante referencias a archivos de herramientas.
API del entorno de ejecución del widgetwindow.openai.requestDisplayMode(...)Solicita los modos PiP o pantalla completa.
API del entorno de ejecución del widgetwindow.openai.requestModal({ params, template })Abre una ventana modal administrada por ChatGPT. Omite template para usar la plantilla actual o pasa el URI de una plantilla registrada para cambiar el contenido de la ventana modal.
API del entorno de ejecución del widgetwindow.openai.requestClose()Pide a ChatGPT que cierre el widget actual.
API del entorno de ejecución del widgetwindow.openai.notifyIntrinsicHeight(...)Informa las alturas dinámicas del widget para evitar que se recorte el contenido al desplazarse.
API del entorno de ejecución del widgetwindow.openai.openExternal({ href, redirectUrl })Abre un enlace externo verificado en el navegador del usuario. Para los destinos de redirección aprobados, ChatGPT agrega ?redirectUrl=... de forma predeterminada; configura redirectUrl: false para omitirlo.
API del entorno de ejecución del widgetwindow.openai.setOpenInAppUrl({ href })Reemplaza de forma opcional el destino externo que se muestra en pantalla completa. Si no se configura, ChatGPT conserva el comportamiento predeterminado y abre la ruta actual del iframe del componente.
Contextowindow.openai.theme, window.openai.displayMode, window.openai.maxHeight, window.openai.safeArea, window.openai.view, window.openai.userAgent, window.openai.localeSeñales del entorno que puedes leer o a las que puedes suscribirte mediante useOpenAiGlobal para adaptar los elementos visuales y los textos.

Función auxiliar useOpenAiGlobal

Muchos proyectos de interfaz de ChatGPT encapsulan el acceso a window.openai en pequeñas funciones auxiliares para que las vistas se puedan seguir probando. Esta función auxiliar de ejemplo escucha los eventos openai:set_globals del host y permite que los componentes de React se suscriban a un único valor global:

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

Cerrar la interfaz

Llama a window.openai.requestClose() para pedirle a ChatGPT que cierre la interfaz actual.

Solicitar otro modo de presentación

Usa window.openai.requestDisplayMode para solicitar la presentación integrada, de imagen en imagen o en pantalla completa:

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

Abrir una ventana modal

Usa window.openai.requestModal para abrir una ventana modal controlada por el host. Proporciona el URI de otra plantilla de interfaz registrada por el mismo servidor MCP u omite template para abrir la plantilla actual:

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

API de archivos

ChatGPT admite funciones auxiliares de carga y descarga de archivos como extensiones opcionales de window.openai.

APIPropósitoNotas
window.openai.uploadFile(file, { library?: boolean })Carga un archivo seleccionado por el usuario y recibe un fileId.Pasa { library: true } para guardar también el archivo cargado en la biblioteca de archivos de ChatGPT del usuario cuando esa biblioteca esté disponible para el usuario actual.
window.openai.selectFiles()Abre el selector de la biblioteca de archivos para elegir archivos existentes.Devuelve [{ fileId, fileName, mimeType }]. Comprueba si esta función auxiliar está disponible, ya que la biblioteca de archivos podría no estar disponible para todos los usuarios.
window.openai.getFileDownloadUrl({ fileId })Solicita una URL de descarga temporal para un archivo.Funciona con archivos cargados por el widget, seleccionados de la biblioteca de archivos, pasados mediante parámetros de archivo o devueltos mediante referencias a archivos de herramientas.

La biblioteca de archivos de ChatGPT es opcional y podría no estar disponible para todos los usuarios. Los archivos devueltos por window.openai.selectFiles() ya están autorizados para el complemento actual cuando la función auxiliar está disponible. Usa el fileId devuelto con window.openai.getFileDownloadUrl({ fileId }) o en una entrada de herramienta que use parámetros de archivo.

Carga un archivo seleccionado por el usuario:

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

Selecciona archivos que el usuario ya haya cargado en ChatGPT:

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

Comprueba si window.openai.selectFiles está disponible y recurre a window.openai.uploadFile cuando la biblioteca de archivos no esté disponible.

Solicita una URL de descarga temporal:

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

Definir archivos de entrada

Para que ChatGPT pueda pasar archivos a una herramienta, enumera cada campo de archivo de entrada de nivel superior en _meta["openai/fileParams"]. Cada campo enumerado debe resolverse en un objeto de archivo o un arreglo de objetos de archivo.

Todo esquema de objeto de archivo debe declarar las cuatro propiedades admitidas:

PropiedadTipoDeclarar en propertiesIncluir en required
download_urlstring
file_idstring
mime_typestringNo
file_namestringNo

mime_type y file_name son valores opcionales, pero debes declarar sus propiedades en el esquema. Tanto en el paso Analizar herramientas como al enviar el complemento, se rechaza cualquier esquema de archivo que omita alguna de las cuatro propiedades, que no exija download_url y file_id, que marque cualquiera de las propiedades opcionales como obligatoria o que exija una propiedad distinta de download_url o file_id. Puedes declarar propiedades opcionales adicionales.

Este descriptor de herramienta completo acepta un archivo de entrada obligatorio:

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

Para aceptar más de un archivo, define el campo de nivel superior como un arreglo y usa el mismo esquema de objeto de archivo en items. La herramienta puede exigir el campo de archivo de nivel superior independientemente de las propiedades obligatorias dentro de cada objeto de archivo.

En tiempo de ejecución, ChatGPT pasa los valores de archivo con campos en snake case:

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

ChatGPT siempre incluye download_url y file_id; puede omitir mime_type y file_name. Usa file_id como valor de fileId para window.openai.getFileDownloadUrl({ fileId }) cuando un widget necesite una nueva URL de descarga temporal.

Al guardar de forma persistente el estado del widget, usa el formato estructurado (modelContent, privateContent, imageIds) si quieres que el modelo vea los ID de las imágenes en los turnos posteriores.

Navegación con soporte del host

El entorno de ejecución del sandbox refleja el historial de navegación del iframe en la interfaz de ChatGPT. Usa API de enrutamiento estándar, como React Router, y el host mantendrá sus controles de navegación sincronizados con tu interfaz.

Configuración del enrutador con BrowserRouter de React Router:

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

Navegación programática:

const navigate = useNavigate();

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

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

Parámetros del descriptor de herramienta

De forma predeterminada, la descripción de una herramienta debería incluir los campos enumerados aquí.

Declara outputSchema para cualquier herramienta que devuelva structuredContent. El esquema debería describir el objeto exacto que devuelve tu herramienta para que los clientes puedan validar los resultados y el modelo pueda razonar sobre las llamadas posteriores a herramientas.

Campos _meta del descriptor de herramienta

Usa estos campos _meta en el descriptor de herramienta. Da preferencia a la clave estándar de MCP Apps _meta.ui.resourceUri para vincular una herramienta con una plantilla de interfaz. ChatGPT admite metadatos específicos de OpenAI para compatibilidad y extensiones opcionales.

ClaveUbicaciónTipoLímitesPropósito
_meta["securitySchemes"]Descriptor de herramientaarrayNingunoCopia para mantener la compatibilidad con versiones anteriores de clientes que solo leen _meta.
_meta.ui.resourceUriDescriptor de herramientastring (URI)NingunoURI de recurso estándar para la plantilla de interfaz.
_meta.ui.visibilityDescriptor de herramientastring[]valor predeterminado ["model", "app"]Controla si una herramienta está disponible para el modelo, la interfaz o ambos. El valor app es el identificador de la interfaz en el protocolo MCP Apps.
_meta["openai/outputTemplate"]Descriptor de herramientastring (URI)NingunoAlias opcional de compatibilidad específico de OpenAI para _meta.ui.resourceUri en ChatGPT.
_meta["openai/profile"]Descriptor de herramientabooleanOpcional; solo true designa una herramienta de perfilIdentifica la herramienta autenticada de solo lectura que devuelve el perfil actual. Impleméntala para ayudar a los usuarios a reconocer y administrar varias cuentas conectadas. Los usuarios pueden conectar varias cuentas sin esta herramienta. Consulta Compatibilidad con varias cuentas.
_meta["openai/widgetAccessible"]Descriptor de herramientabooleanvalor predeterminado falseCampo de compatibilidad específico de OpenAI que usan las integraciones de interfaz existentes; da preferencia a _meta.ui.visibility + tools/call.
_meta["openai/visibility"]Descriptor de herramientastringpublic (predeterminado) o privateCampo de compatibilidad específico de OpenAI que usan las integraciones de interfaz existentes; usa preferentemente _meta.ui.visibility.
_meta["openai/toolInvocation/invoking"]Descriptor de la herramientastring≤ 64 caracteresTexto breve de estado mientras se ejecuta la herramienta.
_meta["openai/toolInvocation/invoked"]Descriptor de la herramientastring≤ 64 caracteresTexto breve de estado cuando finaliza la herramienta.
_meta["openai/fileParams"]Descriptor de la herramientastring[]NingunoLista de campos de entrada de nivel superior que representan archivos. Cada campo recibe { download_url, file_id, mime_type?, file_name? }.

Ejemplo:

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

Anotaciones

Para etiquetar una herramienta como “de solo lectura”, usa los siguientes campos de ToolAnnotations en el descriptor de la herramienta:

ClaveTipoObligatorioNotas
readOnlyHintbooleanObligatorioIndica que la herramienta solo recupera o calcula información y no crea, actualiza, elimina ni envía datos fuera de la conversación.
destructiveHintbooleanObligatorioDeclara que la herramienta puede eliminar o sobrescribir datos del usuario para que el host sepa que debe solicitar aprobación explícita primero.
openWorldHintbooleanObligatorioDeclara que la herramienta accede a la internet pública o a entidades externas de alcance abierto, incluso mediante acciones de solo lectura, como la búsqueda web. Una cuenta o un espacio de trabajo privado de alcance delimitado no se considera de mundo abierto solo por estar alojado externamente.
idempotentHintbooleanOpcionalDeclara que llamar a la herramienta con los mismos argumentos no tiene efectos adicionales en su entorno.

Estas indicaciones solo influyen en cómo ChatGPT o Codex presenta la llamada a la herramienta al usuario; los servidores deben seguir aplicando su propia lógica de autorización.

Ejemplo:

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

Campos _meta del recurso del componente

Configura estas claves en la plantilla de recursos que sirve tu componente (registerResource). Ayudan a ChatGPT a describir y presentar el iframe renderizado sin filtrar metadatos a otros clientes.

ClaveUbicaciónTipoPropósito
_meta.ui.prefersBorderContenido del recursobooleanIndica que el componente debería renderizarse dentro de una tarjeta con borde cuando se admita esta opción.
_meta.ui.cspContenido del recursoobjectUbicación de metadatos preferida para los campos CSP estándar del widget: connectDomains, resourceDomains y, opcionalmente, frameDomains.
_meta.ui.domainContenido del recursostring (origen)Origen dedicado para los componentes alojados (obligatorio al enviar un complemento con interfaz; debe ser único para cada complemento). El valor predeterminado es https://web-sandbox.oaiusercontent.com.
_meta["openai/widgetDescription"]Contenido del recursostringResumen legible para las personas que se proporciona al modelo cuando se carga el componente y reduce las explicaciones redundantes del asistente.
_meta["openai/widgetPrefersBorder"]Contenido del recursobooleanAlias de compatibilidad específico de OpenAI para _meta.ui.prefersBorder en ChatGPT.
_meta["openai/widgetCSP"]Contenido del recursoobjectClave de compatibilidad heredada de ChatGPT para los metadatos CSP del widget. _meta.ui.csp reemplaza los campos CSP estándar, pero redirect_domains sigue siendo obligatorio para los destinos de confianza de openExternal.
_meta["openai/widgetDomain"]Contenido del recursostring (origen)Alias de compatibilidad específico de OpenAI para _meta.ui.domain en ChatGPT.

ChatGPT admite la clave de compatibilidad heredada _meta["openai/widgetCSP"] con los siguientes nombres de campo en snake_case:

  • connect_domains: string[]
  • resource_domains: string[]
  • frame_domains?: string[]
  • redirect_domains?: string[]. Extensión de ChatGPT para los destinos de redirección de window.openai.openExternal.

En general, se prefiere el objeto estándar _meta.ui.csp para las nuevas interfaces de usuario. Admite lo siguiente:

  • connectDomains: string[]. Dominios con los que el widget puede comunicarse mediante fetch/XHR.
  • resourceDomains: string[]. Dominios para recursos estáticos (imágenes, fuentes, scripts, estilos).
  • frameDomains?: string[]. Lista opcional de orígenes permitidos para contenido incrustado en iframes. De forma predeterminada, los widgets no pueden renderizar marcos secundarios. Los complementos pueden incrustar contenido de su propio dominio, incluidos editores e interfaces de administración existentes, conforme a la política de iframes. Se requiere una justificación al realizar el envío, y el uso de iframes puede requerir una revisión adicional o demorar la aprobación.

Sin embargo, _meta.ui.csp no admite redirect_domains para los enlaces de window.openai.openExternal(...). Para agregar destinos de redirección a la lista de permitidos, sigue siendo necesario configurar _meta["openai/widgetCSP"].redirect_domains.

Resultados de herramientas

Los resultados de herramientas pueden contener los siguientes campos. En particular:

ClaveTipoObligatorioNotas
structuredContentobjectOpcionalSe muestra al modelo y al componente. Debe ajustarse al outputSchema declarado, si se proporciona.
contentstring o Content[]OpcionalSe muestra al modelo y al componente.
_metaobjectOpcionalSe entrega únicamente al componente. Se oculta al modelo.

Solo structuredContent y content aparecen en la transcripción de la conversación. El host reenvía _meta al componente para que puedas hidratar la interfaz de usuario sin exponer los datos al modelo.

Metadatos de resultados de herramientas proporcionados por el host:

ClaveUbicaciónTipoPropósito
_meta["openai/widgetSessionId"]_meta del resultado de la herramienta (proporcionado por el host)stringID estable de la instancia del widget montada actualmente; úsalo para correlacionar registros y llamadas a herramientas hasta que el widget se desmonte.

Ejemplo:

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

Resultado de herramienta con error

Para devolver un error en el resultado de la herramienta, usa la siguiente clave de _meta:

ClavePropósitoTipoNotas
_meta["mcp/www_authenticate"]Resultado de errorstring o string[]Desafíos WWW-Authenticate de RFC 7235 para iniciar OAuth.

Campos de _meta que proporciona el cliente

ClaveCuándo se proporcionaTipoPropósito
_meta["openai/locale"]Inicialización + llamadas a herramientasstring (BCP 47)Configuración regional solicitada (los clientes más antiguos pueden enviar _meta["webplus/i18n"]).
_meta["openai/userAgent"]Llamadas a herramientasstringIndicación opcional del agente de usuario, proporcionada en la medida de lo posible, para análisis o formato.
_meta["openai/userLocation"]Llamadas a herramientasobjectIndicación de ubicación aproximada (city, region, country, timezone, longitude, latitude).
_meta["openai/subject"]Llamadas a herramientasstringID de usuario anonimizado que se envía a los servidores MCP para limitar las solicitudes e identificar al usuario
_meta["openai/session"]Llamadas a herramientasstringID de conversación anonimizado para correlacionar llamadas a herramientas dentro de la misma sesión de ChatGPT.
_meta["openai/organization"]Llamadas a herramientasstringID anonimizado de la organización asociado con la organización actual de ChatGPT, cuando está disponible.

Durante la fase de operación, _meta["openai/userAgent"] y _meta["openai/userLocation"] son solo datos orientativos; los servidores nunca deben basarse en ellos para tomar decisiones de autorización y deben tolerar su ausencia. Trata _meta["openai/userAgent"] como metadatos opcionales que se proporcionan en la medida de lo posible, no como una forma estable de detectar desde qué interfaz del host se llama a tu servidor.

Ejemplo:

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