All concepts
ConceptDeep8 minSince 2.0.0
conceptstasksmcpsdk-v2async

MCP on the v2 SDK: Tasks as a negotiated capability

Updated July 22, 2026

MCP on the v2 SDK: Tasks as a negotiated capability

For most of MCP’s life, tasks were a thing the protocol could do but would not promise. The API lived under mcp.server.experimental: present, usable, and explicitly disclaimed – shape subject to change, not something a careful gateway should build a governance surface against. That disclaimer was not incidental. It was the single fact that kept Hangar’s task governance built, tested, and dormant for a full quarter.

The v2 SDK changes that fact. In the v2 line, Tasks are promoted out of experimental into a first-class, negotiated protocol extension. That single word, negotiated, is what turns a stranded governance stack into a shippable one. This page is about why.

What v2 promoted is the SEP-1686 generation of Tasks – the negotiation, not the reshape. SEP-2663’s shapes are not in the SDK at all, in the betas or in the stable mcp==2.0.0; Hangar vendors them. Keep the two apart: the negotiation is what made the relay buildable, and the wire is what Hangar had to supply itself.

What “negotiated” buys that “experimental” did not

An experimental API is a surface you may call. A negotiated capability is a surface both peers agree exists before either uses it. The distinction is the whole story.

Under v2, a server declares a ServerTasksCapability at INITIALIZE, and the client’s capabilities come back the same way – TasksCall, TasksList, TasksCancel, plus the sampling and elicitation sub-capabilities a task might need mid-flight. Nothing is assumed. If a peer does not advertise a capability, you do not reach for it. The protocol gained an actual handshake for the async call-shape, and a discoverable registry of task request types (CreateTask, GetTask, GetTaskPayload, ListTasks, CancelTask) to hang handlers off of.

For a governance layer this is not a convenience – it is the precondition. A gateway that interposes on a call-shape has to know, per session, exactly which methods are in play. Negotiation hands it that knowledge as a contract instead of a guess. ADR-014 names the promotion as its unblocking fact directly: the API ADR-008 called “too churny to build against” is now stable and discoverable, so the relay seam can finally land.

The negotiated model comes with a discipline Hangar already held on principle: do not advertise what does not run. In v2 that principle becomes literal protocol. Hangar advertises the tasks capability at INITIALIZE only once the relay seam is live – never on the strength of a decision alone, never as a claim that a task is currently in flight. The advertisement is an honest signal that governed relay is available, and it flips on with activation, not before.

The same discipline runs one level deeper, into which handlers exist at all. The serving surface registers each tasks/* handler only while the running SDK defines its type – and that turned out to be the wrong mechanism entirely.

The probes watch mcp_types, which carries the SEP-1686 generation that 2026-07-28 removed from the core spec. Measured across mcp==2.0.0b2, 2.0.0rc1 and the stable 2.0.0: all 29 Task* classes are field-for-field identical, while the module around them was edited throughout. A frozen region inside a moving beta could have been a snapshot mid-migration; one that ships unchanged in a major is a decision – so the probes could never trip. ListTasksResult is still there, so tasks/list was always served; UpdateTaskRequest never arrived, so tasks/update never was.

SEP-2663 lands as a separate extension defining its own models (python-sdk#3005), precisely because they are wire-incompatible with what stayed behind. Hangar therefore vendors the wire rather than probing for it, and the rule that came out of it generalises: advertise and serve the wire you actually implement, and treat a capability probe as a hedge only when the probed module can still change.

Negotiation makes governed relay buildable – not automatic

Here is the honest boundary. Negotiation is what makes governed relay possible. It does not make the governance appear. A negotiated task_id handed straight through to a client is still a governance-blind promise – the protocol handshake does nothing to record who owns the task, which tool digest it was born under, or whether a later tasks/result is even yours to fetch.

What v2 supplies is the stable, discoverable surface on which Hangar attaches the governance it already built: register the task in a GovernedTaskStore before the handle reaches the client, pin the digest, key the entry on (target_server_id, task_id), and emit a TaskCreated event onto the provenance chain. The negotiated capability is the socket; relay-with-governance is what plugs into it. The mechanics of that – the ledger, the three handlers, the fail-closed authorization – are the subject of the governed task relay page.

And the promotion changes nothing about the species question. A negotiated capability is still a call-shape Hangar relays, not one it executes. Hangar creates no tasks, runs no scheduler, holds no results, crosses no worker -> main-loop bridge. What v2 lifted was ADR-008’s “permanently no relay” absolutism – the executor prohibition is carried forward untouched. The proxy stays a proxy; it just finally has a stable contract to govern across.

Why the timing was the point

ADR-008 gated the relay build on two triggers: a real upstream emitting tasks, and the task API graduating out of experimental. The second trigger was outside anyone’s control – it was the SDK’s to grant. mcp==2.0.0 granting it is why the relay seam ships in 2.0.0 now and not a quarter ago. The governance was never the bottleneck. The negotiability of the thing it governs was.


Grounded in ADR-014 (relay-with-governance) and the SDK v2 migration (mcp==2.0.0), with the SEP-2663 wire vendored per ADR-015. Everything is MIT and self-hosted – no SaaS. Shipped in 2.0.0; not in the 1.6.x line.