Skip to main content

MCP's Tasks Extension Is Built for Work That Outlives a Single HTTP Request — Exactly the Shape of a Deploy

8 min readDora NodaDora Noda
Share
On this page

Tell an AI agent to deploy your app over chat, and the moment it calls the deploy tool, the clock is already working against it. A real deploy — clone, build, push an image, roll out, wait for health checks — takes anywhere from thirty seconds to several minutes. Most MCP clients time out a synchronous tool call well before that. Until this week, there was no protocol-native way to say "this is going to take a while, come back later" — every deploy-from-chat integration had to invent its own workaround.

On July 28, 2026, the Model Context Protocol project shipped its 2026-07-28 specification as final, and it closes that gap directly with a new Tasks extension. A server can now answer a tools/call with a task handle instead of a final result, and the client polls for status until the work finishes. It's a small addition on paper, but it's built for exactly the shape of work a deploy tool call is — and it lands in the same release that made the rest of MCP stateless, which is not a coincidence.

Why a deploy call breaks a synchronous tool call

MCP's tools/call is, by default, a request-response pair: the client sends arguments, the server sends back a result, and the HTTP connection stays open the whole time. That model works fine for a tool that looks something up or writes a file. It falls apart for a tool that builds a container image and rolls it out across machines.

Two things go wrong in practice:

  • Timeouts. Most MCP clients and the HTTP infrastructure in front of them (load balancers, gateways, serverless function limits) cap a single request well under the two-to-five minutes a real build-push-rollout cycle can take. A deploy call that's still legitimately running gets treated as a hung connection and killed.
  • No visibility into a request that's "still going." Before this extension, a server's only standard way to report progress on a long call was to push notifications/progress messages down the same connection while the client's request stayed open — which meant the server still had to hold that connection alive for the entire deploy, and the client still had to block on it. It's progress reporting bolted onto a synchronous call, not an actual asynchronous primitive.

Every team that has shipped a deploy-from-chat MCP server before this spec has worked around both problems the same way: split deploy into two custom tools — one that kicks the build off and returns immediately, and a second, deploy_status or similar, that the client is expected to know to call in a loop. It works, but it's bespoke. Nothing in the protocol tells the client that deploy_status exists, how often to call it, when to give up, or how to cancel the in-flight deploy if the user changes their mind.

How the Tasks extension actually works

The Tasks extension formalizes exactly that pattern instead of leaving every server to reinvent it. The mechanics, per the spec:

  1. The client signals task support in its per-request capabilities. Task creation is server-directed — the server decides, per call, whether a given tools/call becomes a task or just answers inline.
  2. If the server decides deploy is going to take a while, it returns a CreateTaskResult (resultType: "task") instead of a normal result: a taskId, an initial status, a ttl, and a suggested pollIntervalMs.
  3. The client calls tasks/get with that taskId, waits roughly pollIntervalMs between calls, and repeats until the task reaches a terminal status — completed or failed — at which point the response carries the final result or error.
  4. If the deploy needs input mid-flight (say, a confirmation before overwriting a running production release), the task moves to input_required and the tasks/get response includes an inputRequests map. The client resolves it with tasks/update.
  5. tasks/cancel lets the client abort a task that's still in flight — the standard hook for "stop this deploy."

For a deploy tool call on a platform like bex, that sequence looks like this: the agent calls deploy with a repo and branch; the server immediately returns a task with pollIntervalMs: 2000; the client polls every two seconds while build, push, and rollout happen server-side; each poll returns status: "working" until the rollout's health checks pass, at which point the final tasks/get call returns completed with the live URL and deploy ID in the result. If the agent (or the human behind it) decides to abort mid-rollout, tasks/cancel triggers the same abort path a bex rollback would.

Old workaround vs. Tasks extension, side by side

The functional gap between the hand-rolled version teams have been shipping and the standardized one is smaller than the protocol-churn headlines suggest — but it's real, and it's exactly the gap the TODO for this post asked about:

Hand-rolled deploy_start / deploy_statusTasks extension
DiscoverabilityClient has to know a second tool exists and call it in a loop — nothing in the schema says soCreateTaskResult tells the client a task exists in the same response that started it
Polling cadenceGuessed by whoever wrote the client integrationServer-supplied pollIntervalMs — the server that knows how long a rollout takes sets the pace
ExpiryUndefined — a stuck task lingers forever or the integration has to invent a timeoutttl is part of the task object
Cancel / abortA third custom tool, if it exists at all — most hand-rolled versions skip ittasks/cancel is a standard method every Tasks-aware client already knows how to call
Mid-flight inputNot modeled — the deploy either runs to completion or the integration blocks synchronously for a promptinput_required + tasks/update is a defined state transition
PortabilityWorks with the one client it was built againstAny MCP client that implements the Tasks extension can drive it, with zero bex-specific glue

That last row is the one that matters most for a platform, not just a single integration: a hand-rolled deploy_status tool only works with whichever chat client the team building it happened to test against. A Tasks-based deploy tool works with any MCP client — Claude, an internal agent runtime, a CI bot — the moment that client implements the extension, because the polling contract lives in the protocol instead of in a README someone has to read.

Why this isn't a bolt-on — it's the other half of the same spec

The Tasks extension shipped in the same release that made the rest of MCP stateless: protocol-level sessions and the Mcp-Session-Id header are gone, and — the detail that actually explains why Tasks had to exist now — server-initiated requests are only permitted while the server is actively processing a client request. That rule quietly kills the old progress-notification-over-a-held-connection pattern described above; a server can no longer push a "still building" update down a connection that isn't currently mid-request. Once the core protocol stopped allowing servers to hold a connection open and push updates down it, a poll-based async primitive stopped being optional — it's the only way left to model work that outlives one request. Tasks isn't a convenience feature riding along with the stateless rewrite; it's the piece that had to ship alongside it or long-running tools would have had no home at all.

For a Render-compatible deploy API, that's a good trade even before the developer-experience upgrade: bex's own deploy pipeline is already async-job-shaped under the hood — a deploy today is a build, a push, and a rollout tracked as a job with a status, not a single blocking call. Wiring the MCP server's deploy and rollback tools against tasks/get/tasks/cancel from day one means the MCP layer just exposes an interface bex's job model already has, instead of the MCP server inventing its own polling contract on top of a job system that already tracks state. Building the hand-rolled version first and migrating to Tasks later would mean maintaining a second, MCP-specific notion of task state that has to stay in sync with the one bex's job queue already keeps — building against Tasks from day one skips that entire layer.

What it doesn't solve

Tasks standardizes the polling contract between client and server; it doesn't make a deploy fast, and it doesn't replace the operational discipline a production deploy tool still needs regardless of transport — idempotent retries if a poll is missed, a real rollback path when a rollout's health checks fail partway through, rate limits so one agent can't queue unbounded concurrent deploys. Those stay the deploying platform's job no matter which protocol extension is carrying the status updates.

The takeaway for anyone building a deploy-from-chat integration

If you're building an MCP server today for any tool whose calls can run past a few seconds — a deploy, a data migration, a long-running test suite — the hand-rolled poll-your-own-endpoint pattern is no longer the only option, and after July 28, 2026, it's the option you have to justify rather than the default. The Tasks extension gives you taskId, pollIntervalMs, ttl, and tasks/cancel for free, in a shape every Tasks-aware client already knows how to drive.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with a Render-compatible API built for agents as first-class operators. Star the repo on GitHub or deploy your first app today.

Sources

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide