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.
Advertise exactly what you serve
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.