modelcontextprotocol / modelcontextprotocol/kotlin-sdk

[Proposal] SEP-2549: TTL for List Results implementation design (#813)

Open
#1,001 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
1.5k
Forks
248
Avg merge
1d 20h
Merged PRs (30d)
23

Description

Proposal: SEP-2549 TTL for List Results Implementation Design (#813)
Motivation & Context

Tracking implementation of SEP-2549 in kotlin-sdk for the 2026-07-28 MCP specification release (aligned with umbrella issue #842).

SEP-2549 introduces response caching capabilities for list operations (tools/list, resources/list, resources/templates/list, prompts/list, and resources/read). This significantly reduces transport round-trips and polling overhead for static or slow-changing server surfaces while preserving real-time invalidation via existing list-changed notifications.

Key wire-level fields introduced by SEP-2549:

  • ttlMs: Long?: Time-to-live in milliseconds that a client may treat the result as fresh.
  • cacheScope: String?: Scope of caching ("public" or "private"). Defaults to "private" if ttlMs is set but cacheScope is omitted.

This proposal outlines the Kotlin Multiplatform architecture for implementing SEP-2549 in kotlin-sdk-core, kotlin-sdk-client, and kotlin-sdk-server.


Proposed Architecture & Component Design
1. Protocol Types & Enums (kotlin-sdk-core)

In kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/:

  • Introduce a type-safe CacheScope enum in common.kt:
    @Serializable
    public enum class CacheScope(public val value: String) {
        @SerialName("public")
        PUBLIC("public"),
    
        @SerialName("private")
        PRIVATE("private");
    }
    
  • Define a shared interface for cacheable results:
    public sealed interface WithCacheControl {
        public val ttlMs: Long?
        public val cacheScope: CacheScope?
    }
    
  • Update result data classes (ListToolsResult, ListResourcesResult, ListResourceTemplatesResult, ListPromptsResult, ReadResourceResult) to implement WithCacheControl:
    @Serializable
    public data class ListToolsResult(
        val tools: List<Tool>,
        val nextCursor: String? = null,
        override val _meta: JsonObject? = null,
        @SerialName("ttlMs")
        override val ttlMs: Long? = null,
        @SerialName("cacheScope")
        override val cacheScope: CacheScope? = null,
    ) : ServerResult, PaginatedResult, WithCacheControl
    
  • Add wire validation:
    • Negative ttlMs values should be rejected or clamped to 0L during deserialization/construction.
    • Omitted or blank cacheScope gracefully resolves to null on the wire, falling back to client-side PRIVATE interpretation when ttlMs != null.
2. Client-Side Caching Layer (kotlin-sdk-client)

In kotlin-sdk-client:

  • Add a client-side memory cache contract:
    public interface McpResultCache {
        public suspend fun <T : WithCacheControl> get(key: String): T?
        public suspend fun <T : WithCacheControl> put(key: String, result: T)
        public suspend fun invalidate(pattern: String)
        public suspend fun clear()
    }
    
  • Transparent Cache Integration:
    • When client.listTools(), client.listResources(), etc., are invoked, the client checks McpResultCache.
    • If a cached response exists and now < cachedAt + ttlMs, return the cached instance without network I/O.
    • Automatically invalidate cached tool/resource/prompt buckets when corresponding server notifications arrive:
      • notifications/tools/list_changed -> invalidate tools/*
      • notifications/resources/list_changed -> invalidate resources/*
      • notifications/prompts/list_changed -> invalidate prompts/*
3. Server-Side Ergonomics (kotlin-sdk-server)

In kotlin-sdk-server:

  • Enable servers to attach caching hints declaratively:
    server.addTool(
        name = "calculate",
        description = "Perform calculation",
        inputSchema = schema,
        cacheControl = CacheControl(ttlMs = 300_000L, scope = CacheScope.PUBLIC)
    ) { args -> ... }
    
  • Or provide an overload on server response builders allowing handlers to set ttlMs and cacheScope based on whether the data is user-scoped or system-wide.
4. Backward Compatibility & Testing
  • Fully backward-compatible: ttlMs and cacheScope default to null, producing identical wire JSON for older protocol versions.
  • Unit and Conformance Tests:
    • Serializer round-trip tests for ttlMs and cacheScope (both present, ttlMs alone, neither present).
    • Client cache hit/miss behavior within and after TTL expiry.
    • Cache eviction verification upon receiving tools/list_changed notifications.

Next Steps

Upon maintainer review and consensus on the component design, we are ready to submit an initial PR implementing the kotlin-sdk-core schema fields and serialization tests.

AI assistance disclosure: AI was used to discover this opportunity and draft the change or text. The submission was checked against the prepared artifact and recorded verification evidence.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/common.kt and the listed result data classes to review the protocol fields and serialization conventions. Then inspect kotlin-sdk-client and kotlin-sdk-server entry points and existing tests for list operations and notifications. Done means maintainer consensus on the architecture and a tested initial implementation covering the stated compatibility and cache behaviors.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
backend-api-design, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.