@@ -44,6 +44,12 @@ import io.modelcontextprotocol.kotlin.sdk.ToolListChangedNotification
44
44
import io.modelcontextprotocol.kotlin.sdk.shared.Protocol
45
45
import io.modelcontextprotocol.kotlin.sdk.shared.ProtocolOptions
46
46
import io.modelcontextprotocol.kotlin.sdk.shared.RequestOptions
47
+ import kotlinx.atomicfu.atomic
48
+ import kotlinx.atomicfu.getAndUpdate
49
+ import kotlinx.atomicfu.update
50
+ import kotlinx.collections.immutable.minus
51
+ import kotlinx.collections.immutable.persistentMapOf
52
+ import kotlinx.collections.immutable.toPersistentSet
47
53
import kotlinx.coroutines.CompletableDeferred
48
54
import kotlinx.serialization.json.JsonObject
49
55
@@ -91,9 +97,15 @@ public open class Server(
91
97
92
98
private val capabilities: ServerCapabilities = options.capabilities
93
99
94
- private val tools = mutableMapOf<String , RegisteredTool >()
95
- private val prompts = mutableMapOf<String , RegisteredPrompt >()
96
- private val resources = mutableMapOf<String , RegisteredResource >()
100
+ private val _tools = atomic(persistentMapOf<String , RegisteredTool >())
101
+ private val _prompts = atomic(persistentMapOf<String , RegisteredPrompt >())
102
+ private val _resources = atomic(persistentMapOf<String , RegisteredResource >())
103
+ public val tools: Map <String , RegisteredTool >
104
+ get() = _tools .value
105
+ public val prompts: Map <String , RegisteredPrompt >
106
+ get() = _prompts .value
107
+ public val resources: Map <String , RegisteredResource >
108
+ get() = _resources .value
97
109
98
110
init {
99
111
logger.debug { " Initializing MCP server with capabilities: $capabilities " }
@@ -192,7 +204,9 @@ public open class Server(
192
204
throw IllegalStateException (" Server does not support tools capability. Enable it in ServerOptions." )
193
205
}
194
206
logger.info { " Registering tool: $name " }
195
- tools[name] = RegisteredTool (Tool (name, description, inputSchema, toolAnnotations), handler)
207
+ _tools .update { current ->
208
+ current.put(name, RegisteredTool (Tool (name, description, inputSchema, toolAnnotations), handler))
209
+ }
196
210
}
197
211
198
212
/* *
@@ -207,10 +221,7 @@ public open class Server(
207
221
throw IllegalStateException (" Server does not support tools capability." )
208
222
}
209
223
logger.info { " Registering ${toolsToAdd.size} tools" }
210
- for (rt in toolsToAdd) {
211
- logger.debug { " Registering tool: ${rt.tool.name} " }
212
- tools[rt.tool.name] = rt
213
- }
224
+ _tools .update { current -> current.putAll(toolsToAdd.associateBy { it.tool.name }) }
214
225
}
215
226
216
227
/* *
@@ -226,7 +237,10 @@ public open class Server(
226
237
throw IllegalStateException (" Server does not support tools capability." )
227
238
}
228
239
logger.info { " Removing tool: $name " }
229
- val removed = tools.remove(name) != null
240
+
241
+ val oldMap = _tools .getAndUpdate { current -> current.remove(name) }
242
+
243
+ val removed = name in oldMap
230
244
logger.debug {
231
245
if (removed) {
232
246
" Tool removed: $name "
@@ -250,18 +264,15 @@ public open class Server(
250
264
throw IllegalStateException (" Server does not support tools capability." )
251
265
}
252
266
logger.info { " Removing ${toolNames.size} tools" }
253
- var removedCount = 0
254
- for (name in toolNames) {
255
- logger.debug { " Removing tool: $name " }
256
- if (tools.remove(name) != null ) {
257
- removedCount++
258
- }
259
- }
267
+
268
+ val oldMap = _tools .getAndUpdate { current -> current - toolNames.toPersistentSet() }
269
+
270
+ val removedCount = toolNames.count { it in oldMap }
260
271
logger.info {
261
272
if (removedCount > 0 ) {
262
- " Removed $removedCount tools"
273
+ " Removed $removedCount tools"
263
274
} else {
264
- " No tools were removed"
275
+ " No tools were removed"
265
276
}
266
277
}
267
278
return removedCount
@@ -280,7 +291,7 @@ public open class Server(
280
291
throw IllegalStateException (" Server does not support prompts capability." )
281
292
}
282
293
logger.info { " Registering prompt: ${prompt.name} " }
283
- prompts[ prompt.name] = RegisteredPrompt (prompt, promptProvider)
294
+ _prompts .update { current -> current.put( prompt.name, RegisteredPrompt (prompt, promptProvider)) }
284
295
}
285
296
286
297
/* *
@@ -314,10 +325,7 @@ public open class Server(
314
325
throw IllegalStateException (" Server does not support prompts capability." )
315
326
}
316
327
logger.info { " Registering ${promptsToAdd.size} prompts" }
317
- for (rp in promptsToAdd) {
318
- logger.debug { " Registering prompt: ${rp.prompt.name} " }
319
- prompts[rp.prompt.name] = rp
320
- }
328
+ _prompts .update { current -> current.putAll(promptsToAdd.associateBy { it.prompt.name }) }
321
329
}
322
330
323
331
/* *
@@ -333,7 +341,10 @@ public open class Server(
333
341
throw IllegalStateException (" Server does not support prompts capability." )
334
342
}
335
343
logger.info { " Removing prompt: $name " }
336
- val removed = prompts.remove(name) != null
344
+
345
+ val oldMap = _prompts .getAndUpdate { current -> current.remove(name) }
346
+
347
+ val removed = name in oldMap
337
348
logger.debug {
338
349
if (removed) {
339
350
" Prompt removed: $name "
@@ -357,13 +368,11 @@ public open class Server(
357
368
throw IllegalStateException (" Server does not support prompts capability." )
358
369
}
359
370
logger.info { " Removing ${promptNames.size} prompts" }
360
- var removedCount = 0
361
- for (name in promptNames) {
362
- logger.debug { " Removing prompt: $name " }
363
- if (prompts.remove(name) != null ) {
364
- removedCount++
365
- }
366
- }
371
+
372
+ val oldMap = _prompts .getAndUpdate { current -> current - promptNames.toPersistentSet() }
373
+
374
+ val removedCount = promptNames.count { it in oldMap }
375
+
367
376
logger.info {
368
377
if (removedCount > 0 ) {
369
378
" Removed $removedCount prompts"
@@ -396,7 +405,12 @@ public open class Server(
396
405
throw IllegalStateException (" Server does not support resources capability." )
397
406
}
398
407
logger.info { " Registering resource: $name ($uri )" }
399
- resources[uri] = RegisteredResource (Resource (uri, name, description, mimeType), readHandler)
408
+ _resources .update { current ->
409
+ current.put(
410
+ uri,
411
+ RegisteredResource (Resource (uri, name, description, mimeType), readHandler)
412
+ )
413
+ }
400
414
}
401
415
402
416
/* *
@@ -411,10 +425,7 @@ public open class Server(
411
425
throw IllegalStateException (" Server does not support resources capability." )
412
426
}
413
427
logger.info { " Registering ${resourcesToAdd.size} resources" }
414
- for (r in resourcesToAdd) {
415
- logger.debug { " Registering resource: ${r.resource.name} (${r.resource.uri} )" }
416
- resources[r.resource.uri] = r
417
- }
428
+ _resources .update { current -> current.putAll(resourcesToAdd.associateBy { it.resource.uri }) }
418
429
}
419
430
420
431
/* *
@@ -430,7 +441,10 @@ public open class Server(
430
441
throw IllegalStateException (" Server does not support resources capability." )
431
442
}
432
443
logger.info { " Removing resource: $uri " }
433
- val removed = resources.remove(uri) != null
444
+
445
+ val oldMap = _resources .getAndUpdate { current -> current.remove(uri) }
446
+
447
+ val removed = uri in oldMap
434
448
logger.debug {
435
449
if (removed) {
436
450
" Resource removed: $uri "
@@ -454,13 +468,11 @@ public open class Server(
454
468
throw IllegalStateException (" Server does not support resources capability." )
455
469
}
456
470
logger.info { " Removing ${uris.size} resources" }
457
- var removedCount = 0
458
- for (uri in uris) {
459
- logger.debug { " Removing resource: $uri " }
460
- if (resources.remove(uri) != null ) {
461
- removedCount++
462
- }
463
- }
471
+
472
+ val oldMap = _resources .getAndUpdate { current -> current - uris.toPersistentSet() }
473
+
474
+ val removedCount = uris.count { it in oldMap }
475
+
464
476
logger.info {
465
477
if (removedCount > 0 ) {
466
478
" Removed $removedCount resources"
@@ -586,7 +598,7 @@ public open class Server(
586
598
587
599
private suspend fun handleCallTool (request : CallToolRequest ): CallToolResult {
588
600
logger.debug { " Handling tool call request for tool: ${request.name} " }
589
- val tool = tools [request.name]
601
+ val tool = _tools .value [request.name]
590
602
? : run {
591
603
logger.error { " Tool not found: ${request.name} " }
592
604
throw IllegalArgumentException (" Tool not found: ${request.name} " )
0 commit comments