Skip to content

Commit bcae5fb

Browse files
committed
Add overloaded constructors for JSONRPCRequest and refactor tests
- Updated `JSONRPCRequest` with new string and numeric ID constructors. - Replaced atomic ID handling with UUID defaults. - Enhanced test coverage for JSONRPCRequest creation scenarios.
1 parent 9079b2a commit bcae5fb

File tree

4 files changed

+73
-14
lines changed

4 files changed

+73
-14
lines changed

kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTransportTest.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
1717
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCNotification
1818
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
1919
import io.modelcontextprotocol.kotlin.sdk.types.McpJson
20-
import io.modelcontextprotocol.kotlin.sdk.types.RequestId
21-
import io.modelcontextprotocol.kotlin.sdk.Implementation
22-
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
23-
import io.modelcontextprotocol.kotlin.sdk.JSONRPCNotification
24-
import io.modelcontextprotocol.kotlin.sdk.JSONRPCRequest
25-
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
2620
import kotlinx.coroutines.Dispatchers
2721
import kotlinx.coroutines.delay
2822
import kotlinx.coroutines.test.runTest

kotlin-sdk-core/api/kotlin-sdk-core.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,8 +1980,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/JSONRPCNotification$
19801980

19811981
public final class io/modelcontextprotocol/kotlin/sdk/types/JSONRPCRequest : io/modelcontextprotocol/kotlin/sdk/types/JSONRPCMessage {
19821982
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/JSONRPCRequest$Companion;
1983+
public fun <init> (JLjava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
1984+
public synthetic fun <init> (JLjava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1985+
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Ljava/lang/String;)V
19831986
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
19841987
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1988+
public fun <init> (Ljava/lang/String;)V
1989+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
1990+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
19851991
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;
19861992
public final fun component2 ()Ljava/lang/String;
19871993
public final fun component3 ()Lkotlinx/serialization/json/JsonElement;

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/jsonRpc.kt

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ import kotlinx.serialization.Serializable
88
import kotlinx.serialization.json.JsonElement
99
import kotlinx.serialization.json.decodeFromJsonElement
1010
import kotlinx.serialization.json.encodeToJsonElement
11-
import kotlin.concurrent.atomics.AtomicLong
1211
import kotlin.concurrent.atomics.ExperimentalAtomicApi
13-
import kotlin.concurrent.atomics.incrementAndFetch
1412
import kotlin.jvm.JvmInline
13+
import kotlin.jvm.JvmOverloads
14+
import kotlin.uuid.ExperimentalUuidApi
15+
import kotlin.uuid.Uuid
1516

1617
public const val JSONRPC_VERSION: String = "2.0"
1718

18-
private val REQUEST_MESSAGE_ID: AtomicLong = AtomicLong(0L)
19-
2019
public fun RequestId(value: String): RequestId = RequestId.StringId(value)
2120

2221
public fun RequestId(value: Long): RequestId = RequestId.NumberId(value)
@@ -94,23 +93,50 @@ public sealed interface JSONRPCMessage {
9493
* A request that expects a response.
9594
*
9695
* Requests are identified by a unique [id] and specify a [method] to invoke.
97-
* The server or client (depending on direction) must respond with either a
96+
* The server or client (depending on a direction) must respond with either a
9897
* [JSONRPCResponse] or [JSONRPCError] that has the same [id].
9998
*
10099
* @property jsonrpc Always "2.0" to indicate JSON-RPC 2.0 protocol.
101100
* @property id A unique identifier for this request. The response will include the same ID.
102-
* Can be a string or number.
101+
* Can be a string or number. UUID string is used by default.
103102
* @property method The name of the method to invoke (e.g., "tools/list", "resources/read").
104103
* @property params Optional parameters for the method. Structure depends on the specific method.
105104
*/
106105
@Serializable
107-
public data class JSONRPCRequest(
108-
val id: RequestId = RequestId(REQUEST_MESSAGE_ID.incrementAndFetch()),
106+
@OptIn(ExperimentalUuidApi::class)
107+
public data class JSONRPCRequest @JvmOverloads public constructor(
108+
val id: RequestId = RequestId(Uuid.random().toHexString()),
109109
val method: String,
110110
val params: JsonElement? = null,
111111
) : JSONRPCMessage {
112112
@EncodeDefault
113113
override val jsonrpc: String = JSONRPC_VERSION
114+
115+
/**
116+
* Secondary constructor for creating a `JSONRPCRequest` using a string-based ID.
117+
*
118+
* @param id The string ID for the request.
119+
* @param method The method name for the request.
120+
* @param params The parameters for the request as a JSON element. Defaults to `null`.
121+
*/
122+
public constructor(
123+
id: String,
124+
method: String,
125+
params: JsonElement? = null,
126+
) : this(id = RequestId.StringId(id), method = method, params = params)
127+
128+
/**
129+
* Constructs a JSON-RPC request using a numerical ID, method name, and optional parameters.
130+
*
131+
* @param id The numerical ID for the request.
132+
* @param method The method name for the request.
133+
* @param params The parameters for the request as a JSON element. Defaults to `null`.
134+
*/
135+
public constructor(
136+
id: Long,
137+
method: String,
138+
params: JsonElement? = null,
139+
) : this(id = RequestId.NumberId(id), method = method, params = params)
114140
}
115141

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

209235
// Standard JSON-RPC 2.0 error codes
236+
210237
/** Invalid JSON was received */
211238
public const val PARSE_ERROR: Int = -32700
212239

kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/JsonRpcTest.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.modelcontextprotocol.kotlin.sdk.types
22

33
import io.kotest.assertions.json.shouldEqualJson
4+
import io.kotest.matchers.shouldBe
5+
import io.kotest.matchers.types.shouldBeSameInstanceAs
46
import kotlinx.serialization.json.boolean
57
import kotlinx.serialization.json.buildJsonObject
68
import kotlinx.serialization.json.int
@@ -398,4 +400,34 @@ class JsonRpcTest {
398400
}
399401
""".trimIndent()
400402
}
403+
404+
@Test
405+
fun `should create JSONRPCRequest with string ID`() {
406+
val params = buildJsonObject {
407+
put("foo", "bar")
408+
}
409+
val request = JSONRPCRequest(
410+
id = "req-42",
411+
method = "notifications/log",
412+
params = params,
413+
)
414+
request.id shouldBe RequestId("req-42")
415+
request.method shouldBe "notifications/log"
416+
request.params shouldBeSameInstanceAs params
417+
}
418+
419+
@Test
420+
fun `should create JSONRPCRequest with numeric ID`() {
421+
val params = buildJsonObject {
422+
put("foo", "bar")
423+
}
424+
val request = JSONRPCRequest(
425+
id = 42,
426+
method = "notifications/log",
427+
params = params,
428+
)
429+
request.id shouldBe RequestId(42)
430+
request.method shouldBe "notifications/log"
431+
request.params shouldBeSameInstanceAs params
432+
}
401433
}

0 commit comments

Comments
 (0)