Client-side TTL-honoring cache — SEP-2549 follow-up
Parent: #875 (model fields landed in #889)
Spec: https://modelcontextprotocol.io/specification/draft/server/utilities/caching
SEP: https://modelcontextprotocol.io/seps/2549-TTL-for-list-results
Track: Specification · Stage: final · Priority: P2 (optional fast follow)
Needs code changes: Yes (Medium) — additive client behavior
Summary
#889 added ttlMs / cacheScope on cacheable results (tools/list, prompts/list, resources/list, resources/templates/list, resources/read). Clients can already read those fields; this issue covers honoring them with an automatic response cache in the client service layer.
This is optional per the SEP (TTL is a freshness hint). TypeScript and Python SDKs already ship a built-in client cache; rust-sdk does not yet.
Why
Without a client cache, every list_* / read_resource call still hits the wire even when the server declared a positive TTL. A TTL-honoring cache:
- Reduces round trips for stable feature lists
- Complements (does not replace)
list_changed / resources/updated notifications
- Improves behavior on transports where long-lived SSE is unavailable
Proposed work
Non-goals
References
Client-side TTL-honoring cache — SEP-2549 follow-up
Parent: #875 (model fields landed in #889)
Spec: https://modelcontextprotocol.io/specification/draft/server/utilities/caching
SEP: https://modelcontextprotocol.io/seps/2549-TTL-for-list-results
Track: Specification · Stage: final · Priority: P2 (optional fast follow)
Needs code changes: Yes (Medium) — additive client behavior
Summary
#889 added
ttlMs/cacheScopeon cacheable results (tools/list,prompts/list,resources/list,resources/templates/list,resources/read). Clients can already read those fields; this issue covers honoring them with an automatic response cache in the client service layer.This is optional per the SEP (TTL is a freshness hint). TypeScript and Python SDKs already ship a built-in client cache; rust-sdk does not yet.
Why
Without a client cache, every
list_*/read_resourcecall still hits the wire even when the server declared a positive TTL. A TTL-honoring cache:list_changed/resources/updatednotificationsProposed work
crates/rmcp/src/service/client.rs(or a dedicated module) that stores cacheable results keyed by method + request params that affect the result (e.g.uriforresources/read,cursorfor paginated lists).ttlMsfreshness: fresh whilenow < t_received + ttlMs; treat absent / negative /0as immediately stale.cacheScope:"public"— may be shared across callers"private"— MUST be partitioned by authorization context; MUST NOT serve across different auth contextstools/list_changed,prompts/list_changed,resources/list_changed,resources/updated) even if TTL has not expired.inputResponses/requestState).ttlMs, enable/disable cache) — align with TS/Python SDK knobs where practical.privateisolation, and pagination keys.cachingscenario coverage as needed.Non-goals
References