@@ -631,6 +631,47 @@ def parse_server_notification(
631631 return _monolith_row (monolith , method ).model_validate (_body (method , params ), by_name = False )
632632
633633
634+ def _wire_names (model : type [BaseModel ]) -> frozenset [str ]:
635+ """Wire keys of `model`'s declared fields (alias when one is set)."""
636+ return frozenset ((field .alias or field .serialization_alias or name ) for name , field in model .model_fields .items ())
637+
638+
639+ # Pre-2026 versions share the 2025-11-25 surface (see the module docstring).
640+ _SERVER_CAPABILITY_MODEL : Final [Mapping [str , type [BaseModel ]]] = MappingProxyType (
641+ {"2025-11-25" : v2025 .ServerCapabilities , "2026-07-28" : v2026 .ServerCapabilities }
642+ )
643+ _ALL_SERVER_CAPABILITY_NAMES : Final [frozenset [str ]] = _wire_names (v2025 .ServerCapabilities ) | _wire_names (
644+ v2026 .ServerCapabilities
645+ )
646+ # Results whose top-level `capabilities` object is `ServerCapabilities`.
647+ _CAPABILITY_RESULT_METHODS : Final [frozenset [str ]] = frozenset ({"initialize" , "server/discover" })
648+
649+
650+ def _capability_era (version : str ) -> str :
651+ """Schema era for capability shape. Every pre-2026 version uses 2025-11-25."""
652+ return "2026-07-28" if version >= "2026-07-28" else "2025-11-25"
653+
654+
655+ def _drop_cross_era_server_capability_keys (method : str , version : str , data : dict [str , Any ]) -> None :
656+ """Drop capability names declared only on another schema era.
657+
658+ `ServerCapabilities` is an open set, so the wire model keeps unknown keys.
659+ A name that is a real field on the other era is still version vocabulary and
660+ must not leak: `tasks` on 2026-07-28, `extensions` on every earlier version.
661+ Truly unknown keys, such as a draft `events` object, stay. Known fields were
662+ already validated by the surface model; this only removes the cross-era names.
663+ """
664+ if method not in _CAPABILITY_RESULT_METHODS :
665+ return
666+ capabilities = data .get ("capabilities" )
667+ if not isinstance (capabilities , dict ):
668+ return
669+ era_names = _wire_names (_SERVER_CAPABILITY_MODEL [_capability_era (version )])
670+ owned = cast ("dict[str, Any]" , capabilities )
671+ for name in _ALL_SERVER_CAPABILITY_NAMES - era_names :
672+ owned .pop (name , None )
673+
674+
634675def serialize_server_result (
635676 method : str ,
636677 version : str ,
@@ -640,8 +681,9 @@ def serialize_server_result(
640681) -> dict [str , Any ]:
641682 """Validate `data` against `surface` and return its surface-shaped dump.
642683
643- The surface model carries `extra="ignore"`, so fields not in `version`'s
644- schema are dropped from the returned dict.
684+ Closed surface models carry `extra="ignore"`, so fields not in `version`'s
685+ schema are dropped. `ServerCapabilities` is an open set: unknown keys are
686+ kept, and names that belong only to another schema era are removed.
645687
646688 Raises:
647689 ValueError: `version` is not a known protocol version.
@@ -650,9 +692,11 @@ def serialize_server_result(
650692 """
651693 _check_known_version (version )
652694 adapter = _adapter (surface [(method , version )])
653- return adapter .dump_python (
695+ dumped = adapter .dump_python (
654696 adapter .validate_python (data , by_name = False ), by_alias = True , mode = "json" , exclude_none = True
655697 )
698+ _drop_cross_era_server_capability_keys (method , version , dumped )
699+ return dumped
656700
657701
658702def validate_server_result (
0 commit comments