modelcontextprotocol / modelcontextprotocol/kotlin-sdk

[Proposal] SEP-2260: Require Server requests to be associated with Client requests (#807)

Open
#1,002 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-2260 Server Request Scoping & Client Request Association Design (#807)
Motivation & Context

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

SEP-2260 tightens the scoping of server-to-client requests (roots/list, sampling/createMessage, and elicitation/create), establishing that:

  1. These requests MUST be associable with an originating client-to-server request (e.g., during tools/call, resources/read, or prompts/get execution).
  2. Standalone, unsolicited server-initiated requests of these types outside the scope of an in-flight client request MUST NOT be used in modern/stateless transports (such as Streamable HTTP without dedicated bi-directional streams).
  3. The operational server-to-client Ping is explicitly exempted from this restriction to preserve keep-alive and health-check capabilities.
  4. On the wire, the relationship is established by associating the client's RequestId via _meta.relatedRequestId (or request params).

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


Proposed Architecture & Component Design
1. Coroutine Context Scoping for In-Flight Client Requests (kotlin-sdk-core)

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

  • Define a coroutine context element representing the active client request:
    public class McpClientRequestContext(
        public val requestId: RequestId
    ) : CoroutineContext.Element {
        public companion object Key : CoroutineContext.Key<McpClientRequestContext>
        override val key: CoroutineContext.Key<*> get() = Key
    }
    
  • Add utility helpers to extract the current client RequestId:
    public suspend fun currentClientRequestId(): RequestId? =
        coroutineContext[McpClientRequestContext]?.requestId
    
2. Wire Representation in _meta
  • In common.kt, define the standardized metadata key:
    public const val RELATED_REQUEST_ID_KEY: String = "relatedRequestId"
    
  • Add accessor extensions on WithMeta:
    public val WithMeta.relatedRequestId: RequestId?
        get() = meta?.get(RELATED_REQUEST_ID_KEY)?.jsonPrimitive?.contentOrNull?.let { RequestId(it) }
    
3. Automatic Request Association in Server Session (kotlin-sdk-server)

In kotlin-sdk-server:

  • Handler Execution Scoping:
    When the server dispatches a client-initiated JSON-RPC request (such as tools/call with ID 42), wrap handler execution with the coroutine context element:
    withContext(McpClientRequestContext(clientRequestId)) {
        handler.handle(params)
    }
    
  • Nested Outbound Requests:
    In ServerSession.createMessage(...) (sampling), ServerSession.listRoots(...), and ServerSession.createElicitation(...):
    • Automatically query currentClientRequestId().
    • If present, inject _meta = JsonObject(mapOf(RELATED_REQUEST_ID_KEY to JsonPrimitive(id.value))) if not already explicitly provided.
    • For negotiated protocol version >= 2026-07-28:
      If currentClientRequestId() is null and no explicit relatedRequestId is supplied, log a warning (or throw in strict mode) indicating that unsolicited server requests are deprecated per SEP-2260.
    • ServerSession.ping() ignores currentClientRequestId() and remains exempt from this constraint.
4. Validation in Client (kotlin-sdk-client)

In kotlin-sdk-client:

  • When receiving a server request for sampling/createMessage, roots/list, or elicitation/create:
    • Verify that relatedRequestId is present and matches an currently pending client-to-server request.
    • Provide configurable client validation (enforceAssociatedServerRequests = true|false) so clients can reject unassociated requests or log diagnostics.
5. Testing & Conformance
  • Unit test in kotlin-sdk-server:
    • Verify that a tool handler calling session.createMessage() automatically generates a sampling request with _meta.relatedRequestId equal to the tool call's request ID.
    • Verify that session.ping() does not inject relatedRequestId.
  • Client-side verification:
    • Assert that client receives and correlates relatedRequestId with in-flight calls.

Next Steps

Upon review and consensus on the coroutine-based request scoping design, we are ready to submit a PR implementing McpClientRequestContext, metadata injection, and unit 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 by reading common.kt and the request-dispatch paths in kotlin-sdk-core, then inspect ServerSession.createMessage(), listRoots(), createElicitation(), and ping() in kotlin-sdk-server. Review the client request-handling paths in kotlin-sdk-client and the existing unit-test structure. Done means the agreed SEP-2260 design is implemented across the three modules, with server association and ping-exemption tests plus client correlation coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
api, backend-api-design, testing-qa
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.