Phase 13: Tools & Protocols

Extensão das tarefas do MCP: Trabalho duradouro sobre um núcleo sem nacionalidade

O MCP sem estado não significa que todas as operações devem ser concluídas em uma única solicitação. A extensão oficial Tasks dá ao trabalho de longa duração um manilho durável explícito. Um servidor pode devolver esse manilho a partir de tools/callQualquer instância pode responder .tasks/get, e a entrada do cliente chega através de tasks/updateSem reanimar sessões 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 aprendizagem

  • Distinguir o transporte de protocolo sem estado de aplicação do estado de tarefa de aplicação durável.
  • Negociar o io.modelcontextprotocol/tasksExtensão das capacidades por pedido e server/discover- Não .
  • Retorna um servidor-direcionado CreateTaskResultcomresultType: "task"Só depois de uma criação duradoura.
  • Pesquisa com tasks/get, cumprir tarefas de entrada com tasks/update, e solicitar a cancelamento da cooperação com tasks/cancel- Não .
  • Remova o mais velho .tasks/status- Não .tasks/result, e tasks/list- As suposições.
  • Subscreva-se às notificações opcionais de tarefas através de subscriptions/listenem um fluxo SSE de resposta POST.
  • Expirar a tarefa modelo, reiniciar a recuperação, deduplicar a chave de entrada e erros de execução corretamente.

Por que as tarefas são uma extensão

As tarefas apareceram pela primeira vez como uma característica central experimental em 2025-11-25.io.modelcontextprotocol/tasksExtensão para que clientes e servidores possam optar pelo ciclo de vida extra sem expandir o protocolo principal para todos.

A especificação da extensão continua a ser uma superfície de esboço, mesmo que seja a casa oficial atual para tarefas.

Usar uma tarefa quando a operação tiver uma ou mais das seguintes propriedades:

  • Pode sobreviver a um prazo normal de solicitação.
  • Uma fila de trabalhadores ou um sistema externo de emprego já possui execução.
  • O cliente precisa de se recuperar depois de a sua própria reinicialização.
  • A operação faz pausas para a entrada do usuário ou modelo durante a execução.
  • A cancelamento e a recuperação de resultados duradouros são requisitos do produto.

Não crie uma tarefa para uma pesquisa determinista barata. Um manto, persistência, votação, expiração e cancelamento são complexidades reais.

Núcleo de Estadão, Aplicação Estadual

MCP 2026-07-28 remove initialize- Não .notifications/initialized, sessões de protocolo, eMcp-Session-IdIsso não proíbe os produtos de estado.

Um id de tarefa é um estado de aplicação explícito:

  • O servidor insiste antes de devolvê-lo.
  • O cliente pode armazená-lo e fazer uma pesquisa depois de reiniciar.
  • A identificação pode ser encaminhada para qualquer réplica apoiada pela mesma loja durável.
  • A autorização é verificada em cada método de tarefa.
  • A expiração e a exclusão são definidas por campos de tarefas, não por um período de vida útil do transporte.

Isto é operacionalmente diferente do estado oculto ligado a uma conexão.

Mantém quatro vidas separadas:

StateLifetimeWhere it belongs
Protocol metadataOne requestparams._meta, validated again on every call
Transport workOne stdio request or HTTP responseIn-flight coordinator with a bounded deadline
MRTR continuationOne retry sequenceIntegrity-protected requestState, plus replay controls when needed
Durable taskAcross requests, replicas, restarts, and reconnectsShared application store keyed by an authorized taskId

Movendo um registro de tarefa para a memória de processo não torna o MCP com estado.tasks/getPerseverar antes de devolver o manto, então fazer com que cada método de tarefa resolva o mesmo registro compartilhado sob inquilinos e cheques de principal.

Negociação de Capacidade

O cliente anuncia apoio a cada pedido elegível:

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"
    }
  }
}

O servidor retorna exato supportedVersions, capacidades,ttlMs, e cacheScopedeserver/discoverA Comissão propõe que os Estados-Membros, em conformidade com o artigo 107.°, n.° 1, do Tratado, tenham a mesma extensão em termos de capacidades.tools/listEsse resultado retorna uma determinação .generate_reportDescrição, objeto válido inputSchema- Não .resultType: "complete", metadados de identidade do servidor e dicas de cache público.

