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-tool execution.taskSupport required clients to prime cache with tools/list before issuing task requests, preventing isomorphic request handling.
- The
tasks/result Blocking Trap: Forcing clients to open a long-lived blocking stream on tasks/result to 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/list cannot 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 separate tasks/result stream:
@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/call results 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: Retain GetTaskRequest / GetTaskResult for status polling.
tasks/update: Add UpdateTaskRequest(taskId, inputResponses) allowing clients to respond to multi round-trip requests (SEP-2322) when task status is input_required.
tasks/cancel: Retain CancelTaskRequest / 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).
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:
public suspend fun ServerSession.launchTask(
pollInterval: Long = 1000L,
ttl: Long? = 3600_000L,
block: suspend (TaskExecutionContext) -> JsonElement
): CallToolTaskResult
The context automatically coordinates coroutine cancellation with tasks/cancel and stores intermediate status messages.
4. Client Polling & Ergonomics (kotlin-sdk-client)
- High-Level Polling Utility:
Provide an ergonomic suspend extension on ClientSession:
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
pollInterval from Task.pollInterval if specified.
- Automatically dispatches
tasks/update when input_required is observed and a handler is provided.
- Times out cleanly with
TimeoutCancellationException.
5. Conformance & Verification Plan
- Unit Tests (
kotlin-sdk-core):
- Serialization / deserialization of polymorphic
CallToolResponse (CallToolResult vs CallToolTaskResult).
- Wire validation of
tasks/update and updated Task model containing result and inputRequests.
- Functional Tests (
kotlin-sdk-server & kotlin-sdk-client):
- End-to-end task execution: client initiates tool call, receives task handle, polls
tasks/get until COMPLETED, 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 coroutine Job, task transitions to CANCELLED.
- TTL eviction: verified using virtual time.
- Official Conformance Suite:
Next Steps
Upon review and alignment on this migration design:
- Land core data models (
Task, CallToolResponse, UpdateTaskRequest) in kotlin-sdk-core.
- Implement
TaskStore and server-side task execution runners in kotlin-sdk-server.
- Provide high-level
awaitTaskCompletion client helpers in kotlin-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.
Proposal: SEP-2663 Tasks Extension Architecture & Migration Design (#817)
Motivation & Context
Tracking implementation of SEP-2663 in
kotlin-sdkfor the 2026-07-28 MCP specification release (aligned with umbrella issue #842).The experimental tasks mechanism specified in
2025-11-25served as an initial asynchronous execution mechanism for tool calls. Real-world experience surfaced critical structural issues:tasks.requests.tools.call) combined with per-toolexecution.taskSupportrequired clients to prime cache withtools/listbefore issuing task requests, preventing isomorphic request handling.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.tasks/listcannot be safely scoped or authorized without bespoke perimeter isolation, risking data leakage across unrelated callers.SEP-2663 formally moves Tasks into an official MCP Extension under the SEP-2133 Extensions Framework (
io.modelcontextprotocol/tasks), streamlining the polling lifecycle totasks/get,tasks/update, andtasks/cancel, while directly embedding final results in theTaskobject.This proposal outlines the Kotlin Multiplatform architecture to migrate
kotlin-sdkfrom legacy experimental tasks to the SEP-2663 Tasks Extension acrosskotlin-sdk-core,kotlin-sdk-server, andkotlin-sdk-client.Proposed Architecture & Component Design
1. Extensions Framework Registration (
kotlin-sdk-core)Under the SEP-2133 Extensions Framework:
extensions: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:Final payloads and errors are carried directly within the task representation, eliminating the separate
tasks/resultstream:tools/callresults can return either standard content or a task handle (resultType: "task"):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.GetTaskPayloadRequest(tasks/result) as@Deprecated(message = "Removed in SEP-2663; result is now embedded in Task", level = DeprecationLevel.WARNING).ListTasksRequest(tasks/list) as@Deprecated(message = "Removed in SEP-2663 per SEP-2567 sessionless scoping", level = DeprecationLevel.WARNING).3. Server Task Management & Execution (
kotlin-sdk-server)Define a thread-safe task store interface with coroutine job lifecycle management:
Allow tool handlers to yield execution to a background task:
tasks/canceland stores intermediate status messages.4. Client Polling & Ergonomics (
kotlin-sdk-client)Provide an ergonomic suspend extension on
ClientSession:pollIntervalfromTask.pollIntervalif specified.tasks/updatewheninput_requiredis observed and a handler is provided.TimeoutCancellationException.5. Conformance & Verification Plan
kotlin-sdk-core):CallToolResponse(CallToolResultvsCallToolTaskResult).tasks/updateand updatedTaskmodel containingresultandinputRequests.kotlin-sdk-server&kotlin-sdk-client):tasks/getuntilCOMPLETED, and accesses embedded result.working->input_required->tasks/update->completed.tasks/cancel, server cancels underlying Kotlin coroutineJob, task transitions toCANCELLED.Next Steps
Upon review and alignment on this migration design:
Task,CallToolResponse,UpdateTaskRequest) inkotlin-sdk-core.TaskStoreand server-side task execution runners inkotlin-sdk-server.awaitTaskCompletionclient helpers inkotlin-sdk-client.