11package io.modelcontextprotocol.kotlin.sdk.client
22
3- import io.modelcontextprotocol.kotlin.sdk.CallToolResult
43import io.modelcontextprotocol.kotlin.sdk.Implementation
5- import io.modelcontextprotocol.kotlin.sdk.InitializeResult
6- import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
74import io.modelcontextprotocol.kotlin.sdk.JSONRPCRequest
8- import io.modelcontextprotocol.kotlin.sdk.JSONRPCResponse
9- import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
10- import io.modelcontextprotocol.kotlin.sdk.shared.Transport
115import kotlinx.coroutines.test.runTest
126import kotlinx.serialization.json.JsonObject
7+ import kotlinx.serialization.json.boolean
8+ import kotlinx.serialization.json.int
9+ import kotlinx.serialization.json.jsonPrimitive
1310import kotlin.test.BeforeTest
1411import kotlin.test.Test
1512import kotlin.test.assertContains
@@ -60,6 +57,26 @@ class ClientMetaParameterTest {
6057 }
6158
6259 assertTrue(result.isSuccess, " Valid meta keys should not cause exceptions" )
60+ mockTransport.lastJsonRpcRequest()?.let { request ->
61+ val params = request.params as JsonObject
62+ assertTrue(params.containsKey(" _meta" ), " Request should contain _meta field" )
63+ val metaField = params[" _meta" ] as JsonObject
64+
65+ // Verify all meta keys are present
66+ assertEquals(validMeta.size, metaField.size, " All meta keys should be included" )
67+
68+ // Verify specific key-value pairs
69+ assertEquals(" value1" , metaField[" simple-key" ]?.jsonPrimitive?.content)
70+ assertEquals(" 1.0" , metaField[" api.example.com/version" ]?.jsonPrimitive?.content)
71+ assertEquals(" enabled" , metaField[" com.company.app/setting" ]?.jsonPrimitive?.content)
72+ assertEquals(3 , metaField[" retry_count" ]?.jsonPrimitive?.int)
73+ assertEquals(true , metaField[" user.preference" ]?.jsonPrimitive?.boolean)
74+ assertEquals(" alphanumeric" , metaField[" valid123" ]?.jsonPrimitive?.content)
75+ assertEquals(" multiple-dots" , metaField[" multi.dot.name" ]?.jsonPrimitive?.content)
76+ assertEquals(" underscore" , metaField[" under_score" ]?.jsonPrimitive?.content)
77+ assertEquals(" hyphen" , metaField[" hyphen-dash" ]?.jsonPrimitive?.content)
78+ assertEquals(" complex-valid-prefix" , metaField[" org.apache.kafka/consumer-config" ]?.jsonPrimitive?.content)
79+ }
6380 }
6481
6582 @Test
@@ -191,7 +208,7 @@ class ClientMetaParameterTest {
191208
192209 assertTrue(result.isSuccess, " Complex data type conversion should not throw exceptions" )
193210
194- mockTransport.lastJsonRpcRequest?.let { request ->
211+ mockTransport.lastJsonRpcRequest() ?.let { request ->
195212 assertEquals(" tools/call" , request.method)
196213 val params = request.params as JsonObject
197214 assertTrue(params.containsKey(" _meta" ), " Request should contain _meta field" )
@@ -208,7 +225,7 @@ class ClientMetaParameterTest {
208225
209226 assertTrue(result.isSuccess)
210227
211- mockTransport.lastJsonRpcRequest?.let { request ->
228+ mockTransport.lastJsonRpcRequest() ?.let { request ->
212229 val params = request.params as JsonObject
213230 val metaField = params[" _meta" ] as JsonObject
214231 assertTrue(metaField.containsKey(" config" ))
@@ -219,7 +236,7 @@ class ClientMetaParameterTest {
219236 fun `should include empty meta object when meta parameter not provided` () = runTest {
220237 client.callTool(" test-tool" , mapOf (" arg" to " value" ))
221238
222- mockTransport.lastJsonRpcRequest?.let { request ->
239+ mockTransport.lastJsonRpcRequest() ?.let { request ->
223240 val params = request.params as JsonObject
224241 val metaField = params[" _meta" ] as JsonObject
225242 assertTrue(metaField.isEmpty(), " Meta field should be empty when not provided" )
@@ -254,76 +271,4 @@ class ClientMetaParameterTest {
254271 }
255272}
256273
257- class MockTransport : Transport {
258- private val _sentMessages = mutableListOf<JSONRPCMessage >()
259- val sentMessages: List <JSONRPCMessage > = _sentMessages
260-
261- private var onMessageBlock: (suspend (JSONRPCMessage ) -> Unit )? = null
262- private var onCloseBlock: (() -> Unit )? = null
263- private var onErrorBlock: ((Throwable ) -> Unit )? = null
264-
265- override suspend fun start () = Unit
266-
267- override suspend fun send (message : JSONRPCMessage ) {
268- _sentMessages + = message
269-
270- // Auto-respond to initialization and tool calls
271- when (message) {
272- is JSONRPCRequest -> {
273- when (message.method) {
274- " initialize" -> {
275- val initResponse = JSONRPCResponse (
276- id = message.id,
277- result = InitializeResult (
278- protocolVersion = " 2024-11-05" ,
279- capabilities = ServerCapabilities (
280- tools = ServerCapabilities .Tools (listChanged = null ),
281- ),
282- serverInfo = Implementation (" mock-server" , " 1.0.0" ),
283- ),
284- )
285- onMessageBlock?.invoke(initResponse)
286- }
287-
288- " tools/call" -> {
289- val toolResponse = JSONRPCResponse (
290- id = message.id,
291- result = CallToolResult (
292- content = listOf (),
293- isError = false ,
294- ),
295- )
296- onMessageBlock?.invoke(toolResponse)
297- }
298- }
299- }
300-
301- else -> {
302- // Handle other message types if needed
303- }
304- }
305- }
306-
307- override suspend fun close () {
308- onCloseBlock?.invoke()
309- }
310-
311- override fun onMessage (block : suspend (JSONRPCMessage ) -> Unit ) {
312- onMessageBlock = block
313- }
314-
315- override fun onClose (block : () -> Unit ) {
316- onCloseBlock = block
317- }
318-
319- override fun onError (block : (Throwable ) -> Unit ) {
320- onErrorBlock = block
321- }
322-
323- fun setupInitializationResponse () {
324- // This method helps set up the mock for proper initialization
325- }
326- }
327-
328- val MockTransport .lastJsonRpcRequest: JSONRPCRequest ?
329- get() = sentMessages.lastOrNull() as ? JSONRPCRequest
274+ suspend fun MockTransport.lastJsonRpcRequest (): JSONRPCRequest ? = getSentMessages().lastOrNull() as ? JSONRPCRequest
0 commit comments