Com os eventos MCP, o ChatGPT pode assinar atualizações do seu servidor MCP, como novas mensagens, alterações de conteúdo ou mudanças de status. Os usuários escolhem o que monitorar e o que o ChatGPT deve fazer quando uma atualização chegar.
| Caso de uso | Pedido do usuário | Evento MCP |
|---|---|---|
| Transformar feedback em pull requests | Monitore o canal #product-feedback para identificar relatos de bugs e abra pull requests em rascunho com correções e testes. | message.created, filtrado por channel_id |
| Aplicar alterações sugeridas em um documento | Monitore este documento para identificar novos comentários de revisão e faça as alterações solicitadas. | comment.created, filtrado por document_id |
Antes de começar
Os eventos MCP estão disponíveis nos chats do modo Work do ChatGPT na Web, nos chats do modo Work do aplicativo para desktop com a opção Nuvem selecionada e com dots. Os controles do espaço de trabalho para plugins e tarefas acionadas por eventos também se aplicam.
O ChatGPT oferece suporte a eventos MCP via Streamable HTTP com initialize ou via MCP 2.0 HTTP (versão 2026-07-28 do protocolo). Configure o servidor no seu plugin, armazene as assinaturas de forma persistente e permita conexões HTTPS de saída para as URLs de callback.
O ChatGPT oferece suporte à entrega por webhook e à verificação de callback previstas na proposta de especificação de eventos MCP. A integração não oferece suporte a consultas periódicas, streaming nem às notificações de controle gap e terminated da proposta.
Como funciona
- Seu servidor lista os eventos que ele oferece.
- O usuário informa ao ChatGPT o que monitorar e como responder.
- O ChatGPT cria uma assinatura por meio do seu servidor MCP e fornece uma URL de callback e uma chave secreta de assinatura.
- O servidor envia os eventos que correspondem à assinatura para essa URL.
- O ChatGPT recebe o evento no chat em que a assinatura foi criada e responde conforme as instruções do usuário.
Indique que o servidor oferece suporte a eventos
A descoberta de eventos começa pelos recursos que o servidor anuncia. Para versões do MCP que usam inicialização, inclua events entre os recursos retornados na resposta a initialize:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {},
"events": {}
},
"serverInfo": {
"name": "example-server",
"version": "1.0.0"
}
}
}
Para o MCP 2.0 (versão 2026-07-28 do protocolo), inclua events entre os recursos retornados na resposta a server/discover:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": {
"tools": {},
"events": {}
}
}
}
Implemente estes três métodos de eventos no mesmo endpoint MCP autenticado que suas ferramentas usam:
| Método | Comportamento do servidor |
|---|---|
events/list |
Descreve os eventos disponíveis e seus filtros. |
events/subscribe |
Cria ou renova uma assinatura. |
events/unsubscribe |
Encerra uma assinatura. |
Defina um evento
A definição de um evento informa ao ChatGPT quais eventos os usuários podem assinar e quais filtros estão disponíveis. Retorne essas definições na resposta a events/list e inclua o nome do evento, os modos de entrega compatíveis, os argumentos da assinatura e o schema do payload.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"events": [
{
"name": "comment.created",
"description": "A new review comment was added to the specified document.",
"delivery": ["webhook"],
"inputSchema": {
"type": "object",
"properties": {
"document_id": {
"type": "string",
"description": "ID of the document to monitor for new review comments."
}
},
"required": ["document_id"],
"additionalProperties": false
},
"payloadSchema": {
"type": "object",
"properties": {
"document_id": { "type": "string" },
"comment_id": { "type": "string" },
"text": { "type": "string" },
"url": { "type": "string" }
},
"required": ["document_id", "comment_id", "text", "url"],
"additionalProperties": false
}
}
]
}
}
inputSchema descreve os argumentos que o ChatGPT envia ao criar a assinatura, enquanto payloadSchema descreve o objeto data de cada evento entregue.
Use nomes de eventos estáveis e descrições específicas. Disponibilize filtros por ID de documento, projeto ou fila, por exemplo, e aplique-os no servidor antes da entrega. Retorne apenas os eventos que a conta conectada tem permissão para descobrir.
Se o catálogo tiver várias páginas, retorne nextCursor e aceite esse valor como cursor na próxima requisição a events/list.
Crie uma assinatura
Quando um usuário pede para monitorar um evento, o ChatGPT chama events/subscribe com o nome do evento, os argumentos de filtragem e o destino do webhook:
{
"jsonrpc": "2.0",
"id": 2,
"method": "events/subscribe",
"params": {
"name": "comment.created",
"arguments": {
"document_id": "doc_123"
},
"delivery": {
"mode": "webhook",
"url": "https://receiver.example.com/mcp-events/callback_123",
"secret": "whsec_<base64-encoded-signing-key>"
},
"cursor": null
}
}
Antes de aceitar a assinatura:
- Verifique se o usuário tem autorização para o evento e os argumentos solicitados.
- Valide o nome e os argumentos com base na definição do evento. Exija uma chave secreta de assinatura com o prefixo
whsec_cujo valor em base64 tenha de 24 a 64 bytes após a decodificação. - Valide e verifique a URL de callback.
- Armazene a assinatura, seu titular, os filtros, a URL de callback, a chave secreta de assinatura e a data e hora de expiração.
Gere um ID determinístico para a assinatura a partir da identidade autenticada, da URL de callback, do nome do evento e dos argumentos. Retorne o ID com o prazo de expiração concedido pelo servidor:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"id": "sub_123",
"refreshBefore": "2026-10-02T12:00:00Z",
"cursor": null,
"truncated": false
}
}
Defina refreshBefore com a data e a hora em que a assinatura expira no servidor. Retorne cursor: null para tipos de evento que não permitem reenvio a partir do histórico.
Torne a criação de assinaturas idempotente. Quando os dados que identificam a assinatura coincidirem com os de uma assinatura existente, atualize-a. Compare os argumentos usando JSON canônico para que a ordem das chaves dos objetos não gere assinaturas duplicadas.
Verifique o callback
Antes de enviar dados do aplicativo, verifique o callback enviando uma requisição assinada com um novo desafio de uso único e curta duração:
{
"type": "verification",
"challenge": "a-single-use-random-value"
}
Atribua à requisição de verificação um webhook-id exclusivo, como msg_verification_123, e assine o corpo com a chave secreta da assinatura. Inclua webhook-timestamp, webhook-signature e X-MCP-Subscription-Id. Se a verificação for bem-sucedida, o ChatGPT devolve o desafio na resposta:
{
"challenge": "a-single-use-random-value"
}
Antes de ativar a entrega, exija uma resposta 2xx e compare o desafio retornado com o original em tempo constante. Mantenha em cache, por tempo limitado, o resultado das verificações bem-sucedidas por identidade autenticada e URL de callback. Assim, requisições de assinatura repetidas não geram novos desafios. Se a verificação falhar, retorne o erro JSON-RPC -32015 (CallbackEndpointError) e indique a categoria da falha em data.reason, por exemplo, challenge_failed ou timeout.
Exija HTTPS para callbacks. Resolva e valide os endereços de destino no momento da conexão. Em seguida, conecte-se ao endereço validado e preserve o nome de host original para a verificação TLS. Bloqueie endereços privados, locais e quaisquer outros endereços que não sejam públicos. Não siga redirecionamentos. Aplique essas verificações tanto às requisições de verificação quanto às entregas de eventos.
Envie um evento
Quando um novo comentário corresponder a uma assinatura ativa, envie um único objeto de evento em uma requisição POST para a URL de callback dessa assinatura:
{
"eventId": "evt_456",
"name": "comment.created",
"timestamp": "2026-10-01T12:05:00Z",
"data": {
"document_id": "doc_123",
"comment_id": "comment_456",
"text": "Can we add the rollout dates to this section?",
"url": "https://docs.example.com/doc_123#comment_456"
},
"cursor": null
}
Use um ID exclusivo para o evento e mantenha esse ID em todas as tentativas de entrega. Defina timestamp como a data e hora em que o evento ocorreu, no formato ISO 8601 e com fuso horário. O campo name deve corresponder ao evento da assinatura, e o objeto data deve seguir o respectivo payloadSchema. Mantenha os campos do aplicativo dentro de data. Um campo type no nível superior identifica uma notificação de controle do protocolo.
Para registros grandes, envie um resumo e disponibilize uma ferramenta de leitura para consultar o registro completo. Trate comentários e outros textos escritos pelos usuários como dados. Não inclua no payload do evento instruções sobre como o modelo deve se comportar.
Assine a requisição
O ChatGPT verifica as entregas usando Standard Webhooks. Envie os seguintes cabeçalhos:
| Cabeçalho | Valor |
|---|---|
Content-Type |
application/json |
webhook-id |
O mesmo valor de eventId no corpo da requisição. |
webhook-timestamp |
O instante da assinatura em segundos Unix. |
webhook-signature |
A assinatura HMAC do Standard Webhooks. |
X-MCP-Subscription-Id |
O ID retornado por events/subscribe. |
Assine as entregas usando Standard Webhooks e a chave secreta da assinatura do evento. Como a assinatura criptográfica abrange o ID do evento, o instante da assinatura e os bytes exatos do corpo da requisição, serialize o corpo uma única vez e envie esses mesmos bytes.
Envie um evento assinado com Node.js
Instale a biblioteca Standard Webhooks:
npm install standardwebhooks
Passe uma função webhookFetch com a mesma interface de fetch. A função deve validar os endereços de callback a cada conexão e bloquear redirecionamentos.
import { Webhook } from "standardwebhooks";
export async function sendEvent(subscription, event, webhookFetch) {
const body = JSON.stringify(event);
if (Buffer.byteLength(body, "utf8") > 256 * 1024) {
throw new Error("Event payload exceeds 256 KiB");
}
const signedAt = new Date();
const signer = new Webhook(subscription.secret);
const response = await webhookFetch(subscription.url, {
method: "POST",
redirect: "error",
signal: AbortSignal.timeout(10_000),
headers: {
"Content-Type": "application/json",
"webhook-id": event.eventId,
"webhook-timestamp": String(Math.floor(signedAt.getTime() / 1000)),
"webhook-signature": signer.sign(event.eventId, signedAt, body),
"X-MCP-Subscription-Id": subscription.id,
},
body,
});
return { accepted: response.ok, status: response.status };
}Defina subscription.url e subscription.secret a partir do objeto delivery da requisição de assinatura.
Trate as respostas às entregas
Uma resposta 2xx confirma o recebimento do webhook. O ChatGPT processa o evento de forma assíncrona.
Envie um evento por requisição. O corpo completo da requisição pode ter no máximo 256 KiB (262.144 bytes). O ChatGPT pode agrupar eventos entregues separadamente em uma única execução da tarefa, conforme as configurações de processamento em lote da tarefa.
Em caso de falha temporária, repita a entrega com intervalos que aumentem exponencialmente e limite o número de tentativas. Mantenha o ID do evento e gere uma nova marca de tempo de assinatura e uma nova assinatura criptográfica a cada tentativa. Não repita entregas que retornem 410 ou 413.
Os eventos podem chegar fora de ordem. Torne as ferramentas de escrita idempotentes para que chamadas repetidas não dupliquem as alterações.
Gerencie as assinaturas
Preserve o estado da assinatura durante todo o período de validade concedido, inclusive após reinicializações do servidor. Verifique novamente as permissões do usuário durante esse período e interrompa a entrega se o acesso for revogado.
Renove uma assinatura
Para renovar uma assinatura prestes a expirar, o ChatGPT chama events/subscribe antes do prazo indicado em refreshBefore, usando os mesmos dados de identificação da assinatura e o último cursor salvo. Atualize a assinatura existente e retorne a nova data e hora de expiração em refreshBefore.
Quando ttlMs for omitido, use o período de validade padrão do servidor para assinaturas. Quando informado, esse campo especifica o período de validade solicitado, em milissegundos. Não conceda um período maior que o solicitado, exceto quando for necessário aplicar um período mínimo para evitar um número excessivo de requisições de renovação.
ttlMs: null solicita uma assinatura sem prazo de expiração. Retorne refreshBefore: null somente se conceder a assinatura nessas condições. Caso contrário, retorne uma data e hora de expiração e interrompa a entrega quando esse prazo terminar.
Se a requisição de renovação fornecer uma nova chave secreta de assinatura, substitua a chave armazenada. Durante um breve período de rotação, assine com a chave antiga e a nova, separando as assinaturas por espaço conforme o padrão Standard Webhooks.
Para eventos que permitem reenvio a partir do histórico, use o cursor da requisição para retomar a entrega após a expiração da assinatura ou a reinicialização do servidor. Nas respostas às requisições de assinatura e nos payloads dos eventos, retorne um cursor que não pule eventos ainda pendentes de entrega. Retorne truncated: true quando o histórico solicitado não estiver mais disponível. Para tipos de evento que não permitem reenvio a partir do histórico, retorne cursor: null. Nesses casos, o protocolo não permite recuperar eventos perdidos durante uma interrupção.
Encerre uma assinatura
Processe events/unsubscribe usando o nome do evento, os argumentos e a URL de callback da assinatura original:
{
"jsonrpc": "2.0",
"id": 3,
"method": "events/unsubscribe",
"params": {
"name": "comment.created",
"arguments": {
"document_id": "doc_123"
},
"delivery": {
"mode": "webhook",
"url": "https://receiver.example.com/mcp-events/callback_123"
}
}
}
Interrompa o envio de eventos para a assinatura correspondente e retorne um resultado vazio:
{
"jsonrpc": "2.0",
"id": 3,
"result": {}
}
Torne o cancelamento idempotente e verifique se a conta conectada tem permissão para cancelar a assinatura.
Teste no ChatGPT
Depois de implementar os métodos de eventos e a entrega por webhook, conecte seu servidor MCP ao ChatGPT por meio de um plugin e teste todo o ciclo de vida da assinatura:
-
Confirme que o servidor anuncia
eventspor meio deinitializeouserver/discovere que, em seguida, recebeevents/liste retorna as definições de eventos esperadas. -
Verifique se os eventos aparecem na página do plugin ao lado das ferramentas. Faça uma nova varredura do servidor MCP sempre que alterar suas ferramentas ou eventos.
Eventos do plugin. Os eventos descobertos aparecem ao lado das ferramentas na página do plugin.
-
Inicie um chat no modo Work do ChatGPT na Web ou selecione Work e Nuvem no aplicativo para desktop. Peça ao ChatGPT para assinar um dos seus eventos e especifique o que ele deve fazer quando um evento chegar. Você também pode testar com um dot.
-
Confirme que o servidor recebe
events/subscribecom o nome do evento e os argumentos esperados. -
Confirme que a verificação do callback é bem-sucedida e que o servidor armazena a assinatura.
-
Acione no aplicativo um evento que corresponda à assinatura e confirme que a entrega por webhook recebe uma resposta
2xx. -
Verifique se o ChatGPT recebe os dados esperados do evento e responde conforme as instruções.
-
Para assinaturas com filtros, acione um evento que não corresponda aos filtros e confirme que ele não é entregue à assinatura.
-
Interrompa o monitoramento no ChatGPT. Confirme que o servidor processa
events/unsubscribee interrompe a entrega.
Teste também requisições de assinatura repetidas, a expiração e a renovação de assinaturas após uma reinicialização do servidor, a desconexão da conta, a revogação do acesso a um recurso monitorado por assinatura, assinaturas criptográficas inválidas, entregas duplicadas e picos de eventos com o processamento em lote ativado e desativado. Se a ação solicitada alterar dados no aplicativo de origem, verifique se os eventos resultantes não acionam a mesma ação em um ciclo contínuo.
Referência do protocolo: esboço do protocolo de eventos MCP.