# MCP's Tasks Extension Lets a Tool Call Run in the Background — If You Get the Object Shape Right

> Source: <https://dev.to/ethan_5b3022150e2c07a4030/mcps-tasks-extension-lets-a-tool-call-run-in-the-background-if-you-get-the-object-shape-right-jj0>
> Published: 2026-08-22 07:24:57+00:00

Model Context Protocol has quietly grown an answer to a problem every MCP server hits eventually: what do you do when a tool call takes thirty seconds, or thirty minutes? A CI pipeline, a batch export, a step that's waiting on a human to click approve — none of that fits the request-response model MCP started with, where the client just sits on the connection until a result comes back.

The Tasks extension is the fix. Instead of blocking, a server can hand back a task handle immediately: a `taskId`

, a `status`

, and enough metadata for the client to poll intelligently. The client checks in with `tasks/get`

(or gets pushed a `notifications/tasks/status`

update), waits for the status to land on `completed`

, `failed`

, or `cancelled`

, and then — this is the part that trips people up — fetches the actual output with a *separate* `tasks/result`

request. The task object itself never carries the result. It's pure status metadata: `taskId`

, `status`

, a required-but-nullable `ttl`

, timestamps, and a couple of optional fields. If your mental model says "the completed task has the result attached," you'll write code that works in your own test and breaks against a spec-following client.

I went looking for how solid this corner of MCP actually is before building anything, and the honest answer is: still moving. I pulled two versions of the official TypeScript SDK — one from March, one from July — and diffed the actual type definitions. The request-side `ttl`

field (the one you send when *asking* for a task to be created) changed what values it accepts between those releases. A new `extensions`

field showed up elsewhere in the same schema family. The SDK's own source comments say plainly that these APIs are experimental and may change without notice. That's not a knock on the feature — it's useful and shipping in real servers — but it means a validator has to be built against what's actually in the SDK right now, not against a spec page that might be stating something aspirational.

So that's what I built: paste a Task/status object, a `tasks/get`

/`tasks/cancel`

/`tasks/list`

request or response, a `notifications/tasks/status`

push, a request that's asking for task-backed execution, or a capabilities negotiation object, and it checks the shape against the current SDK — including the required-but-nullable `ttl`

gotcha, the "no result field here" mistake, and the common slip of passing `true`

instead of an empty object `{}`

for a capability flag (MCP's own convention for "yes, I support this").

Free, runs entirely in your browser, nothing you paste leaves your machine: [bracketly.pages.dev/tools/mcp-tasks-validator](https://bracketly.pages.dev/tools/mcp-tasks-validator/)
