Comece pelo padrão aberto. Use
a especificação MCP Apps
para os campos de interface e os métodos de comunicação compartilhados.
As extensões da OpenAI são opcionais e estão disponíveis em window.openai
quando você precisa de recursos específicos do ChatGPT.
Comunicação de componentes com window.openai
O ChatGPT disponibiliza window.openai para aliases de compatibilidade e extensões opcionais do ChatGPT. Ao criar uma interface, use a ponte de comunicação do MCP Apps sempre que a especificação compartilhada oferecer um recurso equivalente. Use window.openai apenas para recursos específicos do ChatGPT.
Consulte Como criar uma interface para o ChatGPT para ver exemplos de implementação passo a passo.
Se a ferramenta exigir confirmação, é normal que toolInput esteja ausente inicialmente. Antes da aprovação, o ChatGPT não carrega nos valores do widget os argumentos que dependem dela. Assim que o usuário aprova a chamada, o host envia esses argumentos por meio de ui/notifications/tool-input.
Recursos
| Recurso | O que faz | Uso típico |
|---|---|---|
| Estado e dados | window.openai.toolInput |
Argumentos fornecidos na chamada da ferramenta. Para ferramentas que exigem aprovação, o valor pode permanecer null até que o host envie ui/notifications/tool-input após a aprovação. |
| Estado e dados | window.openai.toolOutput |
Contém seu structuredContent. Mantenha os campos concisos, pois o modelo os lê literalmente. |
| Estado e dados | window.openai.toolResponseMetadata |
Metadados canônicos do resultado da ferramenta, disponíveis apenas para o widget. No ChatGPT, incluem status, call_tool_result e mcp_tool_result, preservando todo o envelope do resultado MCP, inclusive o campo oculto _meta. |
| Estado e dados | window.openai.widgetState |
Registro do estado da interface preservado entre renderizações. |
| Estado e dados | window.openai.setWidgetState(state) |
Salva um novo registro do estado de forma síncrona. Chame o método após cada interação relevante com a interface. |
| APIs de execução do widget | window.openai.callTool(name, args) |
Chama outra ferramenta MCP a partir do widget, da mesma forma que nas chamadas iniciadas pelo modelo. |
| APIs de execução do widget | window.openai.sendFollowUpMessage({ prompt, scrollToBottom }) |
Solicita ao ChatGPT que publique uma mensagem de autoria do componente. scrollToBottom é opcional, tem true como valor padrão e aceita false para impedir a rolagem automática. |
| APIs de execução do widget | window.openai.uploadFile(file, { library?: boolean }) |
Envia um arquivo selecionado pelo usuário e retorna um fileId. Passe { library: true } para também salvar o arquivo na biblioteca de arquivos do usuário no ChatGPT, quando ela estiver disponível. |
| APIs de execução do widget | window.openai.selectFiles() |
Abre o seletor da biblioteca de arquivos do ChatGPT e retorna os arquivos autorizados para o plugin no formato { fileId, fileName, mimeType }[]. Verifique se a função está disponível antes de usá-la, pois nem todos os usuários têm acesso à biblioteca. |
| APIs de execução do widget | window.openai.getFileDownloadUrl({ fileId }) |
Obtém uma URL temporária de download para um arquivo enviado pelo widget, selecionado na biblioteca, passado como parâmetro de arquivo ou retornado por meio de uma referência a arquivo da ferramenta. |
| APIs de execução do widget | window.openai.requestDisplayMode(...) |
Solicita o modo picture-in-picture (PiP) ou tela cheia. |
| APIs de execução do widget | window.openai.requestModal({ params, template }) |
Abre uma janela modal controlada pelo ChatGPT. Omita template para usar o template atual ou passe o URI de um template registrado para alterar o conteúdo da janela. |
| APIs de execução do widget | window.openai.requestClose() |
Solicita ao ChatGPT que feche o widget atual. |
| APIs de execução do widget | window.openai.notifyIntrinsicHeight(...) |
Informa a altura do widget à medida que ela muda para evitar cortes no conteúdo durante a rolagem. |
| APIs de execução do widget | window.openai.openExternal({ href, redirectUrl }) |
Abre 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 omitir esse parâmetro. |
| APIs de execução do widget | window.openai.setOpenInAppUrl({ href }) |
Permite substituir o destino externo exibido em tela cheia. Se você não definir esse destino, 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 consultar ou acompanhar por meio de useOpenAiGlobal para adaptar a aparência e os textos da interface. |
Função auxiliar useOpenAiGlobal
Muitos projetos de interface para o ChatGPT usam pequenas funções auxiliares para acessar window.openai, o que facilita testar as telas. Neste exemplo, a função auxiliar escuta os eventos openai:set_globals do host e permite que componentes React acompanhem as mudanças de um único valor global:
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];
}
);
}Fechar a interface
Chame window.openai.requestClose() para solicitar ao ChatGPT que feche a interface atual.
Solicitar outro modo de exibição
Use window.openai.requestDisplayMode para solicitar a exibição na própria conversa, em picture-in-picture ou em tela cheia:
await window.openai?.requestDisplayMode({ mode: "fullscreen" });
// On mobile, picture-in-picture may be presented as fullscreen.Abrir uma janela modal
Use window.openai.requestModal para abrir uma janela modal controlada pelo host. Informe o URI de outro template de interface registrado pelo mesmo servidor MCP ou omita template para abrir o template atual:
await window.openai.requestModal({
template: "ui://widget/details.html",
});APIs de arquivos
O ChatGPT oferece funções auxiliares de upload e download de arquivos como extensões opcionais de window.openai.
| API | Finalidade | Observações |
|---|---|---|
window.openai.uploadFile(file, { library?: boolean }) |
Envia um arquivo selecionado pelo usuário e retorna um fileId. |
Passe { library: true } para também salvar o arquivo na biblioteca de arquivos do usuário no ChatGPT, quando ela estiver disponível para esse usuário. |
window.openai.selectFiles() |
Abre o seletor da biblioteca para escolher arquivos existentes. | Retorna [{ fileId, fileName, mimeType }]. Verifique se a função está disponível antes de usá-la, pois nem todos os usuários têm acesso à biblioteca. |
window.openai.getFileDownloadUrl({ fileId }) |
Solicita uma URL temporária de download para um arquivo. | Funciona com arquivos enviados pelo widget, selecionados na biblioteca, passados como parâmetros de arquivo ou retornados por meio de referências a arquivos da ferramenta. |
A biblioteca de arquivos do ChatGPT é opcional e pode não estar disponível para todos os usuários. Quando a função window.openai.selectFiles() está disponível, os arquivos que ela retorna já estão autorizados para o plugin atual. Use o fileId retornado com window.openai.getFileDownloadUrl({ fileId }) ou na entrada de uma ferramenta que aceite 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 quando a biblioteca de arquivos estiver indisponível.
Solicite uma URL temporária de download:
const { downloadUrl } = await window.openai.getFileDownloadUrl({ fileId });Definir entradas de arquivo
Para que o ChatGPT possa passar arquivos a uma ferramenta, liste cada campo de entrada de arquivo de nível superior em _meta["openai/fileParams"]. O valor de cada campo listado deve ser um objeto de arquivo ou um array de objetos de arquivo.
O schema de cada objeto de arquivo deve declarar as quatro propriedades compatíveis:
| 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 |
Os valores de mime_type e file_name são opcionais, mas você deve declarar essas propriedades no schema. Tanto a etapa Verificar ferramentas quanto o processo de envio do plugin rejeitam um schema de arquivo que omita qualquer uma das quatro propriedades, deixe de marcar como obrigatório algum dos campos download_url e file_id, marque qualquer uma das propriedades opcionais como obrigatória ou exija alguma propriedade além de download_url ou file_id. Você pode declarar outras propriedades opcionais.
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 schema de objeto de arquivo em items. A ferramenta pode exigir o campo de arquivo de nível superior independentemente das propriedades obrigatórias de cada objeto de arquivo.
Durante a execução, o ChatGPT passa os valores de arquivo com nomes de campo 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, mas pode omitir mime_type e file_name. Use file_id como valor de fileId em window.openai.getFileDownloadUrl({ fileId }) quando o 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 integrada ao host
O ambiente de execução da sandbox replica 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 interface do componente.
Configuração do roteador com BrowserRouter do 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>
);
}Navegação por código:
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")
);
}Parâmetros do descritor de ferramenta
Por padrão, a descrição de uma ferramenta deve incluir os campos listados na especificação MCP.
Declare outputSchema para toda ferramenta que retorne structuredContent. O schema deve descrever exatamente o objeto que a ferramenta retorna para que os clientes possam validar os resultados e o modelo possa avaliar as próximas chamadas de ferramenta.
Campos _meta do descritor de ferramenta
Use estes campos _meta no descritor da ferramenta. Para associar uma ferramenta a um template de interface, prefira a chave padrão _meta.ui.resourceUri do MCP Apps. O ChatGPT aceita metadados específicos da OpenAI para garantir compatibilidade e oferecer extensões opcionais.
| Chave | Local | Tipo | Limites | Finalidade |
|---|---|---|---|---|
_meta["securitySchemes"] |
Descritor da ferramenta | array | Nenhum | Replica os esquemas de segurança para manter a compatibilidade com clientes que leem apenas _meta. |
_meta.ui.resourceUri |
Descritor da ferramenta | string (URI) | Nenhum | URI padrão do recurso que fornece o template de interface. |
_meta.ui.visibility |
Descritor da ferramenta | string[] | Padrão ["model", "app"] |
Controla se a ferramenta está disponível para o modelo, para a interface ou para ambos. O valor app identifica a interface no protocolo MCP Apps. |
_meta["openai/outputTemplate"] |
Descritor da ferramenta | string (URI) | Nenhum | Alias opcional de _meta.ui.resourceUri, específico da OpenAI, para compatibilidade no ChatGPT. |
_meta["openai/profile"] |
Descritor da ferramenta | booleano | Opcional. Apenas true indica 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 mesmo sem essa ferramenta. Consulte Suporte a várias contas. |
_meta["openai/widgetAccessible"] |
Descritor da ferramenta | booleano | Padrão false |
Campo de compatibilidade específico da OpenAI usado por integrações de interface existentes. Prefira _meta.ui.visibility + tools/call. |
_meta["openai/visibility"] |
Descritor da 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 exibido durante a execução da ferramenta. |
_meta["openai/toolInvocation/invoked"] |
Descritor da ferramenta | string | ≤ 64 caracteres | Texto curto de status exibido 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 { 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.` }],
};
}
);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 |
booleano | Sim | Indique que a ferramenta apenas consulta ou calcula informações e não cria, atualiza, exclui nem envia dados fora da conversa. |
destructiveHint |
booleano | Sim | 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 |
booleano | Sim | Declare que a ferramenta acessa a internet pública ou entidades externas sem escopo delimitado, inclusive por meio de ações somente leitura, como pesquisa na Web. Uma conta privada ou um espaço de trabalho de escopo delimitado não se enquadra nessa categoria apenas por ter hospedagem externa. |
idempotentHint |
booleano | Não | Declare que repetir a chamada da ferramenta com os mesmos argumentos não tem efeito adicional sobre o ambiente. |
Essas indicações influenciam apenas a forma como o ChatGPT ou o Codex apresenta a chamada da ferramenta ao usuário. Os servidores devem aplicar sua própria lógica de autorização.
Exemplo:
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() },
})
);Campos _meta do recurso do componente
Defina estas chaves no template de recurso que disponibiliza o componente (registerResource). Elas ajudam o ChatGPT a descrever o iframe renderizado e a definir como exibi-lo sem expor metadados a outros clientes.
| Chave | Local | Tipo | Finalidade |
|---|---|---|---|
_meta.ui.prefersBorder |
Conteúdo do recurso | booleano | Indica que o componente deve ser exibido em um cartão com borda quando houver suporte. |
_meta.ui.csp |
Conteúdo do recurso | objeto | Local preferencial para declarar os campos padrão da CSP do widget, incluindo connectDomains, resourceDomains e o campo opcional frameDomains. |
_meta.ui.domain |
Conteúdo do recurso | string (origem) | Origem dedicada aos componentes hospedados. É obrigatória ao enviar um plugin com interface e deve ser exclusiva de cada plugin. O valor padrão é https://web-sandbox.oaiusercontent.com. |
_meta["openai/ui"].availableDisplayModes |
Conteúdo do recurso | string[] | Indica os modos de exibição compatíveis: inline, fullscreen e pip. Permite que o ChatGPT escolha um modo de exibição antes de carregar o componente. |
_meta["openai/widgetDescription"] |
Conteúdo do recurso | string | Resumo em linguagem natural apresentado ao modelo quando o componente é carregado. Ajuda a evitar que o assistente repita informações. |
_meta["openai/widgetPrefersBorder"] |
Conteúdo do recurso | booleano | Alias de compatibilidade de _meta.ui.prefersBorder específico da OpenAI no ChatGPT. |
_meta["openai/widgetCSP"] |
Conteúdo do recurso | objeto | Chave legada de compatibilidade do ChatGPT para metadados da CSP do widget. _meta.ui.csp substitui os campos padrão de CSP dessa chave, mas redirect_domains continua obrigatório para destinos confiáveis de openExternal. |
_meta["openai/widgetDomain"] |
Conteúdo do recurso | string (origem) | Alias de compatibilidade de _meta.ui.domain específico da OpenAI no ChatGPT. |
O ChatGPT aceita a chave legada de compatibilidade _meta["openai/widgetCSP"] com os seguintes nomes de campo 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.
Em geral, prefira o objeto padrão _meta.ui.csp para novas interfaces. Ele aceita os seguintes campos:
connectDomains:string[]. Domínios que o widget pode acessar via fetch/XHR.resourceDomains:string[]. Domínios de recursos estáticos, como imagens, fontes, scripts e estilos.frameDomains?:string[]. Lista opcional de origens permitidas para conteúdo incorporado em iframes. Por padrão, os widgets não podem renderizar frames aninhados. Os plugins podem incorporar conteúdo do próprio domínio, inclusive editores e interfaces de administração existentes, de acordo com a política de iframes. É preciso apresentar uma justificativa no envio. O uso de iframes pode exigir uma análise adicional ou atrasar a aprovação.
No entanto, _meta.ui.csp não aceita redirect_domains para links de window.openai.openExternal(...). Para permitir destinos de redirecionamento específicos, ainda é necessário definir _meta["openai/widgetCSP"].redirect_domains.
Declarar os modos de exibição antes de carregar a interface
Defina _meta["openai/ui"].availableDisplayModes no conteúdo do recurso retornado pelo servidor. Se o componente só funcionar em tela cheia, essa declaração permite que o ChatGPT o abra imediatamente nesse modo, sem exibir primeiro um indicador de carregamento na conversa.
Retorne este objeto no array contents do recurso e atribua a html o HTML do componente:
const resource = {
uri: "ui://widget/viewer.html",
mimeType: "text/html;profile=mcp-app",
text: html,
_meta: {
"openai/ui": {
availableDisplayModes: ["fullscreen"],
},
},
};
Liste todos os modos que o componente aceita, por exemplo, ["inline", "fullscreen"]. Continue declarando availableDisplayModes também durante a inicialização do MCP Apps. A declaração no servidor complementa esse fluxo, que também aceita modos de exibição dinâmicos.
Resultados de ferramentas
Os resultados de ferramentas podem conter os campos definidos na especificação MCP. Entre eles estão os seguintes:
| Chave | Tipo | Obrigatório | Observações |
|---|---|---|---|
structuredContent |
objeto | Não | Disponível para o modelo e para o componente. Deve corresponder ao outputSchema declarado, quando fornecido. |
content |
string ou Content[] |
Não | Disponível para o modelo e para o componente. |
_meta |
objeto | Não | Entregue apenas ao componente. O modelo não tem acesso a esse campo. |
Apenas structuredContent e content aparecem na transcrição da conversa. O host encaminha _meta ao componente para que você possa preencher a interface sem expor os dados ao modelo.
Metadados que o host fornece no resultado da ferramenta:
| Chave | Local | Tipo | Finalidade |
|---|---|---|---|
_meta["openai/widgetSessionId"] |
Campo _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 ferramenta até que o widget seja desmontado. |
Exemplo:
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])
),
},
};
}
);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 definidos na RFC 7235 para iniciar o fluxo OAuth. |
Campos _meta fornecidos pelo cliente
| Chave | Quando é fornecida | Tipo | Finalidade |
|---|---|---|---|
_meta["openai/locale"] |
Na inicialização e nas chamadas de ferramenta | string (BCP 47) | Configuração regional solicitada. Clientes mais antigos podem enviar _meta["webplus/i18n"]. |
_meta["openai/userAgent"] |
Nas chamadas de ferramenta | string | Indicação opcional do agente de usuário, fornecida quando possível para análise de uso ou formatação. |
_meta["openai/userLocation"] |
Nas chamadas de ferramenta | objeto | Indicação aproximada da localização (city, region, country, timezone, longitude, latitude). |
_meta["openai/subject"] |
Nas chamadas de ferramenta | string | ID anonimizado do usuário, enviado aos servidores MCP para identificação e limitação da taxa de requisições. |
_meta["openai/session"] |
Nas chamadas de ferramenta | string | ID anonimizado da conversa, usado para correlacionar chamadas de ferramenta na mesma sessão do ChatGPT. |
_meta["openai/organization"] |
Nas chamadas de ferramenta | string | ID anonimizado da organização atual do ChatGPT, quando disponível. |
Durante a operação, _meta["openai/userAgent"] e _meta["openai/userLocation"] servem apenas como indicações. Os servidores nunca devem se basear nesses campos para tomar decisões de autorização e devem funcionar mesmo na ausência deles. Trate _meta["openai/userAgent"] como um metadado opcional, fornecido quando possível. Ele não identifica de forma confiável a interface do host que está chamando o servidor.
Exemplo:
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 },
};
}
);