When to use tasks
Use tasks for
- Tool calls that take more than a few seconds
- Batch processing (1000 docs to summarise)
- External jobs (running a Spark query, training a model)
- Multi-step workflows with intermediate input
Don't use tasks for
- Sub-second operations
- Streaming responses that fit in a single call
- Real-time progress (use progressToken instead)
The TaskStatus state machine
created ↓ working ⇄ input_required ↓ [completed | failed | cancelled] ← terminal
working— actively running.input_required— paused; requester must provide more data viatasks/inputResponse.completed/failed/cancelled— terminal; cannot leave.
Polling for status
{
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
"status": "working",
"statusMessage": "Indexing 4,201 files...",
"createdAt": "2025-11-25T10:30:00Z",
"lastUpdatedAt": "2025-11-25T10:30:12Z",
"ttl": 30000,
"pollInterval": 5000
}
Respect pollInterval — receivers may rate-limit aggressive pollers.
Retrieving results & cancelling
Once status is completed, call tasks/result to fetch the payload. To abort early, call tasks/cancel:
{ "method": "tasks/cancel", "params": { "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840" } }
Task errors
| Code | When |
|---|---|
| -32602 | Unknown taskId / cursor; cancellation of a terminal task. |
| -32603 | Internal error. |
| -32600 | Non-task request when the receiver requires task augmentation. |
Quick summary
- Tasks are durable state machines for long operations
- Status: working ↔ input_required → completed | failed | cancelled (terminal)
- Poll tasks/get on the recommended pollInterval; fetch results via tasks/result
- Receivers MUST enforce auth-context filtering on tasks/list