Skip to content

Implement client-side TTL-honoring cache (SEP-2549 follow-up) #974

Description

@DaleSeo

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

  • Add a client response cache in 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. uri for resources/read, cursor for paginated lists).
  • Honor ttlMs freshness: fresh while now < t_received + ttlMs; treat absent / negative / 0 as immediately stale.
  • Honor cacheScope:
    • "public" — may be shared across callers
    • "private" — MUST be partitioned by authorization context; MUST NOT serve across different auth contexts
  • Invalidate on relevant notifications (tools/list_changed, prompts/list_changed, resources/list_changed, resources/updated) even if TTL has not expired.
  • Do not cache multi-round-trip retry results (inputResponses / requestState).
  • Treat each paginated page as an independently cacheable entry; discard all pages for a list if a cursor becomes invalid.
  • Opt-in / configurable defaults (e.g. default TTL when server omits ttlMs, enable/disable cache) — align with TS/Python SDK knobs where practical.
  • Tests covering hit/miss, expiry, notification invalidation, private isolation, and pagination keys.
  • Wire / extend conformance caching scenario coverage as needed.

Non-goals

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    2026-07-28P2Medium: important but non-blocking improvementT-enhancementNew features and enhancementsT-modelModel/data structure changesT-serviceService layer changes

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions