MCP Tasks — running batch jobs, CI pipelines and human approvals without blocking the connection
A tool call that takes minutes or hours (a CI build, a batch import, a step waiting on human approval) cannot sit on an open connection — most transports time out. The Tasks extension (io.modelcontextprotocol/tasks) turns any request into a "call-now, fetch-later" job: the server hands back a durable task ID immediately, the client polls for status, and retrieves the result once the job is done.
The problem it solves
- Holding a connection open for a long operation ties up resources, and many clients and transport intermediaries impose timeouts that make long-lived connections impractical beyond a few seconds — https://modelcontextprotocol.io/extensions/tasks/overview - Production workloads that legitimately take hours — molecular analysis, enterprise batch processing, code migration — used to block indefinitely under the original synchronous protocol, causing timeouts — https://agnost.ai/blog/long-running-tasks-mcp/ - Human-in-the-loop workflows (approval gates, review steps) need to pause and wait for a person without freezing the whole connection — https://modelcontextprotocol.io/extensions/tasks/overview - Mobile clients and unreliable networks lose progress on long operations if there is no durable handle to resume from after a disconnect — https://modelcontextprotocol.io/extensions/tasks/overview
How to submit and track a task
1. Negotiate the capability. Both client and server must declare support for capabilities.tasks during the initialization handshake (see MCP Session Lifecycle: the Handshake a Client Must Get Right — and the One Being Removed in 2026-07-28) before task-augmented requests are valid — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
2. Submit the job. Add a task field (with an optional TTL) to the params of any supported request, e.g. tools/call. The server responds with a CreateTaskResult containing a taskId, the initial status, the TTL, and a suggested pollInterval — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
3. Poll for status. Call tasks/get repeatedly; the server MAY include a pollInterval (milliseconds) in the response, and requestors SHOULD respect it — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
4. Fetch the result. Call tasks/result — this blocks until the task reaches a terminal status and then returns the result (or a JSON-RPC error) exactly as if the original request had completed synchronously. It is a distinct operation from the initial CreateTaskResult, which carries only task metadata, not the result — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
Lifecycle states
a task moves from creation into working, optionally into input_required, and finally into one of the terminal states completed, failed, or cancelled. Terminal states are irreversible — once reached, the task's state does not change — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
Mid-flight input
when a task needs input it moves to input_required and the tasks/get response includes an inputRequests map; the client fulfills these via tasks/update, and the task transitions back to working — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
Crash resilience
the task ID is a durable handle — if the client disconnects or restarts, it can resume polling with the same ID — https://modelcontextprotocol.io/extensions/tasks/overview
Cleanup
the server assigns the TTL for task retention; once it expires, the server MAY delete the task and its results regardless of the task's status — plan retrieval accordingly — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
Progress, notifications, and cancellation
- Progress: the progressToken supplied in the initial request stays valid for the whole task lifetime, exactly as in non-task requests — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
- Status push (optional, don't rely on it): servers MAY send notifications/tasks/status when status changes, each carrying the full task state identical to a tasks/get response — but requestors MUST NOT rely on receiving them; they are opt-in and receivers may not send them, so polling via tasks/get remains the default, required mechanism — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
- Cancelling: send tasks/cancel at any time. Cancellation is cooperative — the server acknowledges the intent but is not obligated to stop the work immediately — https://modelcontextprotocol.io/extensions/tasks/overview
- Cancel constraints: a task already in a terminal status (completed, failed, cancelled) cannot be cancelled; receivers MUST reject the request with JSON-RPC error code -32602 (Invalid params) — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
- State stickiness: once a task is cancelled, it MUST stay cancelled even if the underlying execution goes on to complete or fail anyway — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
Design choice: polling over callbacks
MCP Tasks use polling (tasks/get, plus optional status notifications) instead of server-initiated callbacks. That is a deliberate trade-off: it lets tasks work across firewall-restricted networks with no unsolicited server-to-client messages required — https://agnost.ai/blog/long-running-tasks-mcp/. One more property follows from the design: it is backward compatible — a server that ignores the task field in a request just executes synchronously, so clients can opt in once via the capability and handle whichever result shape comes back — https://agnost.ai/blog/long-running-tasks-mcp/. Note that task IDs are receiver-generated, not client-generated: the server creates the taskId and returns it in CreateTaskResult, and the client's role is to hold onto that durable, server-issued ID and resume polling with it after a disconnect (see "Crash resilience" above) — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks , https://workos.com/blog/mcp-async-tasks-ai-agent-workflows
Task ID security
Task IDs are capability handles, not opaque log identifiers — treat them as sensitive:
- The receiving system MUST bind each task to its creator's authorization context and validate that binding on every follow-up call (tasks/get, tasks/result, tasks/cancel); requests for a task that doesn't belong to the requestor's authorization context MUST be rejected — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
- If context-binding isn't available, the server MUST generate cryptographically secure task IDs with enough entropy to prevent guessing, and should favor a shorter TTL to shrink the exposure window — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
- Servers SHOULD rate-limit task operations to block denial-of-service and task-ID enumeration attacks — https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
- Tasks are session-scoped: bound to the originating session/authentication context — https://agnost.ai/blog/long-running-tasks-mcp/
This is where Tasks connects directly to this wiki's goal of building a *secure* remote server on the first attempt: a server that implements tasks/get/tasks/result/tasks/cancel without enforcing authorization-context binding on the task ID hands out a capability token to anyone who can guess or intercept it — the same class of problem covered in MCP Limits: Confused Deputy and Overbroad Permission Scopes — why a server you built safely can still act with someone else's authority.
When you need Tasks
Good fits, per the spec and field reports: CI/CD pipelines reporting build/test progress; batch processing over many items where partial progress is meaningful; human-in-the-loop approval/review gates that pause at input_required; wrapping external job systems (cloud deployments, containers) that already hand out async job IDs — return a task when you create the job and resolve it when the job completes; and multi-agent orchestration, where agents parallelize work instead of blocking sequentially on each other — https://modelcontextprotocol.io/extensions/tasks/overview , https://workos.com/blog/mcp-async-tasks-ai-agent-workflows . Reported production examples include healthcare analytics and molecular analysis jobs running for hours — https://agnost.ai/blog/long-running-tasks-mcp/
Related
- MCP Session Lifecycle: the Handshake a Client Must Get Right — and the One Being Removed in 2026-07-28 — Tasks only activates after both sides declare capabilities.tasks at the same handshake that negotiates every other capability; get that step wrong and task-augmented requests are silently invalid.
- Building an MCP Server: SDKs, Tool Schemas, Testing, and Deployment — the practical path from a 15-minute local prototype to a hosted remote server — a server author deciding between stdio and a hosted remote deployment needs Tasks specifically for the remote, long-running case; this page supplies the exact request/response shapes to implement.
- MCP Limits: Confused Deputy and Overbroad Permission Scopes — why a server you built safely can still act with someone else's authority — task IDs are capability handles by spec; unbound or low-entropy task IDs are a concrete instance of the overbroad-permission failure mode that page covers in general.
- MCP Transports: stdio for Local Servers, Streamable HTTP for Remote Ones — Tasks exists because Streamable HTTP connections can't stay open indefinitely; understanding the transport's timeout behavior explains why the extension is shaped as polling rather than a single long call.
Verified against
What links here
Source: Sinapsi — verified compositional memory, queryable by LLMs. Query this wiki live from your assistant over MCP, or build your own verified wiki (public, or private for your team). CC BY 4.0 — reuse with attribution to Sinapsi.