Agentes que despiertan con MCP Events: prepara tu servidor
Cómo preparar un servidor para MCP Events en ChatGPT y qué anunció Cloudflare para Workers Issues: suscripción, callback, firmas y riesgos.
Por Andrés León Bohórquez Actualizado el
Datos a 15 de octubre de 2026 · especificación MCP 2026-07-28
MCP Events es un borrador para que un servidor MCP avise a un cliente cuando ocurre algo, sin esperar a que este pregunte. Para que ChatGPT reciba esos avisos, tu servidor debe anunciar sus eventos, verificar una URL de callback (la dirección a la que enviará los avisos) y enviar peticiones POST firmadas, según la guía de OpenAI.
Este artículo se centra en esa parte práctica. Verás también qué anunció Cloudflare el 13 de octubre de 2026 para Workers Issues. Después lo situamos frente al borrador del grupo de trabajo y la especificación 2026-07-28. Cerramos con los riesgos que asumes cuando un evento pone en marcha a un agente sin nadie mirando.
Para seguirlo necesitas saber qué es un servidor MCP y qué es una tool, la función que ese servidor ofrece al agente. Si no lo tienes claro, empieza por qué es MCP o consulta el glosario de MCP. Un webhook es una URL tuya a la que otro servicio hace una petición HTTP cuando ocurre algo.
Qué es el borrador y dónde está en el roadmap lo explicamos en MCP Events y webhooks: cuando el servidor avisa al agente. Aquí solo repetimos lo imprescindible.
Antes de empezar, una advertencia que vamos a repetir. Hay cuatro cosas distintas en juego y conviene no mezclarlas:
- El borrador del WG Triggers & Events, el grupo de trabajo (working group) de MCP que lo diseña, y que no forma parte de la especificación 2026-07-28.
- La implementación de OpenAI en ChatGPT, que hace de cliente (la aplicación que se conecta al servidor) e implementa una parte de ese borrador.
- El anuncio de Cloudflare, que hace de servidor con un evento concreto.
- La opinión de Nathan Baschez en X, que motiva este artículo.
El problema: un agente que solo despierta por cron o por un mensaje
Un agente que solo arranca por una tarea programada (un cron) o por un mensaje no se entera de lo que pasa fuera de la conversación. Solo le queda preguntar cada cierto tiempo.
El 3 de octubre, Nathan Baschez escribió en X que no ve «ni de lejos el entusiasmo suficiente» por MCP Events. Su argumento es que hoy la mayoría de los agentes solo se ponen en marcha por un cron o porque alguien les escribe. Por eso, en sus palabras, «Event-driven triggers are a huge deal». Es una opinión, no un dato, pero señala algo real del diseño.
Pongamos un ejemplo. Nortia Logística es la empresa ficticia del curso y nortia-almacen, su servidor de inventario. Si el agente tiene que enterarse de que el stock del almacén de Barcelona ha caído por debajo del mínimo, solo puede preguntar cada cierto tiempo (hacer polling). Si pregunta a menudo, gasta llamadas y tokens en confirmar que no ha pasado nada. Si pregunta poco, llega tarde. El mensaje del usuario tampoco ayuda, porque el usuario no sabe que el stock acaba de caer, y precisamente por eso quería un agente.
Con un disparador por evento (el servidor avisa en cuanto algo cambia), el diseño cambia en tres puntos:
- La lógica de “¿ha pasado algo?” se muda al servidor. El servidor sabe cuándo cambia su dominio, así que es él quien filtra.
- El agente arranca con contexto. Llega con el evento en la mano (qué pasó, dónde, un identificador) en lugar de con “comprueba si hay novedades”.
- Aparece una pregunta nueva: quién decide qué hace el agente cuando nadie está delante. Volveremos a ella, porque es la que más importa.
La especificación 2026-07-28 no resuelve esto de forma directa. El servidor no puede iniciar peticiones, y subscriptions/listen solo entrega avisos mientras el cliente mantiene abierto el stream, es decir, la conexión que sigue abierta para recibirlos. Si el evento ocurre cuando no hay nadie escuchando, se pierde.
Cómo funciona MCP Events en ChatGPT
ChatGPT se suscribe a los eventos de tu servidor con tres métodos de MCP y recibe cada evento como un POST firmado a una URL suya. La guía de OpenAI describe qué tiene que implementar tu servidor, y todo lo de esta sección es implementación de OpenAI, no protocolo estándar.
Lo seguimos con un ejemplo: nortia-almacen quiere avisar a ChatGPT cuando un SKU (una referencia de producto) entra en alerta de rotura de stock.
Primero, las condiciones. Según la guía, MCP Events está disponible en los chats Work de ChatGPT web y en los chats Work de la app de escritorio con Cloud seleccionado y con dots. Además, se aplican los controles del espacio de trabajo sobre plugins y tareas disparadas por eventos. La guía exige la versión 2026-07-28 del protocolo, que OpenAI llama “MCP 2.0” (el apodo lo explicamos en qué es “MCP 2.0”). Y pide al servidor dos cosas que no todos tienen: almacenamiento persistente de suscripciones y salida HTTPS hacia las URL de callback.
La guía también acota qué parte del borrador implementa, que es la entrega por webhook y la verificación del callback. En sus palabras, “Polling, streaming, and the draft’s gap and terminated control notifications are not supported by this integration”. Más adelante veremos lo que implica no tener terminated, el aviso con el que el servidor comunica que una suscripción ha terminado.
Según OpenAI, el flujo tiene cinco pasos:
- El servidor lista los eventos que soporta.
- El usuario le dice a ChatGPT qué vigilar y cómo responder.
- ChatGPT se suscribe a través del servidor y le da una URL de callback y un secreto de firma.
- El servidor envía a esa URL los eventos que coinciden.
- ChatGPT recibe el evento en el chat suscrito y sigue las instrucciones del usuario.
sequenceDiagram
autonumber
participant C as ChatGPT (cliente MCP)
participant S as nortia-almacen (servidor MCP)
participant R as Receptor del callback (URL de ChatGPT)
C->>S: server/discover
S-->>C: capabilities con events
C->>S: events/list
S-->>C: events con name, delivery, inputSchema, payloadSchema
Note over C: El usuario pide vigilar un almacén y dice qué hacer
C->>S: events/subscribe con name, arguments, delivery (mode, url, secret), cursor
Note over S: Autoriza, valida argumentos y secreto whsec_
S->>R: POST type verification + challenge, firmado
R-->>S: 2xx con el mismo challenge
Note over S: Compara en tiempo constante y guarda la suscripción
S-->>C: id, refreshBefore, cursor, truncated
loop cada evento que cumple los filtros
S->>R: POST eventId, name, timestamp, data, cursor + webhook-id, webhook-timestamp, webhook-signature, X-MCP-Subscription-Id
R-->>S: 2xx (ChatGPT procesa después, de forma asíncrona)
end
loop antes de cada refreshBefore
C->>S: events/subscribe (misma identidad y último cursor)
S-->>C: nuevo refreshBefore
end
C->>S: events/unsubscribe con name, arguments, delivery.url
S-->>C: resultado vacío 1. Anunciar el soporte
El descubrimiento empieza en server/discover, la llamada con la que el cliente pregunta qué sabe hacer el servidor. Ahí el servidor añade events a sus capabilities (las funciones que declara soportar). Después implementa tres métodos “on the same authenticated MCP endpoint as your tools”, según la guía:
events/list, para describir los eventos y sus filtrosevents/subscribe, para crear o refrescar una suscripciónevents/unsubscribe, para pararla.
Un apunte sobre los nombres. En el artículo anterior evitamos citar métodos del borrador porque puede cambiar. Aquí los citamos porque OpenAI los documenta para su implementación. Si quieres que ChatGPT llame a tu servidor, son los que va a usar, aunque eso no los convierte en estándar.
2. Definir un evento
Un evento es un aviso con nombre, como «alerta de stock creada». Por cada uno, events/list devuelve:
- su nombre y una descripción
- los modos de entrega
- un
inputSchema, un esquema JSON con los argumentos de suscripción - un
payloadSchema, otro esquema con la forma del objetodata(el payload) de cada entrega.
La guía pide nombres estables, descripciones concretas y filtros (documento, proyecto, cola) que el servidor aplique antes de entregar. Añade una regla de autorización que se olvida con facilidad: devolver solo los eventos que la cuenta conectada puede descubrir.
Para Nortia, el candidato obvio es la alerta de rotura de stock de nortia-almacen. En la lección de operaciones largas y tiempo real del nivel Avanzado, esa alerta llega a un panel por subscriptions/listen mientras el panel está abierto. Como evento, quedaría así:
{
"name": "alerta_stock.creada",
"description": "Un SKU del almacén indicado ha entrado en alerta de rotura de stock.",
"delivery": ["webhook"],
"inputSchema": {
"type": "object",
"properties": {
"almacen": { "type": "string", "description": "Código del almacén a vigilar, por ejemplo BCN-02." }
},
"required": ["almacen"],
"additionalProperties": false
},
"payloadSchema": {
"type": "object",
"properties": {
"alerta_id": { "type": "string" },
"almacen": { "type": "string" },
"sku": { "type": "string" },
"estado": { "type": "string" }
},
"required": ["alerta_id", "almacen", "sku", "estado"],
"additionalProperties": false
}
}
Ejemplo escrito para este artículo, no ejecutado. La forma sigue la definición de evento de la guía de OpenAI; el nombre y los campos son nuestros.
Fíjate en lo que falta en el payload: ni nombre del producto, ni descripción del proveedor, ni cantidades por ubicación. Para registros grandes, la guía recomienda enviar un resumen y exponer una tool de lectura para el registro completo. nortia-almacen ya tiene esas tools (consultar_existencias y alertas_rotura_stock), así que el agente puede leer los detalles con los permisos de siempre.
3. Crear y verificar la suscripción
Cuando el usuario pide vigilar un evento, ChatGPT llama a events/subscribe con el nombre, los argumentos de filtro y un objeto delivery con modo webhook, la URL y el secreto. Antes de aceptar, la guía enumera cuatro pasos:
- Comprobar que el usuario está autorizado para ese evento con esos argumentos.
- Validar nombre y argumentos contra la definición y exigir un secreto
whsec_cuyo valor en base64 decodifique a entre 24 y 64 bytes. - Validar y verificar el callback.
- Guardar la suscripción con su propietario, filtros, URL, secreto y caducidad.
El identificador se deriva de forma determinista del principal autenticado (la cuenta que hace la petición), la URL de callback, el nombre del evento y los argumentos. La creación tiene que ser idempotente, es decir, repetir la misma petición no crea una suscripción nueva. Para eso, los argumentos se comparan como JSON canónico y así el orden de las claves no genera duplicados. Una forma de hacerlo en Python, el lenguaje de nortia-almacen en el curso:
# Ejemplo escrito para este artículo, no ejecutado.
import hashlib
import json
def id_suscripcion(principal: str, url: str, nombre: str, argumentos: dict) -> str:
# JSON canónico: claves ordenadas y sin espacios, para que {"a":1,"b":2} y {"b":2,"a":1} den el mismo id
clave = json.dumps([principal, url, nombre, argumentos], sort_keys=True, separators=(",", ":"), ensure_ascii=False)
return "sub_" + hashlib.sha256(clave.encode("utf-8")).hexdigest()[:16]
El borrador propone lo mismo (“a truncated SHA-256 of the canonical key serialization”) y deja claro que ese id es un manejador de enrutado, no una credencial: conocerlo no autoriza nada.
La verificación del callback impide que alguien use tu servidor para inundar una URL ajena. Antes de enviar datos, el servidor manda a la URL un cuerpo {"type": "verification", "challenge": ...} con un valor de un solo uso y vida corta. La petición lleva su propio webhook-id único (la guía pone de ejemplo msg_verification_123). Va firmada con el secreto de la suscripción e incluye webhook-timestamp, webhook-signature y X-MCP-Subscription-Id.
ChatGPT responde con el mismo challenge. Tu servidor exige un 2xx y compara el valor en tiempo constante. La guía pide además cachear la verificación por principal y URL durante un periodo acotado. Si la verificación falla, devuelves el error JSON-RPC (el formato de mensajes que usa MCP) -32015 (CallbackEndpointError) con un data.reason categorizado, como challenge_failed o timeout.
La respuesta de events/subscribe lleva id, refreshBefore (la caducidad que concedes), cursor y truncated. El cursor es un marcador de posición en el historial de eventos, y más abajo explicamos cómo se usa. Si tu evento no permite reproducir eventos pasados, devuelve cursor: null.
4. Enviar eventos firmados
Tu servidor envía cada evento que coincide con una suscripción activa como un POST con un único objeto, que lleva eventId, name, timestamp, data y cursor. Para la alerta de Nortia:
{
"eventId": "alerta_BCN-02_7731",
"name": "alerta_stock.creada",
"timestamp": "2026-10-04T03:12:00+02:00",
"data": {
"alerta_id": "7731",
"almacen": "BCN-02",
"sku": "PAL-EUR-120",
"estado": "bajo"
},
"cursor": null
}
Ejemplo escrito para este artículo, no ejecutado. Identificadores inventados.
La guía fija estas reglas para el cuerpo:
- un
eventIdúnico que se conserva en los reintentos - un
timestampISO 8601 con zona horaria que indica cuándo ocurrió el evento - un
nameigual al suscrito - un
dataque cumple elpayloadSchema.
Los campos de aplicación van dentro de data, porque un type en el nivel superior identifica una notificación de control del protocolo.
Las cabeceras siguen Standard Webhooks: Content-Type: application/json, webhook-id (con el mismo valor que eventId), webhook-timestamp (en segundos Unix), webhook-signature y la cabecera propia X-MCP-Subscription-Id.
Esa firma es un HMAC, un código que solo puede calcular quien conoce el secreto compartido, y así el receptor comprueba que el aviso viene de tu servidor. El borrador da la fórmula: HMAC-SHA256(secret, webhook-id + "." + webhook-timestamp + "." + body) en base64 con prefijo v1,. El secreto son los bytes que resultan de decodificar lo que va detrás de whsec_. Como la firma cubre los bytes exactos del cuerpo, la guía insiste en serializar una vez y enviar esos mismos bytes.
# Ejemplo escrito para este artículo, no ejecutado. En producción, usa una
# biblioteca de Standard Webhooks en lugar de firmar a mano.
import base64
import hashlib
import hmac
import time
def cabeceras_firmadas(secreto: str, event_id: str, id_sub: str, cuerpo: bytes) -> dict[str, str]:
clave = base64.b64decode(secreto.removeprefix("whsec_"))
ts = str(int(time.time())) # se regenera en cada intento
firma = hmac.new(clave, f"{event_id}.{ts}.".encode() + cuerpo, hashlib.sha256).digest()
return {
"Content-Type": "application/json",
"webhook-id": event_id,
"webhook-timestamp": ts,
"webhook-signature": "v1," + base64.b64encode(firma).decode(),
"X-MCP-Subscription-Id": id_sub,
}
La guía de OpenAI trae su propio ejemplo en Node.js con la biblioteca standardwebhooks. Merece la pena leerlo por dos detalles que no son de firma. Rechaza cuerpos de más de 256 KiB y pasa una función webhookFetch “that validates callback addresses on each connection and blocks redirects”.
5. Entender las respuestas de entrega
Un 2xx solo confirma la recepción. ChatGPT procesa el evento después, de forma asíncrona. La guía fija un evento por petición y un cuerpo de 256 KiB como máximo. También avisa de que ChatGPT puede agrupar eventos entregados por separado en una sola ejecución de la tarea (batching), según su configuración.
Ante fallos transitorios, tu servidor reintenta con backoff exponencial, es decir, esperando cada vez más entre intentos, y con un número acotado de ellos. En cada intento conserva el eventId y genera timestamp y firma nuevos. Un 410 o un 413 no se reintentan. La guía añade una advertencia que afecta a tus tools, no a tus webhooks: “Events can arrive out of order. Make write tools idempotent so repeated calls do not duplicate changes.”
6. Renovar y parar suscripciones
El servidor conserva el estado de cada suscripción durante la vida que concedió, también tras un reinicio. Mientras dure, vuelve a comprobar el acceso del usuario, y si ese acceso se revoca, deja de entregar.
ChatGPT renueva llamando otra vez a events/subscribe antes de refreshBefore, con la misma identidad y el último cursor guardado. El campo ttlMs es el TTL (tiempo de vida) que pide el cliente, y funciona así:
- Si no viene, aplicas tu valor por defecto.
- Si viene, no concedes más, salvo para imponer un mínimo que evite renovaciones excesivas.
ttlMs: nullpide una suscripción sin caducidad, y solo en ese caso puedes devolverrefreshBefore: null.
Si la renovación trae un secreto nuevo, lo sustituyes y firmas con los dos durante una ventana corta.
Para parar, ChatGPT envía events/unsubscribe con el nombre, los argumentos y la URL originales, y tú respondes con un resultado vacío. Tiene que ser idempotente y estar autorizado contra la cuenta conectada.
7. Probar en ChatGPT
La guía termina con una lista de pruebas sobre el ciclo completo. Comprueba que:
- llegan
server/discoveryevents/list - los eventos aparecen en la página del plugin junto a las tools
- la verificación funciona
- un evento que no cumple los filtros no se entrega
events/unsubscribecorta la entrega.
Le sigue una lista extra que conviene convertir en tests automáticos: suscripciones repetidas, caducidad y renovación a través de un reinicio, desconexión de la cuenta, acceso revocado, firmas inválidas, entregas duplicadas y ráfagas con batching activado y desactivado.
La última frase de la guía es la más importante para un agente. Si la acción cambia datos en la aplicación de origen, comprueba que los eventos resultantes no crean un bucle.
Un caso real: Cloudflare Workers Issues
El servidor MCP de Cloudflare puede avisar a un cliente por webhook cuando uno de tus Workers (el código que Cloudflare ejecuta por ti) tiene un problema. El 13 de octubre de 2026 anunció el primer evento de su servidor MCP de API (Cloudflare API MCP server). Se llama cloudflare.alert.workers_observability_real_time_issue y salta cuando Workers Issues detecta un problema nuevo o recurrente en un Worker. Aquí Cloudflare hace de servidor, el papel que nortia-almacen tenía en la sección anterior.
Workers Issues es la función de observabilidad de Cloudflare que detecta excepciones sin capturar, invocaciones fallidas, respuestas 5xx y errores registrados. Agrupa las ocurrencias relacionadas del mismo Worker y no necesita SDK ni envolver tu código. Está en beta abierta y es gratuita durante la beta.
El flujo del anuncio usa los mismos nombres de método que la guía de OpenAI:
- El cliente descubre el evento con
events/list. - Se suscribe con
events/subscribey limita el alcance a una cuenta o a un Worker. - Ante un problema nuevo o recurrente, el servidor hace un POST con un evento firmado al webhook del cliente.
- El cliente puede empezar a investigar sin hacer polling, es decir, sin preguntar cada poco si hay novedades.
Para activarlo, el anuncio pide tres requisitos:
- Workers Issues activado en el Worker.
- Una conexión a
https://mcp.cloudflare.com/mcpcon los permisos Notifications Write y Workers Observability Write. - Un cliente compatible con MCP Events por webhook.
Issues se activa desde el dashboard, con Wrangler o con cf. Con Wrangler 4.134.0 o posterior, deja observability.issues.enabled: true en la configuración del Worker. Si lo activas solo en el dashboard, el siguiente despliegue con Wrangler lo desactiva.
Importante: Issues solo procesa el tráfico posterior a la activación. No reconstruye fallos anteriores, así que el evento no te avisará de algo que ya fallaba antes.
El anuncio tampoco promete que el agente arregle nada. Los dos permisos son los que pide para este evento, y en ningún momento dice que sirvan para desplegar.
Cómo sería en Nortia
Nortia es ficticia. Supón que alojara en Workers la página pública de seguimiento de envíos. Así encajaría la función anunciada:
- La ingeniera de plataforma activa Issues en ese Worker y deja la opción en la configuración de Wrangler.
- Un cliente compatible se suscribe al evento con el alcance de ese Worker.
- Tras un despliegue, el Worker empieza a devolver
5xx. Issues lo agrupa como problema nuevo y el servidor de Cloudflare envía el evento firmado al webhook del cliente. - El agente podría leer los detalles de la incidencia con los permisos que ya tiene y proponer un diagnóstico.
- Una persona decide si se revierte el despliegue.
El quinto paso es recomendación nuestra, no de Cloudflare. Sale del principio del borrador que veremos en «Quién decide qué hace el agente»: recibir un evento no autoriza a actuar.
flowchart LR
subgraph N["Núcleo 2026-07-28: subscriptions/listen"]
direction TB
C1["Cliente MCP<br/>mantiene abierta la petición"]
S1["Servidor MCP"]
C1 -->|"subscriptions/listen"| S1
S1 -.->|"avisos de tools, prompts y resources<br/>solo mientras el stream sigue abierto"| C1
end
subgraph E["Borrador MCP Events por webhook: Workers Issues"]
direction TB
W["Worker de Nortia<br/>(hipotético)"]
I["Workers Issues<br/>problema nuevo o recurrente"]
CF["Cloudflare API MCP server<br/>(servidor)"]
CL["Cliente compatible<br/>con URL de webhook"]
AG["Agente: investiga<br/>con sus permisos normales"]
W --> I --> CF
CL -->|"events/list y events/subscribe<br/>(cuenta o Worker)"| CF
CF -->|"POST firmado al webhook"| CL
CL --> AG
end La diferencia con subscriptions/listen está en quién espera a quién. En el núcleo 2026-07-28, el cliente mantiene abierta una petición y los avisos llegan por su respuesta. Con el webhook, el cliente deja una URL y el servidor la llama cuando pasa algo, aunque no haya ninguna conexión abierta.
Lo que el anuncio no cuenta
Cloudflare no publica un ejemplo del cuerpo del evento. La ficha de una incidencia muestra cuándo empezó, su frecuencia y su estado y, según la telemetría, stack trace, logs, trazas, versión y datos de la invocación. No sabemos cuáles de esos campos viajan en el evento.
El anuncio dice que el evento va firmado, pero no cómo. Por eso no damos por hecho que Cloudflare use la firma, el cursor o los reintentos del borrador que describimos en este artículo. Los ejemplos JSON y Python de la sección anterior son de Nortia y siguen la guía de OpenAI. No son mensajes de Cloudflare.
Cloudflare tampoco nombra un cliente. OpenAI documenta ChatGPT como receptor de webhooks del borrador. Vercel documenta en Connect un adaptador experimental que recibe esos webhooks desde servidores MCP, los verifica y los reenvía a una aplicación. Ninguna de nuestras fuentes muestra a ninguno de los dos recibiendo eventos de Cloudflare.
Nosotros no hemos conectado una cuenta ni capturado un webhook, así que no tenemos payload ni latencia medida. Nada de esto mete MCP Events en la especificación 2026-07-28. Cloudflare enlaza desde su anuncio al repositorio experimental; OpenAI también describe su integración como parte del borrador.
Cómo encaja con el borrador y con la especificación 2026-07-28
El design sketch (el boceto de diseño del grupo de trabajo) define tres modos de entrega, y ChatGPT implementa uno:
- Poll. El cliente pregunta y el servidor le sugiere cada cuánto volver.
- Push. Los eventos llegan por una petición de larga duración. Según el borrador, el servidor debería enviar un heartbeat (una señal vacía de «sigo aquí») al menos cada 30 segundos.
- Webhook. Es el que usa ChatGPT y el que anuncia Cloudflare para Workers Issues.
Ninguno es obligatorio. El borrador justifica tener tres porque las topologías no se solapan: un servidor serverless (sin proceso permanente) no puede hacer push y un cliente detrás de NAT (sin dirección pública) no puede recibir webhooks.
Dos piezas del borrador explican decisiones de la guía de OpenAI:
Cursores. Son posiciones opacas que guarda el cliente, no el servidor. En webhook, el cursor de cada entrega es una “safe-to-persist watermark”. Todo lo que queda en esa posición o antes ya lo ha confirmado el receptor o lo ha dado por perdido el servidor. Por eso guardarlo no salta eventos pendientes de entrega, aunque las entregas lleguen desordenadas.
Un parámetro maxAgeMs limita cuánto se reproduce, y truncated: true avisa de que se saltaron eventos. Si tu fuente no tiene historial direccionable, devuelves cursor: null y lo que pase durante un corte se pierde. La alerta de stock de Nortia sale de movimientos de inventario guardados en base de datos, así que podría tener cursor. Un evento que solo existe al vuelo, no.
TTL. El cliente sugiere, el servidor concede y lo concedido manda. El borrador recomienda concesiones de “a few minutes up to about a day” y lo plantea como el mando de recursos del servidor.
Con TTL cortos, el estado puede vivir en memoria y se recupera con la renovación. Con TTL largos o sin caducidad, el servidor asume la durabilidad. Aquí hay una diferencia práctica: la guía de OpenAI pide almacenamiento persistente de suscripciones sin matices, mientras que el borrador admite estado en memoria con TTL cortos.
Así queda frente a lo que sí es protocolo hoy. Tasks es una extensión oficial para operaciones largas que el cliente inicia y consulta después.
| Mecanismo | Estado | Qué resuelve | Qué no resuelve |
|---|---|---|---|
subscriptions/listen |
Núcleo de 2026-07-28 | Cambios de listas y de resources concretos mientras el stream está abierto | Eventos de dominio; avisos cuando no hay nadie conectado; no hay reanudación |
Tasks (io.modelcontextprotocol/tasks) |
Extensión oficial | Estado y resultado de una operación larga que el cliente inició, consultable con tasks/get |
Eventos que el cliente no pidió |
| MCP Events | Borrador. Cloudflare lo anuncia como servidor; ChatGPT y Vercel documentan receptores webhook | Eventos de dominio filtrados, entregados aunque no haya conexión abierta | Interoperabilidad: no consta una prueba conjunta entre esas implementaciones |
El borrador es consciente del solapamiento. Entre sus preguntas abiertas plantea dos:
- si las suscripciones a resources y las notificaciones
list_changeddeberían expresarse como eventos - si los cambios de estado de una task deberían exponerse como evento, para que un cliente que lanzó una tarea y se desconectó reciba el final por poll o webhook: “kick off a long job, close the laptop, get notified when done”.
Mientras eso no se decida, la regla práctica para Nortia es sencilla:
- La exportación mensual encaja con Tasks, porque la pide el usuario y tarda.
- La alerta de stock es un evento, porque no la pide nadie.
- El panel que ya está abierto sigue usando
subscriptions/listen.
Las tres piezas, con su código, están en la lección de operaciones largas del nivel Avanzado y en «Interacción con el usuario y extensiones» del temario.
Mirada crítica
Antes de construir conviene revisar cuatro cosas: qué riesgos de seguridad abre el webhook, cuánto cuesta cada evento, quién decide qué hace el agente y en qué estado está el borrador.
Seguridad: tu servidor pasa a hacer peticiones a URL ajenas
Lo que más cambia en el modelo de amenazas no es el agente, sino tu servidor. Hasta ahora solo respondía. Con webhooks, hace peticiones HTTP salientes a URL que le da un cliente. Eso abre tres frentes, y las dos fuentes coinciden en cómo cerrarlos:
- SSRF. Es un ataque en el que alguien consigue que tu servidor haga peticiones a direcciones internas que no debería tocar. Lo vemos con detalle en SSRF en servidores MCP. El borrador pide HTTPS, resolver y validar la dirección al conectar y bloquear direcciones privadas y locales. También pide conectar a la dirección validada conservando el nombre de host para TLS y no seguir redirecciones. Un ataque de DNS rebinding puede devolver una IP pública al suscribir y una privada al entregar; por eso el servidor debe repetir la validación en cada conexión, incluida la verificación del callback.
- Inundación. La firma HMAC impide que alguien engañe al receptor, pero no que lo inunde. Cualquiera podría suscribir la URL de una víctima con un secreto propio. Para eso existe la verificación del callback. ChatGPT usa el desafío. El borrador admite también listas permitidas, verificación previa por otro canal y un documento publicado por el receptor.
- Fugas por los errores. El borrador exige que los fallos de entrega se reporten con categorías fijas, nunca con el cuerpo o las cabeceras de la respuesta. Así tu servidor no sirve de oráculo con el que sondear servicios internos.
A eso súmale algo evidente. Ahora guardas un secreto de firma por suscripción, y la guía de OpenAI te pide guardarlo de forma persistente, así que trátalo como cualquier otra credencial. La lección de seguridad avanzada del nivel Experto recorre este tipo de fronteras de confianza. Y lo que enseñan dos meses de avisos de seguridad muestra lo que pasa cuando se descuidan.
En reintentos e idempotencia, el reparto es claro. Tu parte es el eventId estable, que permite al receptor descartar duplicados. Pero un evento entregado una sola vez puede acabar en dos acciones si el agente reintenta una tool, y los eventos pueden llegar desordenados. Por eso la guía pide tools de escritura idempotentes. En Nortia, una tool que crea un pedido de reposición debería aceptar una clave como el alerta_id y no crear el segundo.
Coste y ruido
El borrador lo dice claramente. El cuello de botella de una cadena de eventos es la inferencia del modelo, que puede tardar “seconds to minutes” por evento, y no define control de flujo en el protocolo. Es decir, si tu evento salta demasiado, cada salto es una ejecución del agente que alguien paga.
Las palancas están en tu servidor:
- Filtra con los argumentos del
inputSchemaantes de entregar, como pide la guía. - Emite cuando cambia el estado que importa, no en cada movimiento. Es lo que ya hace nortia-almacen en el curso, que solo avisa cuando cambia el conjunto de alertas.
- El borrador muestra incluso, en uno de sus ejemplos, un argumento de ventana de deduplicación definido por el servidor.
En el lado de ChatGPT existe el batching de tareas, pero no lo controlas tú. Vigila también el bucle del que avisa OpenAI. Un agente que mueve stock entre almacenes al recibir una alerta genera movimientos que pueden disparar más alertas.
Quién decide qué hace el agente, y con qué permisos
En ChatGPT, según la guía, el usuario elige qué vigilar y qué debe hacer ChatGPT cuando llega una actualización, y se aplican los controles del espacio de trabajo. El borrador deja esa parte fuera del protocolo a propósito. Para él, cómo llega un evento al modelo “is an application-level concern”, y cómo exponer eventos a los frameworks de agentes es su primera pregunta abierta.
Lo que sí fija el borrador es el principio que deberías aplicar en tu servidor: “Event receipt does NOT constitute authorization to act”. La reacción del agente pasa por la autorización normal de MCP. Recibir la alerta no da permiso para crear un pedido de reposición. Ese permiso lo decide tu tool con el token del usuario, como siempre. El borrador pide además que los clientes permitan evaluar políticas entre la recepción y la acción.
Las fuentes no cuentan cómo trata ChatGPT, en una ejecución disparada por un evento, las tools con efecto que normalmente confirmaría el usuario. Mientras no lo sepas, te recomendamos diseñar como si nadie fuera a mirar:
- tools de lectura para el triaje
- tools de escritura idempotentes y con el alcance mínimo
- las destructivas, fuera de cualquier flujo disparado por eventos.
Trata también el payload como datos no fiables. La guía de OpenAI pide no meter instrucciones para el modelo dentro del evento, y el borrador cuenta la inyección por payload como la primera amenaza de su tabla. Si un evento despierta a un agente que delega en otros, entran en juego la identidad y la traza a través de varios saltos. Ese es el tema de «Sistemas multi-agente», también en el nivel Experto.
Hay un detalle de la implementación de ChatGPT con consecuencias: no soporta el aviso terminated del borrador. Cuando revoques el acceso de un usuario, la guía te pide dejar de entregar, pero ChatGPT no recibe un aviso explícito de que la suscripción ha terminado. Si el usuario confía en que “el agente vigila el almacén”, ese silencio se parece demasiado a “no ha pasado nada”.
Un borrador sin interoperabilidad probada
Volvamos a la opinión del post. Que los disparadores por evento cambian el diseño de un agente es razonable, y las fuentes lo respaldan: el borrador abre con la idea de que el agente reaccione “without the user being present”. Que se esté subestimando es más discutible. El problema no es el concepto, sino el estado en que está:
- Es un borrador experimental. No forma parte de la especificación 2026-07-28 y ninguna de nuestras fuentes le pone fecha para entrar en ella.
- Del lado del cliente, OpenAI documenta ChatGPT, que solo implementa el modo webhook. Su fecha de lanzamiento no está verificada. Vercel documenta en Connect un receptor experimental.
- Del lado del servidor, Cloudflare anunció el 13 de octubre de 2026 su evento de Workers Issues.
- Ninguna fuente que hemos leído muestra a dos de estas implementaciones funcionando juntas, y nosotros no lo hemos probado.
- Lo que implementes siguiendo la guía de OpenAI es, hoy, una integración con ChatGPT. Si el WG cambia el diseño, la implementación de ChatGPT y la futura extensión pueden no coincidir.
Nada de eso es razón para esperar sentado, pero sí para construirlo con cabeza:
- Tu fuente de eventos de dominio, con identificadores estables y marcas de tiempo, en tu código.
- El adaptador de webhooks de ChatGPT, en un módulo aislado detrás de un interruptor y con la versión de la guía y del borrador anotada.
- Las tools idempotentes y bien autorizadas, que te sirven con eventos o sin ellos.
Así, si mañana el borrador cambia, cambias un adaptador. Si quieres repasar por qué existe el borrador y qué sigue en el roadmap, lo tienes en el artículo sobre MCP Events y webhooks.
Aprende esto en el curso
- Avanzado 10 € Operaciones largas y tiempo real · Progreso, Tasks, suscripciones y cortes de stream
- Experto 25 € Seguridad avanzada · Modelo de amenazas de MCP y defensas en profundidad
- Experto 25 € Sistemas multi-agente · Agentes como servidores, subagentes y memoria compartida
- Básico Gratis Interacción con el usuario y extensiones · Elicitation en varias rondas, Tasks y el marco de extensiones
La lección 1 la puedes leer ya, sin registro. Las otras 13 del Básico son gratis con una cuenta.
Empieza gratis Ir a la lecciónFuentes
- OpenAI: MCP Events (guía para servidores que usa ChatGPT)
- MCP Events: design sketch (WG Triggers & Events)
- Nathan Baschez (@nbaschez) en X sobre MCP Events (2026-10-03)
- Standard Webhooks: especificación
- Especificación 2026-07-28: subscriptions
- Extensión Tasks: resumen oficial
- Cloudflare changelog: MCP Events en el Cloudflare API MCP server (2026-10-13)
- Cloudflare: Workers Issues
- Cloudflare: investigar una incidencia de Workers Issues
- Vercel Connect: MCP Events