Comece pelo padrão aberto. Use a
especificação MCP Apps
para campos de interface e métodos da ponte de comunicação compartilhados.
As extensões da OpenAI são opcionais e ficam em window.openai
para quando você precisar de capacidades específicas do ChatGPT.
Ponte de comunicação de componentes window.openai
O ChatGPT fornece window.openai para aliases de compatibilidade e extensões opcionais
do ChatGPT. Novas interfaces devem usar a ponte de comunicação MCP Apps sempre que a especificação
compartilhada oferecer um equivalente e usar window.openai apenas para
capacidades específicas do ChatGPT.
Consulte Crie uma interface para o ChatGPT para ver guias de implementação passo a passo.
Se sua ferramenta exigir confirmação, a ausência inicial de toolInput é
esperada. O ChatGPT não carrega argumentos que dependem de aprovação nos valores do widget
antes da aprovação; o host os envia por meio de
ui/notifications/tool-input assim que o usuário aprova a chamada.
Capacidades
| Capacidade | O que faz | Uso típico |
|---|---|---|
| Estado e dados | window.openai.toolInput | Argumentos fornecidos quando a ferramenta foi chamada. Para ferramentas que dependem de aprovação, esse valor pode permanecer null até que o host envie ui/notifications/tool-input após a aprovação. |
| Estado e dados | window.openai.toolOutput | Seu structuredContent. Mantenha os campos concisos; o modelo lê seu conteúdo exatamente como foi escrito. |
| Estado e dados | window.openai.toolResponseMetadata | Metadados canônicos do resultado da ferramenta, exclusivos do widget. No ChatGPT, isso inclui status, call_tool_result e mcp_tool_result, preservando o envelope completo do resultado MCP, inclusive o campo oculto _meta. |
| Estado e dados | window.openai.widgetState | Registro do estado da interface persistido entre renderizações. |
| Estado e dados | window.openai.setWidgetState(state) | Armazena um novo registro de estado de forma síncrona; faça a chamada após cada interação relevante com a interface. |
| APIs do ambiente de execução do widget | window.openai.callTool(name, args) | Chame outra ferramenta MCP a partir do widget (reproduz as chamadas iniciadas pelo modelo). |
| APIs do ambiente de execução do widget | window.openai.sendFollowUpMessage({ prompt, scrollToBottom }) | Peça ao ChatGPT para publicar uma mensagem criada pelo componente. scrollToBottom é opcional, tem true como valor padrão e pode ser definido como false para impedir a rolagem automática. |
| APIs do ambiente de execução do widget | window.openai.uploadFile(file, { library?: boolean }) | Envie um arquivo selecionado pelo usuário e receba um fileId. Passe { library: true } para também salvar o arquivo enviado na biblioteca de arquivos do usuário no ChatGPT, quando ela estiver disponível. |
| APIs do ambiente de execução do widget | window.openai.selectFiles() | Abra o seletor da biblioteca de arquivos do ChatGPT e retorne os arquivos cujo acesso foi autorizado para o plug-in no formato { fileId, fileName, mimeType }[]. Verifique se essa função auxiliar está disponível antes de usá-la, pois a biblioteca de arquivos pode não estar disponível para todos os usuários. |
| APIs do ambiente de execução do widget | window.openai.getFileDownloadUrl({ fileId }) | Obtenha uma URL temporária de download para um arquivo enviado pelo widget, selecionado na biblioteca de arquivos, passado por parâmetros de arquivo ou retornado por referências a arquivos da ferramenta. |
| APIs do ambiente de execução do widget | window.openai.requestDisplayMode(...) | Solicite os modos PiP ou tela cheia. |
| APIs do ambiente de execução do widget | window.openai.requestModal({ params, template }) | Abra um modal gerenciado pelo ChatGPT. Omita template para usar o modelo de interface atual ou passe o URI de um modelo de interface registrado para trocar o conteúdo do modal. |
| APIs do ambiente de execução do widget | window.openai.requestClose() | Peça ao ChatGPT para fechar o widget atual. |
| APIs do ambiente de execução do widget | window.openai.notifyIntrinsicHeight(...) | Informe as alturas dinâmicas do widget para evitar que o conteúdo seja cortado durante a rolagem. |
| APIs do ambiente de execução do widget | window.openai.openExternal({ href, redirectUrl }) | Abra um link externo verificado no navegador do usuário. Para destinos de redirecionamento aprovados, o ChatGPT acrescenta ?redirectUrl=... por padrão; defina redirectUrl: false para evitar isso. |
| APIs do ambiente de execução do widget | window.openai.setOpenInAppUrl({ href }) | Substitua, opcionalmente, o destino externo exibido em tela cheia. Se não for definido, o ChatGPT mantém o comportamento padrão e abre o caminho atual do iframe do componente. |
| Contexto | window.openai.theme, window.openai.displayMode, window.openai.maxHeight, window.openai.safeArea, window.openai.view, window.openai.userAgent, window.openai.locale | Sinais do ambiente que você pode ler ou acompanhar por meio de useOpenAiGlobal para adaptar os elementos visuais e os textos. |
Função auxiliar useOpenAiGlobal
Muitos projetos de interface para o ChatGPT encapsulam o acesso a window.openai em pequenas funções auxiliares
para que as visualizações continuem testáveis. Esta função auxiliar de exemplo monitora os eventos
openai:set_globals do host e permite que componentes React acompanhem um ú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]
);
}
Feche a interface
Chame window.openai.requestClose() para pedir ao ChatGPT que feche a interface atual.
Solicite outro modo de apresentação
Use window.openai.requestDisplayMode para solicitar a apresentação na própria conversa, em imagem sobre imagem
ou em tela cheia:
await window.openai?.requestDisplayMode({ mode: "fullscreen" });
// On mobile, picture-in-picture may be presented as fullscreen.
Abra um modal
Use window.openai.requestModal para abrir um modal controlado pelo host. Forneça o
URI de outro modelo de interface registrado pelo mesmo servidor MCP ou omita
template para abrir o modelo de interface atual:
await window.openai.requestModal({
template: "ui://widget/checkout.html",
});
APIs de arquivos
O ChatGPT oferece funções auxiliares para envio e download de arquivos como extensões opcionais
de window.openai.
| API | Finalidade | Observações |
|---|---|---|
window.openai.uploadFile(file, { library?: boolean }) | Envie um arquivo selecionado pelo usuário e receba um fileId. | Passe { library: true } para também salvar o arquivo enviado na biblioteca de arquivos do usuário no ChatGPT, quando ela estiver disponível para o usuário atual. |
window.openai.selectFiles() | Abra o seletor da biblioteca de arquivos para selecionar arquivos existentes. | Retorna [{ fileId, fileName, mimeType }]. Verifique se essa função auxiliar está disponível, pois a biblioteca de arquivos pode não estar disponível para todos os usuários. |
window.openai.getFileDownloadUrl({ fileId }) | Solicite uma URL temporária de download para um arquivo. | Funciona com arquivos enviados pelo widget, selecionados na biblioteca de arquivos, passados por parâmetros de arquivo ou retornados por referências a arquivos nas ferramentas. |
A biblioteca de arquivos do ChatGPT é opcional e pode não estar disponível para todos os usuários.
Os arquivos retornados por window.openai.selectFiles() já estão autorizados para
o plug-in atual quando a função auxiliar está disponível. Use o fileId retornado com
window.openai.getFileDownloadUrl({ fileId }) ou em uma entrada de ferramenta que use
parâmetros de arquivo.
Envie um arquivo selecionado pelo usuário:
const { fileId } = await window.openai.uploadFile(file, {
library: true,
});
Selecione arquivos que o usuário já enviou ao ChatGPT:
if (window.openai?.selectFiles) {
const files = await window.openai.selectFiles();
// [{ fileId, fileName, mimeType }]
}
Verifique se window.openai.selectFiles está disponível e use
window.openai.uploadFile como alternativa quando a biblioteca de arquivos estiver indisponível.
Solicite uma URL temporária de download:
const { downloadUrl } = await window.openai.getFileDownloadUrl({ fileId });
Defina arquivos de entrada
Para permitir que o ChatGPT passe arquivos a uma ferramenta, liste cada entrada de arquivo de nível superior em
_meta["openai/fileParams"]. Cada campo listado deve corresponder a um objeto de arquivo ou
a um array de objetos de arquivo.
Todo esquema de objeto de arquivo deve declarar as quatro propriedades suportadas:
| Propriedade | Tipo | Declarar em properties | Incluir em required |
|---|---|---|---|
download_url | string | Sim | Sim |
file_id | string | Sim | Sim |
mime_type | string | Sim | Não |
file_name | string | Sim | Não |
mime_type e file_name são valores opcionais, mas você deve declarar suas
propriedades no esquema. A etapa Verificar ferramentas e o processo de envio do plug-in rejeitam um
esquema de arquivo que omita qualquer uma das quatro propriedades, não exija
download_url e file_id, marque qualquer uma das propriedades opcionais como obrigatória ou
exija uma propriedade diferente de download_url ou file_id. Você pode declarar
propriedades opcionais adicionais.
Este descritor completo de ferramenta aceita uma entrada de arquivo obrigatória:
{
"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 aceitar mais de um arquivo, defina o campo de nível superior como um array e use o
mesmo esquema de objeto de arquivo em items. A ferramenta pode exigir o campo de arquivo
de nível superior independentemente das propriedades obrigatórias dentro de cada objeto de arquivo.
Em tempo de execução, o ChatGPT passa valores de arquivo com campos em snake case:
{
"download_url": "https://...",
"file_id": "file_...",
"mime_type": "image/png",
"file_name": "input.png"
}
O ChatGPT sempre inclui download_url e file_id; ele pode omitir mime_type
e file_name. Use file_id como valor de fileId em
window.openai.getFileDownloadUrl({ fileId }) quando um widget precisar de uma nova
URL temporária de download.
Ao persistir o estado do widget, use o formato estruturado (modelContent, privateContent, imageIds) se quiser que o modelo tenha acesso aos IDs das imagens nas próximas interações.
Navegação com suporte do host
O ambiente de execução do sandbox espelha o histórico de navegação do iframe na interface do ChatGPT. Use APIs de roteamento padrão, como o React Router, e o host manterá seus controles de navegação sincronizados com a sua interface.
Configuração do roteador com o BrowserRouter do React Router:
export default function PizzaListRouter() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<PizzaListPlugin />}>
<Route path="place/:placeId" element={<PizzaListPlugin />} />
</Route>
</Routes>
</BrowserRouter>
);
}
Navegação programática:
const navigate = useNavigate();
function openDetails(placeId: string) {
navigate(`place/${placeId}`, { replace: false });
}
function closeDetails() {
navigate("..", { replace: true });
}
Parâmetros do descritor de ferramenta
Por padrão, a descrição de uma ferramenta deve incluir os campos listados aqui.
Declare outputSchema para qualquer ferramenta que retorne structuredContent. O
esquema deve descrever exatamente o objeto retornado pela ferramenta para que os clientes possam
validar os resultados e o modelo possa raciocinar sobre chamadas de ferramenta subsequentes.
Campos de _meta no descritor de ferramenta
Use estes campos de _meta no descritor de ferramenta. Dê preferência à chave padrão do MCP Apps
_meta.ui.resourceUri para vincular uma ferramenta a um modelo de interface. O ChatGPT oferece suporte a
metadados específicos da OpenAI para compatibilidade e extensões opcionais.
| Chave | Localização | Tipo | Limites | Finalidade |
|---|---|---|---|---|
_meta["securitySchemes"] | Descritor de ferramenta | array | Nenhum | Cópia para compatibilidade com versões anteriores, destinada a clientes que leem apenas _meta. |
_meta.ui.resourceUri | Descritor de ferramenta | string (URI) | Nenhum | URI de recurso padrão para o modelo de interface. |
_meta.ui.visibility | Descritor de ferramenta | string[] | padrão ["model", "app"] | Controla se uma ferramenta está disponível para o modelo, para a interface ou para ambos. O valor app é o identificador de interface no protocolo MCP Apps. |
_meta["openai/outputTemplate"] | Descritor de ferramenta | string (URI) | Nenhum | Alias opcional de compatibilidade, específico da OpenAI, para _meta.ui.resourceUri no ChatGPT. |
_meta["openai/profile"] | Descritor de ferramenta | boolean | Opcional; somente true designa uma ferramenta de perfil | Identifica a ferramenta autenticada e somente leitura que retorna o perfil atual. Implemente-a para ajudar os usuários a reconhecer e gerenciar várias contas conectadas. Os usuários podem conectar várias contas sem essa ferramenta. Consulte Suporte a várias contas. |
_meta["openai/widgetAccessible"] | Descritor de ferramenta | boolean | padrão false | Campo de compatibilidade específico da OpenAI usado por integrações de interface existentes; dê preferência a _meta.ui.visibility + tools/call. |
_meta["openai/visibility"] | Descritor de ferramenta | string | public (padrão) ou private | Campo de compatibilidade específico da OpenAI usado por integrações de interface existentes; prefira _meta.ui.visibility. |
_meta["openai/toolInvocation/invoking"] | Descritor da ferramenta | string | ≤ 64 caracteres | Texto curto de status durante a execução da ferramenta. |
_meta["openai/toolInvocation/invoked"] | Descritor da ferramenta | string | ≤ 64 caracteres | Texto curto de status após a conclusão da ferramenta. |
_meta["openai/fileParams"] | Descritor da ferramenta | string[] | Nenhum | Lista de campos de entrada de nível superior que representam arquivos. Cada campo recebe { download_url, file_id, mime_type?, file_name? }. |
Exemplo:
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.` }],
};
}
);
Anotações
Para identificar uma ferramenta como "somente leitura", use os seguintes
campos de
ToolAnnotations
no descritor da ferramenta:
| Chave | Tipo | Obrigatório | Observações |
|---|---|---|---|
readOnlyHint | boolean | Obrigatório | Indique que a ferramenta apenas consulta ou calcula informações e não cria, atualiza, exclui nem envia dados fora da conversa. |
destructiveHint | boolean | Obrigatório | Declare que a ferramenta pode excluir ou sobrescrever dados do usuário para que o host saiba que deve solicitar aprovação explícita antes de prosseguir. |
openWorldHint | boolean | Obrigatório | Declare que a ferramenta acessa a internet pública ou entidades externas de escopo não delimitado, inclusive por meio de ações somente leitura, como pesquisa na Web. Uma conta privada ou um workspace de escopo delimitado não constitui um ambiente aberto apenas por estar hospedado externamente. |
idempotentHint | boolean | Opcional | Declare que chamar a ferramenta com os mesmos argumentos não tem efeito adicional sobre o ambiente em que ela opera. |
Essas indicações influenciam apenas como o ChatGPT ou o Codex apresenta a chamada de ferramenta ao usuário; os servidores ainda devem aplicar sua própria lógica de autorização.
Exemplo:
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 do recurso do componente
Defina estas chaves no template de recurso que fornece seu componente (registerResource). Elas ajudam o ChatGPT a descrever e apresentar o iframe renderizado sem expor metadados a outros clientes.
| Chave | Localização | Tipo | Finalidade |
|---|---|---|---|
_meta.ui.prefersBorder | Conteúdo do recurso | boolean | Indique que o componente deve ser renderizado dentro de um cartão com borda quando houver suporte. |
_meta.ui.csp | Conteúdo do recurso | object | Local preferencial nos metadados para os campos padrão de CSP do widget: connectDomains, resourceDomains e, opcionalmente, frameDomains. |
_meta.ui.domain | Conteúdo do recurso | string (origem) | Origem dedicada para componentes hospedados (obrigatória ao enviar um plug-in com interface; deve ser exclusiva de cada plug-in). O padrão é https://web-sandbox.oaiusercontent.com. |
_meta["openai/widgetDescription"] | Conteúdo do recurso | string | Resumo legível por humanos, disponibilizado ao modelo quando o componente é carregado, para reduzir explicações redundantes do assistente. |
_meta["openai/widgetPrefersBorder"] | Conteúdo do recurso | boolean | Alias de compatibilidade específico da OpenAI para _meta.ui.prefersBorder no ChatGPT. |
_meta["openai/widgetCSP"] | Conteúdo do recurso | object | Chave legada de compatibilidade do ChatGPT para metadados de CSP do widget. Os campos padrão de CSP são substituídos por _meta.ui.csp, mas redirect_domains ainda é obrigatório para destinos confiáveis de openExternal. |
_meta["openai/widgetDomain"] | Conteúdo do recurso | string (origem) | Alias de compatibilidade específico da OpenAI para _meta.ui.domain no ChatGPT. |
O ChatGPT oferece suporte à chave legada de compatibilidade _meta["openai/widgetCSP"] com os seguintes nomes de campos em snake_case:
connect_domains:string[]resource_domains:string[]frame_domains?:string[]redirect_domains?:string[]. Extensão do ChatGPT para destinos de redirecionamento dewindow.openai.openExternal.
O objeto padrão _meta.ui.csp geralmente é a opção recomendada para novas interfaces e oferece suporte a:
connectDomains:string[]. Domínios aos quais o widget pode se conectar via fetch/XHR.resourceDomains:string[]. Domínios para recursos estáticos (imagens, fontes, scripts, estilos).frameDomains?:string[]. Lista opcional de origens permitidas para incorporações em iframes. Por padrão, widgets não podem renderizar subquadros. Plug-ins podem incorporar conteúdo do próprio domínio, incluindo editores e interfaces de administração existentes, conforme a política de iframes. É necessário apresentar uma justificativa no envio, e o uso de iframes pode exigir revisão adicional ou tornar a aprovação mais demorada.
No entanto, _meta.ui.csp não oferece suporte a redirect_domains para links de window.openai.openExternal(...). Para adicionar destinos de redirecionamento à lista de permissões, ainda é necessário definir _meta["openai/widgetCSP"].redirect_domains.
Resultados de ferramentas
Os resultados de ferramentas podem conter os seguintes campos. Em especial:
| Chave | Tipo | Obrigatório | Observações |
|---|---|---|---|
structuredContent | object | Opcional | Disponibilizado ao modelo e ao componente. Deve corresponder ao outputSchema declarado, quando fornecido. |
content | string ou Content[] | Opcional | Disponibilizado ao modelo e ao componente. |
_meta | object | Opcional | Enviado apenas ao componente. Oculto para o modelo. |
Apenas structuredContent e content aparecem na transcrição da conversa. O host encaminha _meta ao componente para que você possa hidratar a interface sem expor os dados ao modelo.
Metadados do resultado da ferramenta fornecidos pelo host:
| Chave | Localização | Tipo | Finalidade |
|---|---|---|---|
_meta["openai/widgetSessionId"] | _meta do resultado da ferramenta (fornecido pelo host) | string | ID estável da instância do widget atualmente montada; use-o para correlacionar logs e chamadas de ferramentas até que o widget seja desmontado. |
Exemplo:
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 ferramenta com erro
Para retornar um erro no resultado da ferramenta, use a seguinte chave de _meta:
| Chave | Finalidade | Tipo | Observações |
|---|---|---|---|
_meta["mcp/www_authenticate"] | Resultado de erro | string ou string[] | Desafios WWW-Authenticate da RFC 7235 para iniciar o OAuth. |
Campos de _meta fornecidos pelo cliente
| Chave | Quando é fornecido | Tipo | Finalidade |
|---|---|---|---|
_meta["openai/locale"] | Inicialização + chamadas de ferramentas | string (BCP 47) | Localidade solicitada (clientes mais antigos podem enviar _meta["webplus/i18n"]). |
_meta["openai/userAgent"] | Chamadas de ferramentas | string | Indicação opcional do agente de usuário, fornecida quando possível, para análise de uso ou formatação. |
_meta["openai/userLocation"] | Chamadas de ferramentas | object | Indicação de localização aproximada (city, region, country, timezone, longitude, latitude). |
_meta["openai/subject"] | Chamadas de ferramentas | string | ID anonimizado do usuário enviado aos servidores MCP para limitação de taxa e identificação |
_meta["openai/session"] | Chamadas de ferramentas | string | ID anonimizado da conversa para correlacionar chamadas de ferramentas na mesma sessão do ChatGPT. |
_meta["openai/organization"] | Chamadas de ferramentas | string | ID anonimizado da organização associado à organização atual do ChatGPT, quando disponível. |
Na fase de operação, _meta["openai/userAgent"] e _meta["openai/userLocation"] são apenas indicações; os servidores nunca devem usá-los como base para decisões de autorização e devem funcionar mesmo na ausência deles. Trate _meta["openai/userAgent"] como metadados opcionais, fornecidos conforme possível, e não como uma forma estável de detectar qual interface do host está chamando seu servidor.
Exemplo:
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 },
};
}
);