Um método de tarefa de um cliente que não declarou os retornos de extensão -32021, Falta de capacidade de cliente exigida, com data.requiredCapabilitiesdefinido para {"extensions":{"io.modelcontextprotocol/tasks":{}}}Uma cadeia de protocolo não suportada retorna .-32022com exatosupportedE ...requesteddados; uma versão faltante ou não-string retornar -32602- Não .

Um envelope sem um JSON-RPC idO receptor pode processá-lo, mas não emite nenhum resultado ou erro JSON-RPC. Um adaptador HTTP Streamable retorna 202 AcceptedSem organismo para uma notificação aceita.

No momento, apenastools/callDesenha a sua abstração interna para que os tipos de solicitações futuros não necessitem de reescrever armazenamento.

Criação de tarefas dirigidas por servidores

A velha bandeira do cliente .params._meta.task.requiredO cliente declara o suporte de extensão, e o servidor decide se um determinadotools/calltorna-se uma tarefa.

Pedido:

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": {}
        }
      }
    }
  }
}

Resposta:

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
  }
}

O servidor não deve devolver esta manobra até que um tasks/getEm uma loja eventualmente consistente, espere a visibilidade de leitura antes de responder. caso contrário, um cliente pode receber um ID válido e imediatamente obter "não encontrado".

Uma resposta à tarefa não é solicitada no sentido de que o cliente não solicita o modo tarefa.

A forma da tarefa

Cada tarefa inclui:

  • taskId: identificador estável gerado pelo servidor;
  • status- Não .working- Não .input_required- Não .completed- Não .cancelled, ou failedO artigo 2.o
  • createdAtE ...lastUpdatedAt: marcas de tempo ISO 8601;
  • ttlMs: duração de expiração desde a criação, ou nullsem limite anunciado;
  • opcionalpollIntervalMs: cadência de votação sugerida mínima do servidor;
  • opcionalstatusMessage: contexto orientado para o utilizador ou para o modelo.

Os campos específicos de status só aparecem quando relevantes:

  • input_requiredincluiinputRequests- Não .
  • completedInclui as informações do pedido original result- Forma.
  • failedinclui um JSON-RPC errorObjeto.

O cliente deve honrar .pollIntervalMsUm servidor pode limitar as taxas de pesquisas mais agressivas e pode alterar o intervalo durante a vida útil da tarefa.

Pesquisa com tasks/get

O cliente pede uma instantânea atual:

httpPOST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/get
Mcp-Name: tsk_786512e29e0d
json{
  "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/getO resultado é sempre o mesmo.resultType: "complete"A tarefa aninhada ainda pode terstatus: "working"ou status: "input_required"- Não .

Esta distinção impede um bug comum do parser:

textresult.resultType = complete    means the tasks/get RPC finished
result.status = working        means the represented job is still running

Não há .tasks/resultQuando a tarefa estiver concluída, o próximo tasks/getA resposta inclui o original CallToolResultSubresult- Não .

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"
    }
  }
}

O exterior .resultTypeDiz o tasks/getO RPC completado.result.resultTypeO dispositivo original de chamada de ferramenta completado.CallToolResultDeveria também ter o seu próprio .io.modelcontextprotocol/serverInfoEsta lição inclui-o em vez de armazenar uma carga útil não tipográfica.

Não há .tasks/listOs servidores sem sessão não podem inferir com segurança quais tarefas pertencem a uma lista escaneada por conexão.

Introdução durante a execução de tarefas

A entrada de tarefa e o MRTR central parecem semelhantes, mas usam continuidades diferentes.

Entrada necessária antes da criação de tarefa

Núcleo de retornoresultType: "input_required"do original tools/callO cliente cumpre e retrata a chamada original, só cria a tarefa depois que as rodadas de MRTR sincronas terminem.

Entrada necessária após a criação da tarefa

Defina a tarefa para input_required- Não .tasks/getexpõe o excepcional inputRequests, e o cliente envia respostas através de tasks/updateO cliente não retoma o original .tools/call- Não .

Imagem:

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"]
        }
      }
    }
  }
}

Atualização:

httpPOST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/update
Mcp-Name: tsk_786512e29e0d
json{
  "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": {}
        }
      }
    }
  }
}

A resposta de sucesso é um reconhecimento vazio e mais resultType: "complete"A mudança de estado pode eventualmente ser consistente, então o cliente continua a votar ou a ouvir.

