modelcontextprotocol / modelcontextprotocol/kotlin-sdk
[Proposal] SEP-2260: Require Server requests to be associated with Client requests (#807)
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:
- These requests MUST be associable with an originating client-to-server request (e.g., during
tools/call,resources/read, orprompts/getexecution). - 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).
- The operational server-to-client Ping is explicitly exempted from this restriction to preserve keep-alive and health-check capabilities.
- On the wire, the relationship is established by associating the client's
RequestIdvia_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 astools/callwith ID42), wrap handler execution with the coroutine context element:withContext(McpClientRequestContext(clientRequestId)) { handler.handle(params) } - Nested Outbound Requests:
InServerSession.createMessage(...)(sampling),ServerSession.listRoots(...), andServerSession.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:
IfcurrentClientRequestId()isnulland no explicitrelatedRequestIdis supplied, log a warning (or throw in strict mode) indicating that unsolicited server requests are deprecated per SEP-2260. ServerSession.ping()ignorescurrentClientRequestId()and remains exempt from this constraint.
- Automatically query
4. Validation in Client (kotlin-sdk-client)
In kotlin-sdk-client:
- When receiving a server request for
sampling/createMessage,roots/list, orelicitation/create:- Verify that
relatedRequestIdis 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.
- Verify that
5. Testing & Conformance
- Unit test in
kotlin-sdk-server:- Verify that a tool handler calling
session.createMessage()automatically generates a sampling request with_meta.relatedRequestIdequal to the tool call's request ID. - Verify that
session.ping()does not injectrelatedRequestId.
- Verify that a tool handler calling
- Client-side verification:
- Assert that client receives and correlates
relatedRequestIdwith in-flight calls.
- Assert that client receives and correlates
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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