Extensión de las tareas del MCP: trabajo duradero en un núcleo sin estatus
tools/call, cualquier instancia puede responder .tasks/get, y la entrada del cliente llega a través de tasks/updateSin reanudar las sesiones de protocolo.Type: Build
Languages: Python
Prerequisites: Phase 13 · 09 (transports), Phase 13 · 11 (stateless MRTR), Phase 13 · 12 (elicitation)
Time: ~90 minutes
Objetivos de aprendizaje
- Distinguir el transporte de protocolos sin estado de origen del estado de tareas de aplicación duraderas.
- Negociar el acuerdo
io.modelcontextprotocol/tasksextensión de las capacidades por solicitud yserver/discover¿ Qué ? - Regresa un mensaje dirigido al servidor
CreateTaskResultconresultType: "task"sólo después de una creación duradera. - Encuesta con
tasks/get, cumple las tareas introducidas contasks/update, y solicitar la cancelación de la cooperación contasks/cancel¿ Qué ? - Retira el viejo .
tasks/status¿ Qué ?tasks/result, ytasks/listlas suposiciones. - Suscribirse a las notificaciones opcionales de tareas a través de
subscriptions/listenen una corriente de respuesta POST SSE. - Expirado de tarea modelo, reinicio de recuperación, deduplicación de la clave de entrada y errores de ejecución correctamente.
Por qué las tareas son una extensión
Las tareas aparecieron por primera vez como una característica central experimental en 2025-11-25.io.modelcontextprotocol/tasksextensión para que los clientes y servidores puedan optar por el ciclo de vida extra sin expandir el protocolo principal para todos.
La especificación de extensión sigue siendo un borrador de superficie a pesar de que es el hogar oficial actual de tareas. Enlazar la versión de extensión compatible con su SDK, ejecutar escenarios de conformidad y aislar los adaptadores de cable de su dominio de trabajo y almacenamiento.
Utilice una tarea cuando la operación tenga una o más de estas propiedades:
- Puede sobrevivir a un tiempo de espera normal.
- Una cola de trabajadores o un sistema de trabajo externo ya posee la ejecución.
- El cliente necesita recuperarse después de su propio reinicio.
- La operación se detiene para la entrada del usuario o modelo durante la ejecución.
- La cancelación y la recuperación de resultados duraderos son requisitos del producto.
No crea una tarea para una búsqueda determinista barata. Un manejo, persistencia, votación, vencimiento y cancelación son una complejidad real.
El núcleo de los apátridas, la aplicación estatal
MCP 2026-07-28 se elimina initialize¿ Qué ?notifications/initialized, sesiones de protocolo, y Mcp-Session-IdEso no prohíbe los productos de estado.
Una identificación de tarea es el estado de aplicación explícito:
- El servidor lo insiste antes de devolverlo.
- El cliente puede almacenarlo y volver a hacer una encuesta después de reiniciar.
- La identificación puede ser enviada a cualquier réplica respaldada por la misma tienda duradera.
- La autorización se verifica en cada método de tarea.
- La expiración y eliminación se definen por campos de tareas, no por un período de vida del transporte.
Esto es operativamente diferente del estado oculto conectado a una conexión.
Mantenga cuatro vidas separadas:
| State | Lifetime | Where it belongs |
|---|---|---|
| Protocol metadata | One request | params._meta, validated again on every call |
| Transport work | One stdio request or HTTP response | In-flight coordinator with a bounded deadline |
| MRTR continuation | One retry sequence | Integrity-protected requestState, plus replay controls when needed |
| Durable task | Across requests, replicas, restarts, and reconnects | Shared application store keyed by an authorized taskId |
El traslado de un registro de tareas a la memoria de proceso no hace que MCP sea estado.tasks/getPersiste antes de devolver el mango, luego haga que cada método de tarea resuelva el mismo registro compartido bajo los controles del inquilino y del principal.
Negociación de la capacidad
El cliente anuncia apoyo en cada solicitud elegible:
json{
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
},
"io.modelcontextprotocol/clientInfo": {
"name": "lesson-client",
"version": "1.0.0"
}
}
}El servidor devuelve exactamente supportedVersions, capacidades,ttlMs, y cacheScopede laserver/discoverLa aplicación de las tecnologías de la información y de la información es una de las principales modalidades de aplicación de las tecnologías de la información y de la información.tools/listEse resultado devuelve un deterministagenerate_reportdescriptor, objeto válido inputSchema¿ Qué ?resultType: "complete", metadatos de identidad del servidor, y pistas de caché público.
Un método de tarea de un cliente que no declaró las devoluciones de extensión -32021, Falta de capacidad requerida para el cliente, con data.requiredCapabilitiesse fija en {"extensions":{"io.modelcontextprotocol/tasks":{}}}Una cadena de protocolo no soportada devuelve .-32022con exactitudsupportedy requesteddatos; una versión que no está disponible o no está en cadena se devuelve -32602¿ Qué ?
Un sobre sin un JSON-RPC idEl receptor puede procesarlo, pero no emite ningún resultado o error JSON-RPC.202 Acceptedsin organismo para una notificación aceptada.
En la actualidad, sólotools/callsoporta la ejecución de tareas aumentadas. Diseñe su abstracción interna para que los tipos de solicitudes futuros no requieran reescribir almacenamiento.
Creación de tareas dirigidas por servidores
La vieja bandera del cliente .params._meta.task.requiredEl cliente declara soporte de extensión, luego el servidor decide si un determinado tools/callse convierte en una tarea.
Solicitud:
json{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "generate_report",
"arguments": {"size": "large"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}Respuesta:
json{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "task",
"taskId": "tsk_786512e29e0d",
"status": "working",
"statusMessage": "Preparing report outline.",
"createdAt": "2026-08-21T10:30:00Z",
"lastUpdatedAt": "2026-08-21T10:30:00Z",
"ttlMs": 900000,
"pollIntervalMs": 1000
}
}El servidor no debe devolver este mango hasta que un tasks/getEn una tienda de almacenamiento consistente, espere a la visibilidad de lectura antes de responder. de lo contrario un cliente puede recibir un ID de aspecto válido y recibir inmediatamente "no se encuentra".
Una respuesta a la tarea no se solicita en el sentido de que el cliente no solicita el modo de tarea.
La forma de la tarea
Cada tarea lleva consigo:
taskId: identificador estable generado por el servidor;status¿ Qué es esto ?working¿ Qué ?input_required¿ Qué ?completed¿ Qué ?cancelled, ofailedEl artículo 1createdAtylastUpdatedAt: sello de tiempo ISO 8601;ttlMs: duración de caducidad desde la creación, onullsin límite anunciado;- opcional
pollIntervalMs: la cadencia mínima de encuestas sugerida del servidor actual; - opcional
statusMessage: contexto orientado al usuario o al modelo.
Los campos específicos de estado aparecen sólo cuando sean relevantes:
input_requiredincluyeinputRequests¿ Qué ?completedincluye la solicitud originalresult¿Qué forma tiene?failedincluye un JSON-RPCerrorObjeto.
El cliente debe honrar .pollIntervalMsUn servidor puede limitar las tasas de encuestas más agresivas y puede cambiar el intervalo durante la vida de la tarea.
Encuesta con tasks/get
El cliente pide una instantánea actual:
httpPOST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/get
Mcp-Name: tsk_786512e29e0djson{
"jsonrpc": "2.0",
"id": 2,
"method": "tasks/get",
"params": {
"taskId": "tsk_786512e29e0d",
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}tasks/getEl resultado siempre ha sido el mismo.resultType: "complete"La tarea anida todavía puede tenerstatus: "working"o status: "input_required"¿ Qué ?
Esta distinción evita un error común del parser:
textresult.resultType = complete means the tasks/get RPC finished
result.status = working means the represented job is still runningNo hay ninguna .tasks/resultCuando la tarea se complete, el siguiente tasks/getLa respuesta se enmarca en el original CallToolResulten elresult¿Qué es esto ?
json{
"resultType": "complete",
"taskId": "tsk_786512e29e0d",
"status": "completed",
"createdAt": "2026-08-21T10:30:00Z",
"lastUpdatedAt": "2026-08-21T10:34:12Z",
"ttlMs": 900000,
"result": {
"resultType": "complete",
"content": [
{"type": "text", "text": "Generated large report with approved outline."}
],
"structuredContent": {"size": "large", "approved": true},
"isError": false,
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "tasks-demo",
"version": "1.0.0"
}
}
},
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "tasks-demo",
"version": "1.0.0"
}
}
}El exterior .resultTypedice el tasks/getEl RPC completado.result.resultTypedice que la llamada de herramienta original se completó. ese discriminador anidado es necesario.CallToolResultTambién debe llevar su propio io.modelcontextprotocol/serverInfoEsta lección incluye en lugar de almacenar una carga útil no tipificada.
No hay ninguna .tasks/listLos servidores sin sesión no pueden inferir con seguridad qué tareas pertenecen a una lista escaneada por conexión. Las aplicaciones que necesitan historial deben exponer una herramienta de dominio autorizado con filtros explícitos y reglas de propiedad.
Ingreso durante la ejecución de tareas
La entrada de tarea y el MRTR central se parecen, pero utilizan continuidades diferentes.
Entrada necesaria antes de crear tareas
El núcleo de retorno resultType: "input_required"del original tools/callEl cliente lo cumple y vuelve a intentar la llamada original.
Entrada necesaria después de la creación de tareas
Establezca la tarea para input_required- ¿ Qué ?tasks/getexpone lo sobresaliente inputRequests, y el cliente envía respuestas a través de tasks/updateEl cliente no vuelve a intentar el original .tools/call¿ Qué ?
Instantánea:
json{
"resultType": "complete",
"taskId": "tsk_786512e29e0d",
"status": "input_required",
"createdAt": "2026-08-21T10:30:00Z",
"lastUpdatedAt": "2026-08-21T10:31:00Z",
"ttlMs": 900000,
"inputRequests": {
"approve_outline": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Approve the generated report outline?",
"requestedSchema": {
"type": "object",
"properties": {"approved": {"type": "boolean"}},
"required": ["approved"]
}
}
}
}
}Actualización:
httpPOST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/update
Mcp-Name: tsk_786512e29e0djson{
"jsonrpc": "2.0",
"id": 4,
"method": "tasks/update",
"params": {
"taskId": "tsk_786512e29e0d",
"inputResponses": {
"approve_outline": {
"action": "accept",
"content": {"approved": true}
}
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}La respuesta de éxito es un reconocimiento vacío más resultType: "complete"El cambio de estado puede ser consistente, así que el cliente continúa haciendo encuestas o escuchando.
Cada uno .inputRequestsLa clave debe ser única durante toda la vida de la tarea.tasks/getLas imágenes instantáneas pueden mostrar la misma clave pendiente; los clientes deduplican la interfaz de usuario y los servidores ignoran las respuestas de las claves desconocidas, reemplazadas o ya cumplidas.input_requiredhasta que se contesten todas las llaves requeridas.
La cancelación es cooperativa
tasks/cancelEl trabajo puede terminar primero, ignorar la cancelación o la transición más tarde.
httpPOST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/cancel
Mcp-Name: tsk_786512e29e0djson{
"jsonrpc": "2.0",
"id": 5,
"method": "tasks/cancel",
"params": {
"taskId": "tsk_786512e29e0d",
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}Para los tres métodos de tarea,Mcp-Nameespejosparams.taskId. No repite el nombre del método JSON-RPC. code/main.pycentraliza esta regla en make_http_request¿ Qué ?
El trabajador de la lección honra la cancelación inmediatamente, haciendo llamadas repetidas idempotentes.
No se utilice notifications/cancelledLa notificación pertenece a la solicitud de cancelación, no a tareas duraderas.
La distinción es importante en el límite de enrutamiento. La cancelación de solicitudes se dirige a una operación JSON-RPC en vuelo o a su respuesta HTTP escaneada por la solicitud.tools/callYa ha regresado .resultType: "task", que la solicitud está completa y que el cierre de su transporte no puede nombrar o detener el trabajo duradero. tasks/cancelEl nuevo RPC está autorizado.params.taskId, refleja esa identidad enMcp-Name, resuelve el backend de la tarea, registra la intención de cancelación de la cooperativa y devuelve un reconocimiento sin afirmar que el trabajador ha parado.
Por lo tanto, una puerta de entrada debe mantener los coordinadores de las solicitudes y las rutas de tareas en diferentes tablas. La tabla de las solicitudes puede desaparecer cuando finalice la respuesta. La ruta de tareas debe sobrevivir hasta que expire el estado terminal y la retención. Lesson 29: MCP Reliability, Cancellation, and Flow Controlconstruye la carrera, el tiempo de espera, la impotencia, la presión y retomar las reglas para ambos caminos.
Notificaciones opcionales
Las encuestas son la línea de base. Un cliente que quiere actualizaciones push envíasubscriptions/listenCon ID de tarea. Para Streamable HTTP, este es un POST cuya respuesta es un flujo de SSE escaneado por solicitud. No hay un flujo de eventos GET independiente y ninguna sesión de protocolo para mantenerlo vivo.
El servidor reconoce las identidades aceptadas con notifications/subscriptions/acknowledgedy luego puede enviar instantáneas completas a través de notifications/tasks. El reconocimiento y cada notificación de tareaio.modelcontextprotocol/subscriptionIdEn el_meta, igual al subscriptions/listenCada notificación de tarea es equivalente a lo que tasks/getregresaría en ese momento.
Los clientes deben declarar la extensión de tareas. Deben volver a conectarse y reanudar desde ID de tareas duraderas en lugar de depender de la repetición de eventos o Last-Event-ID¿ Qué ?
Semántica del fracaso
Utilice las dos capas de error correctamente.
Error de protocolo
Parámetros de método inválidos o un id de tarea desconocido devuelven un error JSON-RPC, comúnmente -32602. Falta de declaraciones de apoyo a la extensión -32021con el objeto de capacidad requerido.
Resultados de la ejecución de tareas
- Un resultado normal de la herramienta con
isError: truees todavía uncompletedla tarea porque la llamada de herramienta produjo su resultado definido. - Un error JSON-RPC durante la ejecución diferida hace la tarea
failedy almacena ese error JSON-RPC enerror¿ Qué ? - El rechazo del usuario puede producir
cancelled, un resultado de rechazo completado, u otro resultado seguro específico del dominio.
Durabilidad, vencimiento y propiedad
Persiste al menos el ID de tarea, estado, timestamps, ttl, intervalo de encuestas, propiedad original de la operación, resultado o error, solicitudes de entrada pendientes y todas las entradas emitidas.
La clave de almacenamiento debe incluir o resolver un inquilino y un principal autorizado.tasks/get¿ Qué ?tasks/update¿ Qué ?tasks/cancel, y suscripción.
ttlMsEl cliente puede tratarlo como un respaldo cuando una tarea ha dejado de producir actualizaciones observables. Un servidor puede fallar y luego eliminar una tarea expirada. No lo describa como una promesa de conservar un resultado completado durante tantos milisegundos después de la finalización.
El curso escribe un archivo temporal y renombre automáticamente. Un servicio de réplica múltiple debe utilizar una tienda duradera compartida y un contrato de arrendamiento de trabajadores o control de concurrencia equivalente.
Construye el mismo
code/main.pyImplementa un servicio de tareas deterministas:
server/discoverretornosupportedVersions, las pistas de caché, y la extensión de tareas.tools/listdevuelve un determinista, cachéablegenerate_reportDescriptor con un esquema de entrada válido.tools/callcrea y persiste la tarea antes de regresarresultType: "task"¿ Qué ?- Una nueva instancia de servicio recarga la misma tarea, demostrando la recuperación de reinicio.
tasks/getdevuelve instantáneas completas de tareas.- El trabajador se mueve de
working¿ Qué ?input_required¿ Qué ? tasks/updateacepta una respuesta en el formulario y devuelve un reconocimiento completo vacío.- El trabajador almacena un nido
CallToolResultcon su propioresultTypey la identidad del servidor, luego las transiciones acompleted¿ Qué ? tasks/cancelEl Consejo de Ministros de la Unión Europea ha adoptado una decisión en el marco de la cual se ha adoptado una decisión.- Los conjuntos de constructor HTTP
Mcp-Name¿ Qué ?params.taskIdportasks/get¿ Qué ?tasks/update, ytasks/cancel¿ Qué ? - Los asistentes de notificación utilizan
notifications/subscriptions/acknowledgedynotifications/tasks, ambos etiquetados con la solicitud de escucha ID. - Las notificaciones sin ID no producen respuesta JSON-RPC.
El trabajador avanza explícitamente en lugar de dormir en un hilo de fondo. Eso hace que cada transición de estado sea determinista y mantiene el ejemplo de protocolo separado de la mecánica de cola.
Usalo
Desde la raíz del repositorio:
bashcd phases/13-tools-and-protocols/13-mcp-async-tasks/code
python3 main.py
python3 -m unittest discover tests -vSecuencia de resultados esperada:
textid=0 resultType=complete status=ack
id=1 resultType=task status=working
id=2 resultType=complete status=working
id=3 resultType=complete status=input_required
id=4 resultType=complete status=ack
id=5 resultType=complete status=completedTambién verifique quetasks/status¿ Qué ?tasks/result, y tasks/listmétodo de devolución no encontrado en el servicio moderno.
Verifique eso .tools/listes determinista y cada método de tarea HTTP actual refleja su ID de tarea a través de Mcp-Name¿ Qué ?
Envío
outputs/skill-task-store-designer.mdAhora produce un diseño consciente de la extensión: negociación de capacidad, creación duradera antes de regreso, métodos actuales, flujo de actualización de entrada, propiedad, vencimiento, cancelación, suscripción y migración de los métodos experimentales eliminados.
Los ejercicios
- Añadir una segunda clave de entrada pendiente. Envía una parcial
tasks/updatey demostrar que la tarea sigue .input_requiredHasta que se respondan las dos llaves. - Añadir la propiedad del inquilino a la tienda y rechazar un ID de tarea válido presentado por el principal autenticado incorrecto.
- Añadir un contrato de arrendamiento de trabajadores con vencimiento. Demostrar que dos instancias de servicio no pueden completar la misma tarea simultáneamente.
- Implementar un adaptador SSE de respuesta POST para
subscriptions/listenNo agregue GET,Last-Event-ID, o un encabezado de sesión. - Añadir limpieza de vencimiento. Distinguir una tarea vencida de una identificación de tarea malformada sin filtración de existencia entre los inquilinos.
Términos clave
| Term | Meaning in the current extension |
|---|---|
| Tasks extension | Optional io.modelcontextprotocol/tasks capability for durable async work |
CreateTaskResult | Server-directed resultType: "task" response to an eligible request |
tasks/get | Poll a full current task snapshot, including terminal result or pending input |
tasks/update | Submit responses to a task's outstanding inputRequests |
tasks/cancel | Acknowledge cooperative cancellation intent |
input_required | Task status indicating client input is outstanding |
pollIntervalMs | Server-suggested minimum delay before another poll |
ttlMs | Expiry duration measured from task creation |
| Durable-before-return | Rule that the task id must resolve before its handle is sent |
notifications/tasks | Optional full task snapshot delivered on a subscribed SSE response |
Compatibilidad con el legado
La superficie experimental 2025-11-25 utilizó el aumento de tareas solicitadas por el cliente, tasks/status¿ Qué ?tasks/result, y opcionales tasks/listUn cliente actual utiliza la capacidad de extensión, acepta manipulaciones dirigidas por servidores, encuestas tasks/get, suministra entrada con tasks/update, y lee el resultado final de la foto de tarea.
Leer más
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.