Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCNotification
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
import io.modelcontextprotocol.kotlin.sdk.types.McpJson
import io.modelcontextprotocol.kotlin.sdk.types.RequestId
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
Expand Down Expand Up @@ -50,7 +49,7 @@ class StreamableHttpClientTransportTest {
@Test
fun testSendJsonRpcMessage() = runTest {
val message = JSONRPCRequest(
id = RequestId.StringId("test-id"),
id = "test-id",
method = "test",
params = buildJsonObject { },
)
Expand Down Expand Up @@ -78,7 +77,7 @@ class StreamableHttpClientTransportTest {
@Test
fun testStoreSessionId() = runTest {
val initMessage = JSONRPCRequest(
id = RequestId.StringId("test-id"),
id = "test-id",
method = "initialize",
params = buildJsonObject {
put(
Expand Down
6 changes: 6 additions & 0 deletions kotlin-sdk-core/api/kotlin-sdk-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -2118,8 +2118,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/JSONRPCNotification$

public final class io/modelcontextprotocol/kotlin/sdk/types/JSONRPCRequest : io/modelcontextprotocol/kotlin/sdk/types/JSONRPCMessage {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/JSONRPCRequest$Companion;
public fun <init> (JLjava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
public synthetic fun <init> (JLjava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Ljava/lang/String;)V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lkotlinx/serialization/json/JsonElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlin.concurrent.atomics.AtomicLong
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.concurrent.atomics.incrementAndFetch
import kotlin.jvm.JvmInline
import kotlin.jvm.JvmOverloads
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

public const val JSONRPC_VERSION: String = "2.0"

private val REQUEST_MESSAGE_ID: AtomicLong = AtomicLong(0L)

public fun RequestId(value: String): RequestId = RequestId.StringId(value)

public fun RequestId(value: Long): RequestId = RequestId.NumberId(value)
Expand Down Expand Up @@ -94,23 +93,50 @@ public sealed interface JSONRPCMessage {
* A request that expects a response.
*
* Requests are identified by a unique [id] and specify a [method] to invoke.
* The server or client (depending on direction) must respond with either a
* The server or client (depending on a direction) must respond with either a
* [JSONRPCResponse] or [JSONRPCError] that has the same [id].
*
* @property jsonrpc Always "2.0" to indicate JSON-RPC 2.0 protocol.
* @property id A unique identifier for this request. The response will include the same ID.
* Can be a string or number.
* Can be a string or number. UUID string is used by default.
* @property method The name of the method to invoke (e.g., "tools/list", "resources/read").
* @property params Optional parameters for the method. Structure depends on the specific method.
*/
@Serializable
public data class JSONRPCRequest(
val id: RequestId = RequestId(REQUEST_MESSAGE_ID.incrementAndFetch()),
@OptIn(ExperimentalUuidApi::class)
public data class JSONRPCRequest @JvmOverloads public constructor(
val id: RequestId = RequestId(Uuid.random().toHexString()),
val method: String,
val params: JsonElement? = null,
) : JSONRPCMessage {
@EncodeDefault
override val jsonrpc: String = JSONRPC_VERSION

/**
* Secondary constructor for creating a `JSONRPCRequest` using a string-based ID.
*
* @param id The string ID for the request.
* @param method The method name for the request.
* @param params The parameters for the request as a JSON element. Defaults to `null`.
*/
public constructor(
id: String,
method: String,
params: JsonElement? = null,
) : this(id = RequestId.StringId(id), method = method, params = params)

/**
* Constructs a JSON-RPC request using a numerical ID, method name, and optional parameters.
*
* @param id The numerical ID for the request.
* @param method The method name for the request.
* @param params The parameters for the request as a JSON element. Defaults to `null`.
*/
public constructor(
id: Long,
method: String,
params: JsonElement? = null,
) : this(id = RequestId.NumberId(id), method = method, params = params)
}

// ============================================================================
Expand Down Expand Up @@ -207,6 +233,7 @@ public data class RPCError(val code: Int, val message: String, val data: JsonEle
public const val REQUEST_TIMEOUT: Int = -32001

// Standard JSON-RPC 2.0 error codes

/** Invalid JSON was received */
public const val PARSE_ERROR: Int = -32700

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.modelcontextprotocol.kotlin.sdk.types

import io.kotest.assertions.json.shouldEqualJson
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeSameInstanceAs
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
Expand Down Expand Up @@ -398,4 +400,34 @@ class JsonRpcTest {
}
""".trimIndent()
}

@Test
fun `should create JSONRPCRequest with string ID`() {
val params = buildJsonObject {
put("foo", "bar")
}
val request = JSONRPCRequest(
id = "req-42",
method = "notifications/log",
params = params,
)
request.id shouldBe RequestId("req-42")
request.method shouldBe "notifications/log"
request.params shouldBeSameInstanceAs params
}

@Test
fun `should create JSONRPCRequest with numeric ID`() {
val params = buildJsonObject {
put("foo", "bar")
}
val request = JSONRPCRequest(
id = 42,
method = "notifications/log",
params = params,
)
request.id shouldBe RequestId(42)
request.method shouldBe "notifications/log"
request.params shouldBeSameInstanceAs params
}
}
Loading