@@ -259,10 +259,12 @@ pub struct MetaObject(pub JsonObject);
259259
260260/// Deprecated alias for [`MetaObject`].
261261///
262- /// Note: request and notification metadata now have dedicated types; use
263- /// [`RequestMetaObject`] or [`NotificationMetaObject`] where those are expected.
262+ /// This is a re-export rather than a type alias so the `Meta(...)` tuple
263+ /// constructor keeps working. Request and notification metadata now have
264+ /// dedicated types; use [`RequestMetaObject`] or [`NotificationMetaObject`]
265+ /// where those are expected.
264266#[ deprecated( note = "Use MetaObject (or RequestMetaObject / NotificationMetaObject)" ) ]
265- pub type Meta = MetaObject ;
267+ pub use self :: MetaObject as Meta ;
266268
267269impl MetaObject {
268270 /// Reserved `_meta` key for the W3C Trace Context `traceparent` value (SEP-414).
@@ -390,14 +392,15 @@ impl schemars::JsonSchema for MetaObject {
390392///
391393/// In addition to arbitrary extension keys, requests reserve:
392394/// - `progressToken` for progress tracking
393- /// - `io.modelcontextprotocol/protocolVersion` (SEP-1319 )
394- /// - `io.modelcontextprotocol/clientInfo` (SEP-1319 )
395- /// - `io.modelcontextprotocol/clientCapabilities` (SEP-1319 )
396- /// - `io.modelcontextprotocol/logLevel` (SEP-1319 )
395+ /// - `io.modelcontextprotocol/protocolVersion` (SEP-2575 )
396+ /// - `io.modelcontextprotocol/clientInfo` (SEP-2575 )
397+ /// - `io.modelcontextprotocol/clientCapabilities` (SEP-2575 )
398+ /// - `io.modelcontextprotocol/logLevel` (SEP-2575 )
397399///
398400/// The 2026-07-28 draft schema marks the protocol-version, client-info, and
399401/// client-capabilities keys as required; earlier protocol versions do not know
400- /// them. All keys therefore stay optional at runtime — use
402+ /// them. All keys therefore stay optional at runtime and in the generated
403+ /// (version-shared) JSON schema — use
401404/// [`RequestMetaObject::missing_required_keys`] to validate a request against
402405/// the negotiated protocol version.
403406///
@@ -496,8 +499,12 @@ impl RequestMetaObject {
496499 . insert_serialized ( Self :: META_KEY_LOG_LEVEL , log_level) ;
497500 }
498501
499- /// Return the [`Self::DRAFT_REQUIRED_KEYS`] absent from this map, if
500- /// `protocol_version` requires them.
502+ /// Return the [`Self::DRAFT_REQUIRED_KEYS`] whose values are absent or
503+ /// invalid in this map, if `protocol_version` requires them.
504+ ///
505+ /// A key counts as missing when it is not present *or* when its value does
506+ /// not decode into the expected type (e.g. a numeric `protocolVersion` or
507+ /// a string `clientInfo`), matching what the typed accessors return.
501508 ///
502509 /// Protocol versions before 2026-07-28 have no required request metadata,
503510 /// so this always returns an empty list for them.
@@ -513,7 +520,7 @@ impl RequestMetaObject {
513520 /// meta.missing_required_keys(&ProtocolVersion::V_2025_11_25)
514521 /// .is_empty()
515522 /// );
516- /// // The 2026-07-28 draft requires the SEP-1319 keys.
523+ /// // The 2026-07-28 draft requires the SEP-2575 keys.
517524 /// assert_eq!(
518525 /// meta.missing_required_keys(&ProtocolVersion::V_2026_07_28),
519526 /// RequestMetaObject::DRAFT_REQUIRED_KEYS.to_vec(),
@@ -523,10 +530,17 @@ impl RequestMetaObject {
523530 if protocol_version. as_str ( ) < ProtocolVersion :: V_2026_07_28 . as_str ( ) {
524531 return Vec :: new ( ) ;
525532 }
526- Self :: DRAFT_REQUIRED_KEYS
527- . into_iter ( )
528- . filter ( |key| !self . 0 . 0 . contains_key ( * key) )
529- . collect ( )
533+ let mut missing = Vec :: new ( ) ;
534+ if self . protocol_version ( ) . is_none ( ) {
535+ missing. push ( Self :: META_KEY_PROTOCOL_VERSION ) ;
536+ }
537+ if self . client_info ( ) . is_none ( ) {
538+ missing. push ( Self :: META_KEY_CLIENT_INFO ) ;
539+ }
540+ if self . client_capabilities ( ) . is_none ( ) {
541+ missing. push ( Self :: META_KEY_CLIENT_CAPABILITIES ) ;
542+ }
543+ missing
530544 }
531545
532546 /// Insert every entry of `other`, overwriting existing keys on conflict.
@@ -572,6 +586,11 @@ impl schemars::JsonSchema for RequestMetaObject {
572586 let client_info = generator. subschema_for :: < Implementation > ( ) ;
573587 let client_capabilities = generator. subschema_for :: < ClientCapabilities > ( ) ;
574588 let log_level = generator. subschema_for :: < LoggingLevel > ( ) ;
589+ // rmcp generates one schema shared by every supported protocol
590+ // version, so the keys the 2026-07-28 draft marks as required are left
591+ // optional here: a 2025-11-25 request whose `_meta` only carries
592+ // `progressToken` is valid. Draft-strict validation is available at
593+ // runtime via [`RequestMetaObject::missing_required_keys`].
575594 schemars:: json_schema!( {
576595 "description" : "Metadata reserved by MCP on requests. Extension keys are also allowed." ,
577596 "type" : "object" ,
@@ -584,11 +603,6 @@ impl schemars::JsonSchema for RequestMetaObject {
584603 "io.modelcontextprotocol/clientCapabilities" : client_capabilities,
585604 "io.modelcontextprotocol/logLevel" : log_level,
586605 } ,
587- "required" : [
588- "io.modelcontextprotocol/protocolVersion" ,
589- "io.modelcontextprotocol/clientInfo" ,
590- "io.modelcontextprotocol/clientCapabilities" ,
591- ] ,
592606 "additionalProperties" : true ,
593607 } )
594608 }
@@ -831,6 +845,20 @@ mod tests {
831845 ) ;
832846 }
833847
848+ #[ test]
849+ fn treats_malformed_values_as_missing ( ) {
850+ let meta: RequestMetaObject = serde_json:: from_value ( serde_json:: json!( {
851+ "io.modelcontextprotocol/protocolVersion" : 123 ,
852+ "io.modelcontextprotocol/clientInfo" : "not an implementation" ,
853+ "io.modelcontextprotocol/clientCapabilities" : null,
854+ } ) )
855+ . unwrap ( ) ;
856+ assert_eq ! (
857+ meta. missing_required_keys( & ProtocolVersion :: V_2026_07_28 ) ,
858+ RequestMetaObject :: DRAFT_REQUIRED_KEYS . to_vec( )
859+ ) ;
860+ }
861+
834862 #[ test]
835863 fn is_empty_when_draft_keys_are_present ( ) {
836864 let mut meta = RequestMetaObject :: new ( ) ;
0 commit comments