Why do MCP tasks live in the io.modelcontextprotocol/tasks extension rather than core?
answer
- core stays small on purpose
- not every server should pay for it
- opt-in happens once, not per tool
- the superseded design had a per-tool flag
- no opt-in means fall back or reject
basics
~20 sRevision 2026-07-28 kept the core protocol small and moved long-running work into the optional io.modelcontextprotocol/tasks extension. Both sides must opt in; the client does so once through its capabilities, and a server facing a client that did not must answer synchronously instead.
solid answer
~50 sMCP 2026-07-28 pushed several things out of the core protocol, and asynchronous tasks were one of them: they became the `io.modelcontextprotocol/tasks` extension rather than a feature every implementation must carry. The reasoning is that a task requires durable server-side state, a polling method, cancellation and a lifecycle — a large surface that a simple stdio server exposing three read-only tools should not have to implement to be spec-compliant. Because it is an extension it is opt-in on both sides, and crucially the client opts in **once**, by declaring the extension in its capabilities. There is no per-tool flag: the superseded 2025-11-25 design had a per-tool `execution.taskSupport` field, and that is gone. If the client has not declared the extension, the server must fall back to core behaviour — run the call synchronously — or reject it, never return a task handle the client has no methods to follow.
go deeper
Remember that tasks are optional in MCP — a server is fully compliant without them — and that a client must declare support before a server may use them.
Be able to state the opt-in shape precisely: one declaration of io.modelcontextprotocol/tasks in client capabilities, no per-tool flag, and a server that must fall back to synchronous behaviour without it.
Argue the design rationale — implementation cost on both sides and a fast-moving feature kept off the core surface — and describe the concrete fallback path a client needs when the server has no tasks support.
Own the portfolio view: since fleet support is uneven, decide whether your product depends on tasks at all, or whether you shape server operations so that no call needs to run long enough to require them.
## What changed in 2026-07-28 Revision 2026-07-28 is the largest reshaping MCP has had. Alongside removing the `initialize` handshake and protocol-level sessions, it drew a sharper line between the core protocol every implementation must support and optional, separately-specified extensions. Asynchronous tasks landed on the optional side, as `io.modelcontextprotocol/tasks`. That identifier follows the same prefixed naming as the reserved `_meta` keys: a domain-ish prefix owned by the specification, then the extension's name. Extensions are declared in an `extensions` map on `ClientCapabilities` and `ServerCapabilities`, mapping identifier to a settings object, where an empty object means "supported, no settings". ## Why tasks are not core Three arguments, and a good answer gives at least two: **1. Implementation cost.** Supporting tasks is not one method. It is durable state that outlives a request, a status machine, a polling endpoint, a mid-flight input path, cooperative cancellation, and a retention policy for finished work. An enormous share of real MCP servers are small stdio processes wrapping a local API where every call returns in milliseconds. Forcing all of them to carry that machinery to claim compliance would make the protocol expensive to adopt for no benefit. **2. Client cost.** The asymmetry cuts both ways. A client that supports tasks must be able to hold handles across reconnects, poll on a schedule, present partial progress in a UI, and decide when to give up on an abandoned task. That is real product work. A minimal client should be able to speak MCP correctly without it. **3. Evolution speed.** The task design has already been revised once. The 2025-11-25 shape — a blocking `tasks/result` method, a `tasks/list` method, and a per-tool `execution.taskSupport` field — was superseded by the current handle-and-poll design. Keeping a fast-moving feature outside the core surface lets it change without forcing a backwards-incompatible bump of the whole protocol, since MCP revision strings name the date of the last backwards-incompatible change. ## The single opt-in The most testable detail: the client opts into tasks **once**, at the extension level. There is no per-tool flag in the current design. This is exactly what changed from 2025-11-25, where a tool could advertise `execution.taskSupport` to say whether that particular tool could be run as a task. A candidate who describes a per-tool opt-in is quoting a superseded revision. One opt-in means the client is saying "I can handle a `CreateTaskResult` from any call you choose to run asynchronously." The decision about *which* calls become tasks then sits with the server, which knows which of its operations are slow. That is a sensible division: the client declares a capability, the server exercises judgment. ## Mutual opt-in and fallback, applied to tasks Extensions are always opt-in on both sides, and if one side does not support one, the other must either revert to core behaviour or reject the request. For tasks specifically that means: - Client supports tasks, server does not: nothing happens. Every call answers synchronously, and the client's task machinery simply never fires. A long call is then an ordinary long request, with all the fragility that implies. - Server supports tasks, client did not declare it: the server must **not** return `resultType: "task"`. It runs the work synchronously or refuses. Returning a handle to a client with no `tasks/get` implementation would leave the client holding a receipt it cannot redeem — a silent, confusing failure. The hardest engineering consequence is the first case. A server author cannot assume tasks are available and must have a synchronous path for the same operation, even if that path is only viable for smaller inputs. ## What this costs a deployment Because the fleet of MCP servers straddles revisions and extension support, a client that wants long-running work to be reliable cannot depend on tasks being there. Realistic clients degrade: use tasks where offered, fall back to a synchronous call with a generous timeout otherwise, and consider tightening the work itself — smaller units, a server-minted handle passed as an ordinary tool argument for resumption — so that no single call needs to run for twenty minutes in the first place. ## Interview framing Answer in three beats: tasks moved out of core in 2026-07-28 to keep core small and cheap to implement; the opt-in is mutual and, on the client side, a single extension declaration rather than the superseded per-tool `execution.taskSupport` flag; and a server without the client's opt-in must fall back to synchronous behaviour or reject, never hand out a handle the client cannot follow.
- If a client supports tasks but the server does not, what happens to a slow tool call?It is an ordinary synchronous request and stays that way — the client's task machinery never fires. That means all the usual fragility: a long-held response stream, no resumability if it breaks in MCP 2026-07-28, and a re-issue as a brand-new request with a new JSON-RPC id if it does. A realistic client keeps a synchronous fallback path with a generous timeout.
- Who decides which calls become tasks — the client or the server?The server. The client's single extension declaration says only that it can handle a task handle from any call. The server, which knows which of its operations are slow, decides per call whether to answer synchronously or return a CreateTaskResult. The current design deliberately has no per-tool advertisement; that was the superseded 2025-11-25 execution.taskSupport field.
- How is this different from the way tasks worked in the 2025-11-25 revision?That earlier design had a blocking tasks/result method the client called to wait for completion, a tasks/list method, and a per-tool execution.taskSupport flag. 2026-07-28 superseded all three: the extension is opted into once, work is followed by polling tasks/get, mid-flight input goes through tasks/update, and cancellation is tasks/cancel.
saying these in an interview costs you the question
- Says every compliant MCP server must support tasks
- Describes a per-tool execution.taskSupport opt-in as current
- Claims the client picks which calls run as tasks
- Thinks a server may return a task handle without the client's opt-in
- Says extensions are negotiated in a handshake at connect time