@@ -1246,6 +1246,34 @@ pub type ProgressNotification = Notification<ProgressNotificationMethod, Progres
12461246
12471247pub type Cursor = String ;
12481248
1249+ /// Scope describing who may cache cacheable list/read results (SEP-2549).
1250+ ///
1251+ /// Defaults to [`CacheScope::Public`] when absent from the wire.
1252+ #[ derive( Debug , Default , Clone , Copy , PartialEq , Eq , Serialize , Deserialize ) ]
1253+ #[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
1254+ #[ serde( rename_all = "lowercase" ) ]
1255+ #[ non_exhaustive]
1256+ pub enum CacheScope {
1257+ /// Any client or intermediary may cache and serve the response to any user.
1258+ #[ default]
1259+ Public ,
1260+ /// Only the requesting user's client may cache the response.
1261+ Private ,
1262+ }
1263+
1264+ /// Normalize a `ttlMs` value during deserialization.
1265+ ///
1266+ /// Per SEP-2549, `ttlMs` MUST be `>= 0`; if a server returns a negative value,
1267+ /// clients SHOULD treat it as `0` (immediately stale). This tolerates that case
1268+ /// rather than erroring, while still accepting an absent field as `None`.
1269+ fn deserialize_ttl_ms < ' de , D > ( deserializer : D ) -> Result < Option < u64 > , D :: Error >
1270+ where
1271+ D : serde:: Deserializer < ' de > ,
1272+ {
1273+ let value = Option :: < i64 > :: deserialize ( deserializer) ?;
1274+ Ok ( value. map ( |ttl_ms| ttl_ms. max ( 0 ) as u64 ) )
1275+ }
1276+
12491277macro_rules! paginated_result {
12501278 ( $t: ident {
12511279 $i_item: ident: $t_item: ty
@@ -1258,24 +1286,50 @@ macro_rules! paginated_result {
12581286 /// Result type discriminator. Absent values deserialize as `"complete"`.
12591287 #[ serde( default ) ]
12601288 pub result_type: ResultType ,
1261- #[ serde( rename = "_meta" , skip_serializing_if = "Option::is_none" ) ]
1289+ #[ serde( rename = "_meta" , default , skip_serializing_if = "Option::is_none" ) ]
12621290 pub meta: Option <Meta >,
1263- #[ serde( skip_serializing_if = "Option::is_none" ) ]
1291+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
12641292 pub next_cursor: Option <Cursor >,
1293+ /// Time, in milliseconds, that this result may be treated as fresh (SEP-2549).
1294+ /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1295+ /// with older spec versions.
1296+ #[ serde(
1297+ default ,
1298+ deserialize_with = "deserialize_ttl_ms" ,
1299+ skip_serializing_if = "Option::is_none"
1300+ ) ]
1301+ pub ttl_ms: Option <u64 >,
1302+ /// Scope describing who may cache this result (SEP-2549).
1303+ /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1304+ /// with older spec versions.
1305+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
1306+ pub cache_scope: Option <CacheScope >,
12651307 pub $i_item: $t_item,
12661308 }
12671309
12681310 impl $t {
1269- pub fn with_all_items(
1270- items: $t_item,
1271- ) -> Self {
1311+ pub fn with_all_items( items: $t_item) -> Self {
12721312 Self {
12731313 result_type: ResultType :: default ( ) ,
12741314 meta: None ,
12751315 next_cursor: None ,
1316+ ttl_ms: None ,
1317+ cache_scope: None ,
12761318 $i_item: items,
12771319 }
12781320 }
1321+
1322+ /// Set the time, in milliseconds, that this result may be treated as fresh.
1323+ pub fn with_ttl_ms( mut self , ttl_ms: u64 ) -> Self {
1324+ self . ttl_ms = Some ( ttl_ms) ;
1325+ self
1326+ }
1327+
1328+ /// Set the cache scope for this result.
1329+ pub fn with_cache_scope( mut self , cache_scope: CacheScope ) -> Self {
1330+ self . cache_scope = Some ( cache_scope) ;
1331+ self
1332+ }
12791333 }
12801334 } ;
12811335}
@@ -1368,12 +1422,27 @@ pub type ReadResourceRequestParam = ReadResourceRequestParams;
13681422
13691423/// Result containing the contents of a read resource
13701424#[ derive( Debug , Serialize , Deserialize , Clone , PartialEq ) ]
1425+ #[ serde( rename_all = "camelCase" ) ]
13711426#[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
13721427#[ non_exhaustive]
13731428pub struct ReadResourceResult {
13741429 /// Result type discriminator. Absent values deserialize as `"complete"`.
13751430 #[ serde( default ) ]
13761431 pub result_type : ResultType ,
1432+ /// Time, in milliseconds, that this result may be treated as fresh (SEP-2549).
1433+ /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1434+ /// with older spec versions.
1435+ #[ serde(
1436+ default ,
1437+ deserialize_with = "deserialize_ttl_ms" ,
1438+ skip_serializing_if = "Option::is_none"
1439+ ) ]
1440+ pub ttl_ms : Option < u64 > ,
1441+ /// Scope describing who may cache this result (SEP-2549).
1442+ /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1443+ /// with older spec versions.
1444+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
1445+ pub cache_scope : Option < CacheScope > ,
13771446 /// The actual content of the resource
13781447 pub contents : Vec < ResourceContents > ,
13791448 #[ serde( rename = "_meta" , skip_serializing_if = "Option::is_none" ) ]
@@ -1385,10 +1454,24 @@ impl ReadResourceResult {
13851454 pub fn new ( contents : Vec < ResourceContents > ) -> Self {
13861455 Self {
13871456 result_type : ResultType :: default ( ) ,
1457+ ttl_ms : None ,
1458+ cache_scope : None ,
13881459 contents,
13891460 meta : None ,
13901461 }
13911462 }
1463+
1464+ /// Set the time, in milliseconds, that this result may be treated as fresh.
1465+ pub fn with_ttl_ms ( mut self , ttl_ms : u64 ) -> Self {
1466+ self . ttl_ms = Some ( ttl_ms) ;
1467+ self
1468+ }
1469+
1470+ /// Set the cache scope for this result.
1471+ pub fn with_cache_scope ( mut self , cache_scope : CacheScope ) -> Self {
1472+ self . cache_scope = Some ( cache_scope) ;
1473+ self
1474+ }
13921475}
13931476
13941477/// Request to read a specific resource
0 commit comments