Client-side 2026-07-28 support: .discover()/.adopt() + Client(mode=); request-metadata green (#2950)

This commit is contained in:
Max 2026-06-25 16:09:23 +02:00 • committed by GitHub
commit f226d00d0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -63,9 +63,7 @@ CONNECTABLE_TRANSPORTS: tuple[Transport, ...] = ("in-memory", "sse", "streamable
TRANSPORT_SPEC_VERSIONS: dict[Transport, tuple[SpecVersion, ...]] = {
"sse": ("2025-11-25",),
# Temporary lock: the in-memory transport has no modern entry point yet, so it cannot
# negotiate the newer revision. Remove once an in-memory factory for the modern path lands.
"in-memory": ("2025-11-25",),
"in-memory": ("2025-11-25", "2026-07-28"),
# At the newer revision the protocol-version header check runs before the stateless branch is
# taken, so a stateless connection at that revision behaves identically to the stateful one.
# Locked to avoid a redundant matrix column; revisit if the header/stateless ordering changes.
@ -252,6 +250,7 @@ REQUIREMENTS: dict[str, Requirement] = {
source=f"{SPEC_BASE_URL}/basic/lifecycle#initialization",
behavior="The client's name, version, and title are visible to server handlers after initialization.",
removed_in="2026-07-28",
superseded_by="lifecycle:envelope:stamped-on-every-request",
note="initialize handshake removed at 2026-07-28; per-request _meta envelope replaces it.",
arm_exclusions=(ArmExclusion(reason="requires-session", transport="streamable-http-stateless"),),
),
@ -262,6 +261,7 @@ REQUIREMENTS: dict[str, Requirement] = {
"(sampling, elicitation, roots)."
),
removed_in="2026-07-28",
superseded_by="lifecycle:envelope:stamped-on-every-request",
note="initialize handshake removed at 2026-07-28; per-request _meta envelope replaces it.",
arm_exclusions=(ArmExclusion(reason="requires-session", transport="streamable-http-stateless"),),
),
@ -395,6 +395,82 @@ REQUIREMENTS: dict[str, Requirement] = {
"hosting:http:legacy-no-modern-vocabulary covers the same vocabulary set"
),
),
"lifecycle:envelope:stamped-on-every-request": Requirement(
source=f"{SPEC_2026_BASE_URL}/basic#_meta",
behavior=(
"Every client→server request on a modern-negotiated session carries "
"_meta.{protocolVersion,clientInfo,clientCapabilities}; notifications do not."
),
added_in="2026-07-28",
supersedes=("lifecycle:initialize:client-info", "lifecycle:initialize:client-capabilities"),
),
"lifecycle:envelope:header-matches-meta": Requirement(
source=f"{SPEC_2026_BASE_URL}/basic/transports/streamable-http#headers",
behavior="On HTTP, the MCP-Protocol-Version header on every POST matches _meta.protocolVersion in the body.",
transports=("streamable-http", "streamable-http-stateless"),
added_in="2026-07-28",
note="HTTP-only: the header is a streamable-http transport concern; stdio and in-memory carry no headers.",
),
"lifecycle:discover:basic": Requirement(
source=f"{SPEC_2026_BASE_URL}/basic/lifecycle#discover",
behavior=(
"Calling discover() sends server/discover with no params and returns a typed DiscoverResult "
"carrying protocolVersion, capabilities, serverInfo and the cache hint fields."
),
added_in="2026-07-28",
),
"lifecycle:discover:retry-on-32022": Requirement(
source=f"{SPEC_2026_BASE_URL}/basic/lifecycle#version-errors",
behavior=(
"When server/discover returns -32022 UnsupportedProtocolVersion, the client retries once with "
"the intersection of error.data.supported and its own modern versions; an empty intersection raises."
),
added_in="2026-07-28",
),
"lifecycle:discover:fallback-method-not-found": Requirement(
source=f"{SPEC_2026_BASE_URL}/basic/transports/stdio#backward-compatibility",
behavior=(
"When server/discover returns any JSON-RPC error or a bare HTTP 4xx, an auto-negotiating "
"client falls back to the legacy initialize handshake and the connection succeeds at a "
"handshake-era version (legacy servers reject the probe with various codes)."
),
added_in="2026-07-28",
),
"lifecycle:discover:network-error-raises": Requirement(
source="sdk",
behavior=(
"A network/connection error during server/discover propagates to the caller without "
"falling back to initialize; any rpc-error or 4xx falls back (legacy servers reject the "
"probe with various codes). An outage is never an era verdict."
),
transports=("streamable-http", "streamable-http-stateless"),
added_in="2026-07-28",
note="HTTP-only: distinguishes transport-level failures from server-side rejection.",
),
"lifecycle:mode:legacy-never-probes": Requirement(
source="sdk",
behavior=(
"A Client constructed with mode='legacy' sends initialize as its first request "
"and never sends server/discover."
),
added_in="2026-07-28",
),
"lifecycle:mode:pin-never-handshakes": Requirement(
source="sdk",
behavior=(
"A Client constructed with mode='2026-07-28' sends no initialize and no server/discover; its "
"first wire request is the caller's first call, carrying the full _meta envelope."
),
added_in="2026-07-28",
),
"lifecycle:mode:prior-discover-zero-rtt": Requirement(
source="sdk",
behavior=(
"A Client constructed with prior_discover=<DiscoverResult> sends no negotiation traffic; "
"server_info and capabilities are populated from the prior result."
),
added_in="2026-07-28",
),
# ═══════════════════════════════════════════════════════════════════════════
# Protocol primitives: cancellation, timeout, progress, errors, _meta
# ═══════════════════════════════════════════════════════════════════════════
@ -581,7 +657,9 @@ REQUIREMENTS: dict[str, Requirement] = {
"Progress notifications emitted by a handler during a request are delivered to the caller's "
"progress callback, in order, with their progress, total, and message."
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"protocol:progress:token-injected": Requirement(
source=f"{SPEC_BASE_URL}/basic/utilities/progress#progress-flow",
@ -594,7 +672,14 @@ REQUIREMENTS: dict[str, Requirement] = {
"protocol:progress:token-unique": Requirement(
source=f"{SPEC_BASE_URL}/basic/utilities/progress#progress-flow",
behavior=("Concurrent in-flight requests that each supply a progress callback carry distinct progress tokens."),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
note=(
"Tested as the consequence: each callback receives only its own request's progress under "
"interleaved emission. Token distinctness is the JSON-RPC mechanism for that; the in-process "
"direct dispatcher carries the callback per-request without a wire-level token."
),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"protocol:progress:monotonic": Requirement(
source=f"{SPEC_BASE_URL}/basic/utilities/progress#progress-flow",
@ -607,7 +692,9 @@ REQUIREMENTS: dict[str, Requirement] = {
"handler that emits non-increasing values has them forwarded to the callback unchanged."
),
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"protocol:progress:stops-after-completion": Requirement(
source=f"{SPEC_BASE_URL}/basic/utilities/progress#behavior-requirements",
@ -745,7 +832,9 @@ REQUIREMENTS: dict[str, Requirement] = {
"Log notifications emitted by a tool handler during execution reach the client's logging "
"callback before the tool result returns."
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"tools:call:progress": Requirement(
source=f"{SPEC_BASE_URL}/basic/utilities/progress#progress-flow",
@ -753,7 +842,9 @@ REQUIREMENTS: dict[str, Requirement] = {
"Progress notifications emitted by a tool handler reach the caller's progress callback before "
"the tool result returns."
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"tools:call:sampling-roundtrip": Requirement(
source=f"{SPEC_BASE_URL}/client/sampling#creating-messages",
@ -974,14 +1065,18 @@ REQUIREMENTS: dict[str, Requirement] = {
"The Context logging helpers (debug/info/warning/error) send log message notifications at the "
"corresponding severity."
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"mcpserver:context:progress": Requirement(
source="sdk",
behavior=(
"Context.report_progress sends a progress notification against the requesting client's progress token."
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"mcpserver:context:elicit": Requirement(
source="sdk",
@ -1339,7 +1434,9 @@ REQUIREMENTS: dict[str, Requirement] = {
"logging:message:all-levels": Requirement(
source=f"{SPEC_BASE_URL}/server/utilities/logging#log-levels",
behavior="All eight RFC 5424 severity levels are deliverable as log message notifications.",
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"logging:message:fields": Requirement(
source=f"{SPEC_BASE_URL}/server/utilities/logging#log-message-notifications",
@ -1347,7 +1444,9 @@ REQUIREMENTS: dict[str, Requirement] = {
"A log message sent by a server handler is delivered to the client's logging callback with its "
"severity level, logger name, and data."
),
known_failures=(KnownFailure(spec_version="2026-07-28", note=_MODERN_NOTIFY_DROP, issue=None),),
known_failures=(
KnownFailure(spec_version="2026-07-28", transport="streamable-http", note=_MODERN_NOTIFY_DROP, issue=None),
),
),
"logging:message:filtered": Requirement(
source=f"{SPEC_BASE_URL}/server/utilities/logging#setting-log-level",
@ -1970,6 +2069,7 @@ REQUIREMENTS: dict[str, Requirement] = {
known_failures=(
KnownFailure(
spec_version="2026-07-28",
transport="streamable-http",
note=(
"List-mutation assertions hold; only the sentinel ctx.info() never reaches the client. "
+ _MODERN_NOTIFY_DROP