You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Scope the CreateTaskResult arm to the revision that defines it
Review follow-up, four corrections.
The arm goes back to `2025-11-25` alone. It was widened to all four pre-2026
rows on the reasoning that they share `v2025.CallToolRequest` and so already
parse `params.task`, which made refusing to answer at 2025-06-18 look like the
same incoherence one revision along. That was the wrong conclusion: the request
side accepting `task` at revisions that predate SEP-1686 is an artifact of the
shared schema era, and matching it on the response side propagates the artifact
instead of containing it. A client that sends `task` to a 2024-11-05 server is
already off-spec, and a clean INTERNAL_ERROR serves it better than a result
shape its revision cannot parse. The rows now say what each revision defines,
which is also what the requirement's `added_in` window claims.
`params.task` is not an era test, and the migration guide said it was. The
handler receives the version-free params model, which carries `task` at every
version, so a client can set it on a 2026-07-28 connection and drive a handler
straight into a result that revision rejects. The recipe now gates on
`ctx.protocol_version`.
The field cache moves from a `ClassVar` to a module-level cached function. The
`ClassVar` was inherited by every subclass, so correctness rested on the
serializer remembering to read `cls.__dict__` rather than the attribute; keying
a `cache` on the class removes the hazard along with twenty lines. The restored
key now also honours `serialize_by_alias`, not just the `by_alias` argument, so
a config-driven dump cannot come back mixed-convention.
`tests/types/test_parity.py` now shares `admits_none` with the runtime instead
of inlining a copy that had already drifted, and its docstring no longer calls
itself the authority: sharing the predicate makes it a consistency check between
the schema-side rule and the annotation-side one, which is what it actually is.
The generator's base patcher matches whatever bases codegen chose, so a `$def`
that composes through `allOf` no longer aborts regeneration.
Two things to know about handlers registered this way. They serve every negotiated version, so a server that also answers 2026-era clients should check `ctx.protocol_version` and reject anything outside 2025-11-25; the method names collide with the 2026 tasks extension but the payloads are not compatible. And their results are not validated against a per-version surface, so raise `MCPError` for the failure cases rather than letting an exception escape: an unhandled one reaches the client as an unmapped error carrying the exception text.
1680
1680
1681
-
The same era check belongs in `on_call_tool`. Its return type admits a `CreateTaskResult` at every version because the signature has no version to key on, but 2026-07-28 dropped tasks from the core protocol and rejects the shape, turning it into an opaque internal error. Returning one only when `params.task`is set keeps that unreachable for a well-behaved client, since a client that never learned the field never sets it.
1681
+
The same era check belongs in `on_call_tool`, and `params.task` is not a substitute for it. The handler receives the version-free params model, which carries `task` at every version, so a client can set the field on a 2026-07-28 connection and reach a handler that then answers with a `CreateTaskResult`, which that revision rejects as an opaque internal error. Gate on `ctx.protocol_version == "2025-11-25"` and treat `params.task`as the opt-in within that era, not as the era test.
1682
1682
1683
1683
`Server.get_capabilities` does not derive a `tasks` capability from the registered handlers, and a spec-compliant client will not augment a request until it sees one, so build the advertisement explicitly:
0 commit comments