MCP Tasks (2025): Durable Tool Calls, Polling, and Deferred Results
The November 2025 MCP spec introduces experimental Tasks, a durable execution layer that lets clients track long-running work, poll status, and retrieve results later. This deep dive explains the exact message flow and the best practices you need for production.
Why Tasks exist
MCP started as a clean abstraction for calling tools and reading resources, but real workloads do not always finish within a single request. Code migration, deep research, big data processing, and enterprise automation frequently take minutes or hours.
Tasks provide a standardized way to make an MCP request durable: instead of blocking until completion, the receiver returns a task handle that the requestor can poll. When the work is complete, the requestor retrieves the final result via a separate method.
The core idea
The message flow (with real JSON-RPC)
1) Create a task via `tools/call`
The requestor includes a `task` object in the request params. A `ttl` can be requested (milliseconds), but the receiver can override it.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "generate_report",
"arguments": {
"range": "last_90_days"
},
"task": {
"ttl": 600000
}
}
}The receiver returns a `CreateTaskResult` with the task state (including `pollInterval`).
2) Poll status via `tasks/get`
The requestor polls until the task reaches a terminal state (`completed`, `failed`, `cancelled`) or enters `input_required`.
{
"jsonrpc": "2.0",
"id": 2,
"method": "tasks/get",
"params": {
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
}
}3) Retrieve the final output via `tasks/result`
`tasks/result` blocks until the task reaches a terminal status, then returns the underlying request result.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tasks/result",
"params": {
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
}
}Automate Your Emails with AI
GetResponse combines email marketing and AI for ultra-high-performing campaigns. Perfect for MCP workflows.
Try GetResponse FreeStatus lifecycle, cancellation, and operational safety
The allowed status transitions
Tasks start in `working`. From there, they may move to `input_required` or a terminal state. Once terminal, they must not transition again.
`input_required` is a first-class concept
If the receiver needs more data to proceed, it should move the task into `input_required`. The protocol requires task-related messages to include related-task metadata in `_meta`.
Cancellation is explicit
Requestors can cancel with `tasks/cancel`. Receivers should attempt to stop execution and must transition the task to `cancelled`.
{
"jsonrpc": "2.0",
"id": 4,
"method": "tasks/cancel",
"params": {
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
}
}Production checklist (clients and servers)
Client-side checklist
- 1. Respect `pollInterval` and avoid aggressive polling.
- 2. Treat `taskId` as opaque and never derive meaning from it.
- 3. Handle `input_required` by preemptively calling `tasks/result` when appropriate.
- 4. Plan for expiry: results may disappear after TTL.
Server-side checklist
- 1. Return `CreateTaskResult` as soon as possible after accepting work.
- 2. Provide realistic `pollInterval` values.
- 3. Use strong isolation boundaries so tasks cannot leak across sessions.
- 4. Validate inputs and return tool execution errors to enable model self-correction.
If you are building automation workflows around MCP and you want a practical way to trigger campaigns and follow-ups after a task completes, consider connecting your pipeline to GetResponse for email marketing automation.
Conclusion
Tasks are the missing piece for real-world MCP adoption: long-running jobs become first-class, clients stay responsive, and results remain retrievable after completion.
Treat Tasks as an execution contract: define clear lifecycles, communicate progress, and harden boundaries. Do that, and you can safely move agentic systems from demos to production.