modelcontextprotocol / modelcontextprotocol/kotlin-sdk
[Proposal] SEP-2663: Tasks Extension architecture & migration design (#817)
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 1.5k
- Forks
- 248
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 23
Description
Proposal: SEP-2663 Tasks Extension Architecture & Migration Design (#817)
Motivation & Context
Tracking implementation of SEP-2663 in kotlin-sdk for the 2026-07-28 MCP specification release (aligned with umbrella issue #842).
The experimental tasks mechanism specified in 2025-11-25 served as an initial asynchronous execution mechanism for tool calls. Real-world experience surfaced critical structural issues:
- Fragile Handshake: Method-level capabilities (
tasks.requests.tools.call) combined with per-toolexecution.taskSupportrequired clients to prime cache withtools/listbefore issuing task requests, preventing isomorphic request handling. - The
tasks/resultBlocking Trap: Forcing clients to open a long-lived blocking stream ontasks/resultto receive out-of-band events conflicts with SEP-2260 (prohibiting unsolicited server-to-client requests) and modern streamable transports. - Undefinable Scoping under SEP-2567: With sessions removed from the core specification, server-wide
tasks/listcannot be safely scoped or authorized without bespoke perimeter isolation, risking data leakage across unrelated callers. - No Client-Hosted Tasks: Under SEP-2260, client-hosted sampling/elicitation tasks would require unsolicited polling from server to client, making client-hosted tasks inexpressible.
SEP-2663 formally moves Tasks into an official MCP Extension under the SEP-2133 Extensions Framework (io.modelcontextprotocol/tasks), streamlining the polling lifecycle to tasks/get, tasks/update, and tasks/cancel, while directly embedding final results in the Task object.
This proposal outlines the Kotlin Multiplatform architecture to migrate kotlin-sdk from legacy experimental tasks to the SEP-2663 Tasks Extension across kotlin-sdk-core, kotlin-sdk-server, and kotlin-sdk-client.
Proposed Architecture & Component Design
1. Extensions Framework Registration (kotlin-sdk-core)
Under the SEP-2133 Extensions Framework:
- Define the official extension identifier:
public object TasksExtension { public const val EXTENSION_ID: String = "io.modelcontextprotocol/tasks" public const val EXTENSION_VERSION: String = "1.0.0" } - Client and server capability declarations register support via
extensions:// In ClientCapabilities / ServerCapabilities val extensions: Map<String, JsonElement>? = mapOf( TasksExtension.EXTENSION_ID to buildJsonObject { put("version", TasksExtension.EXTENSION_VERSION) } ) - Server-directed task creation: When the client advertises
TasksExtension.EXTENSION_ID, the server may return a task handle for any tool execution without requiring per-tool flags.
2. Wire Data Models & Discriminators (kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tasks.kt)
Migrate and expand tasks.kt:
- Task Status Enumeration:
@Serializable public enum class TaskStatus { @SerialName("working") WORKING, @SerialName("input_required") INPUT_REQUIRED, @SerialName("completed") COMPLETED, @SerialName("failed") FAILED, @SerialName("cancelled") CANCELLED } - Redesigned Task Entity:
Final payloads and errors are carried directly within the task representation, eliminating the separatetasks/resultstream:@Serializable public data class Task( val taskId: String, val status: TaskStatus, val statusMessage: String? = null, val createdAt: String, val lastUpdatedAt: String, val ttl: Long? = null, val pollInterval: Long? = null, val result: JsonElement? = null, val error: JsonRpcError? = null, val inputRequests: List<ServerRequest>? = null, @SerialName("_meta") override val meta: JsonObject? = null, ) : WithMeta - Polymorphic Tool Call Response:
tools/callresults can return either standard content or a task handle (resultType: "task"):@Serializable public sealed interface CallToolResponse : ClientResult { public val meta: JsonObject? } @Serializable @SerialName("call") public data class CallToolResult( val content: List<ContentBlock> = emptyList(), val isError: Boolean = false, @SerialName("_meta") override val meta: JsonObject? = null, ) : CallToolResponse @Serializable @SerialName("task") public data class CallToolTaskResult( val task: Task, @SerialName("_meta") override val meta: JsonObject? = null, ) : CallToolResponse - Lifecycle Methods:
tasks/get: RetainGetTaskRequest/GetTaskResultfor status polling.tasks/update: AddUpdateTaskRequest(taskId, inputResponses)allowing clients to respond to multi round-trip requests (SEP-2322) when task status isinput_required.tasks/cancel: RetainCancelTaskRequest/CancelTaskResult.- Deprecation / Removal:
- Mark legacy
GetTaskPayloadRequest(tasks/result) as@Deprecated(message = "Removed in SEP-2663; result is now embedded in Task", level = DeprecationLevel.WARNING). - Mark legacy
ListTasksRequest(tasks/list) as@Deprecated(message = "Removed in SEP-2663 per SEP-2567 sessionless scoping", level = DeprecationLevel.WARNING).
- Mark legacy
3. Server Task Management & Execution (kotlin-sdk-server)
- Task Runner & Storage SPI:
Define a thread-safe task store interface with coroutine job lifecycle management:public interface TaskStore { public suspend fun get(taskId: String): Task? public suspend fun save(task: Task) public suspend fun update(taskId: String, block: (Task) -> Task): Task public suspend fun cancel(taskId: String): Boolean } public class InMemoryTaskStore( private val clock: Clock = Clock.System ) : TaskStore { ... } - Server Handler Integration:
Allow tool handlers to yield execution to a background task:
The context automatically coordinates coroutine cancellation withpublic suspend fun ServerSession.launchTask( pollInterval: Long = 1000L, ttl: Long? = 3600_000L, block: suspend (TaskExecutionContext) -> JsonElement ): CallToolTaskResulttasks/canceland stores intermediate status messages.
4. Client Polling & Ergonomics (kotlin-sdk-client)
- High-Level Polling Utility:
Provide an ergonomic suspend extension onClientSession:public suspend fun ClientSession.awaitTaskCompletion( taskId: String, timeout: Duration = 5.minutes, pollInterval: Duration? = null, onInputRequired: (suspend (Task, List<ServerRequest>) -> List<ClientResult>)? = null ): Task- Automatically respects server's
pollIntervalfromTask.pollIntervalif specified. - Automatically dispatches
tasks/updatewheninput_requiredis observed and a handler is provided. - Times out cleanly with
TimeoutCancellationException.
- Automatically respects server's
5. Conformance & Verification Plan
- Unit Tests (
kotlin-sdk-core):- Serialization / deserialization of polymorphic
CallToolResponse(CallToolResultvsCallToolTaskResult). - Wire validation of
tasks/updateand updatedTaskmodel containingresultandinputRequests.
- Serialization / deserialization of polymorphic
- Functional Tests (
kotlin-sdk-server&kotlin-sdk-client):- End-to-end task execution: client initiates tool call, receives task handle, polls
tasks/getuntilCOMPLETED, and accesses embedded result. - Multi round-trip task update:
working->input_required->tasks/update->completed. - Cancellation test: client issues
tasks/cancel, server cancels underlying Kotlin coroutineJob, task transitions toCANCELLED. - TTL eviction: verified using virtual time.
- End-to-end task execution: client initiates tool call, receives task handle, polls
- Official Conformance Suite:
- Validate against https://github.com/modelcontextprotocol/conformance/pull/262 scenarios for SEP-2663.
Next Steps
Upon review and alignment on this migration design:
- Land core data models (
Task,CallToolResponse,UpdateTaskRequest) inkotlin-sdk-core. - Implement
TaskStoreand server-side task execution runners inkotlin-sdk-server. - Provide high-level
awaitTaskCompletionclient helpers inkotlin-sdk-client. - Validate against the official conformance test suite.
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 kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tasks.kt and the existing capability and task request/result models. Run the proposed core serialization and wire-validation tests first, then implement and verify the core models, server TaskStore and execution flow, client polling helper, functional lifecycle tests, and the SEP-2663 conformance scenarios.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kotlin
- Domain
- api, backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100