Cada um .inputRequestsA chave deve ser única para toda a vida da tarefa.tasks/getOs dados de um servidor podem ser utilizados para fazer uma análise de dados de um servidor.input_requiredAté que todas as chaves necessárias sejam respondidas.

Cancelamento é cooperativo

tasks/cancelO trabalho pode terminar primeiro, ignorar o cancelamento ou a transição mais tarde.

httpPOST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/cancel
Mcp-Name: tsk_786512e29e0d
json{
  "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 os três métodos de tarefa,Mcp-Nameespelhosparams.taskIdNão repete o nome do método JSON-RPC. code/main.pycentraliza esta regra em make_http_request- Não .

O trabalhador da lição honra a cancelamento imediatamente, fazendo chamadas repetidas idempotentes.

Não utilize notifications/cancelledEsta notificação pertence a um pedido de cancelamento, não a tarefas duradouras.

A distinção importa na fronteira de roteamento. A cancelamento de solicitações visa uma operação JSON-RPC em voo ou sua resposta HTTP escopoada por solicitação.tools/callJá voltou .resultType: "task"O pedido é completo e o encerramento do seu transporte não pode nomear ou interromper o trabalho duradouro. tasks/cancelO presente regulamento é um novo RPC autorizado.params.taskId, espelhos que identificamMcp-Name, resolve o backend de posse da tarefa, registra a intenção de cancelamento da cooperativa e retorna um reconhecimento sem alegar que o trabalhador parou.

Uma gateway deve, portanto, manter os coordenadores de solicitações e as rotas de tarefas em tabelas diferentes. A tabela de solicitações pode desaparecer quando a resposta termina. A rota de tarefas deve sobreviver até o estado terminal e a expiração da retenção. Lesson 29: MCP Reliability, Cancellation, and Flow ControlConstrui a corrida, o tempo de espera, a impotência, a pressão e a regra de retomada para ambos os caminhos.

Notificações opcionais

Uma cliente que quer actualizações push enviasubscriptions/listenPara Streamable HTTP, este é um POST cuja resposta é um fluxo de SSE com escala de solicitação. Não há fluxo de eventos GET independente e nenhuma sessão de protocolo para manter vivo.

O servidor reconhece as identidades aceitas com notifications/subscriptions/acknowledgede pode então enviar instantâneos completos através notifications/tasksO reconhecimento e todas as notificações de tarefasio.modelcontextprotocol/subscriptionIdem _meta, igual ao subscriptions/listenCada notificação de tarefa é equivalente atasks/getVoltaria nesse momento.

Os clientes ainda devem declarar a extensão das tarefas. Eles devem reconectar e retomar a partir de ids de tarefas duráveis em vez de depender da repetição de eventos ou Last-Event-ID- Não .

Semântica do fracasso

Use as duas camadas de erro corretamente.

Erro de protocolo

Parâmetros de método inválidos ou um id de tarefa desconhecido retornam um erro JSON-RPC, comumente -32602- Falta de declarações de apoio à extensão .-32021com o objeto de capacidade exigido.

Resultados da execução da tarefa

  • Um resultado normal de ferramenta com isError: true- Ainda é um .completedA função é executada porque a chamada de ferramenta produziu o resultado definido.
  • Um erro JSON-RPC durante a execução diferida faz a tarefa failede armazena esse erro JSON-RPC em error- Não .
  • Rejeição do utilizador pode produzir cancelled, um resultado de recusa concluído, ou outro resultado seguro específico do domínio.

Durabilidade, expiração e propriedade

Permaneça pelo menos o id da tarefa, o status, as timestamps, ttl, o intervalo de pesquisa, a propriedade original da operação, o resultado ou o erro, os pedidos de entrada pendentes e todas as chaves de entrada emitidas.

A chave de armazenamento deve incluir ou resolver um inquilino e principal autorizado.tasks/get- Não .tasks/update- Não .tasks/cancel, e assinatura.

ttlMsO cliente pode tratá-lo como um backstop quando uma tarefa deixou de produzir atualizações observáveis. Um servidor pode falhar e posteriormente excluir uma tarefa expirada. Não descreva como uma promessa de manter um resultado concluído por tantos milissegundos após a conclusão.

Usar atômicos escreve ou transações. A lição escreve um arquivo temporário e renomeia-o atomicamente. Um serviço multi-replica deve usar uma loja durável compartilhada e um contrato de locação de trabalhadores ou controle de simultaneidade equivalente.

Construí-lo

code/main.pyImplementa um serviço de tarefas deterministas:

  • server/discoverRetorno supportedVersions, dicas de cache e a extensão das tarefas.
  • tools/listRetorna um determinista, cacheable generate_reportDescrição com um esquema de entrada válido.
  • tools/callcria e persiste a tarefa antes de retornar resultType: "task"- Não .
  • Uma nova instância de serviço recarrega a mesma tarefa, demonstrando a recuperação de restart.
  • tasks/getRetorna instantâneos completos da tarefa.
  • O trabalhador muda deworking- Não .input_required- Não .
  • tasks/updateAceita uma resposta do formulário e retorna um reconhecimento completo vazio.
  • O trabalhador armazena um aninhadoCallToolResultcom o seu próprio .resultTypee identidade do servidor, e depois transições para completed- Não .
  • tasks/cancelé impotente nesta aplicação.
  • Configurações do construtor HTTP Mcp-Name- Não .params.taskIdParatasks/get- Não .tasks/update, e tasks/cancel- Não .
  • Auxiliares de notificação utilizam notifications/subscriptions/acknowledgedE ...notifications/tasks, ambos marcados com a identificação de solicitação de escuta.
  • As notificações sem ID não produzem resposta JSON-RPC.

O trabalhador avança explicitamente em vez de dormir em um fio de fundo, o que torna cada transição de estado determinista e mantém o exemplo de protocolo separado da mecânica de fila.

Usá-lo

A partir da raiz do repositório:

bashcd phases/13-tools-and-protocols/13-mcp-async-tasks/code
python3 main.py
python3 -m unittest discover tests -v

Seqüência 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=completed

Verifique também que .tasks/status- Não .tasks/result, e tasks/listmétodo de devolução não encontrado no serviço moderno.

Verifica isso .tools/listé determinista e cada método de tarefa HTTP atual reflete sua tarefa id através Mcp-Name- Não .

Envia-o

outputs/skill-task-store-designer.mdAgora produz um projeto consciente de extensão: negociação de capacidade, criação durável antes do retorno, métodos atuais, fluxo de atualização de entrada, propriedade, expiração, cancelamento, assinatura e migração dos métodos experimentais removidos.

Exercícios

  1. Adicione uma segunda chave de entrada pendente. Envie uma parcial tasks/updateE provar que a tarefa continua.input_requiredAté que as duas chaves sejam respondidas.
  2. Adicionar a propriedade do inquilino à loja e rejeitar um ID válido de tarefa apresentado pelo principal autenticado errado.
  3. Adicionar um contrato de locação de trabalhadores com expiração. Demonstrar que duas instâncias de serviço não podem realizar a mesma tarefa simultaneamente.
  4. Implementar um adaptador SSE de resposta POST para subscriptions/listenNão adicionar GET,Last-Event-ID, ou um cabeçalho de sessão.
  5. Adicionar limpeza de expiração. Diferenciar uma tarefa expirada de uma identificação de tarefa mal formada sem vazamento de existência entre inquilinos.

Termos-chave

TermMeaning in the current extension
Tasks extensionOptional io.modelcontextprotocol/tasks capability for durable async work
CreateTaskResultServer-directed resultType: "task" response to an eligible request
tasks/getPoll a full current task snapshot, including terminal result or pending input
tasks/updateSubmit responses to a task's outstanding inputRequests
tasks/cancelAcknowledge cooperative cancellation intent
input_requiredTask status indicating client input is outstanding
pollIntervalMsServer-suggested minimum delay before another poll
ttlMsExpiry duration measured from task creation
Durable-before-returnRule that the task id must resolve before its handle is sent
notifications/tasksOptional full task snapshot delivered on a subscribed SSE response

Compatibilidade do legado

A superfície experimental de 2025-11-25 utilizou o aumento de tarefas solicitado pelo cliente,tasks/status- Não .tasks/result, e opcionais tasks/listUm cliente atual usa a extensão, aceita manipulações direcionadas por servidor, pesquisas.tasks/get, fornece entrada com tasks/update, e lê o resultado final da tarefa instantânea.

Mais leitura

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.