@@ -44,6 +44,10 @@ 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.persistentMapOf
47
51
import kotlinx.coroutines.CompletableDeferred
48
52
import kotlinx.serialization.json.JsonObject
49
53
@@ -91,9 +95,15 @@ public open class Server(
91
95
92
96
private val capabilities: ServerCapabilities = options.capabilities
93
97
94
- private val tools = mutableMapOf<String , RegisteredTool >()
95
- private val prompts = mutableMapOf<String , RegisteredPrompt >()
96
- private val resources = mutableMapOf<String , RegisteredResource >()
98
+ private val _tools = atomic(persistentMapOf<String , RegisteredTool >())
99
+ private val _prompts = atomic(persistentMapOf<String , RegisteredPrompt >())
100
+ private val _resources = atomic(persistentMapOf<String , RegisteredResource >())
101
+ private val tools: Map <String , RegisteredTool >
102
+ get() = _tools .value
103
+ private val prompts: Map <String , RegisteredPrompt >
104
+ get() = _prompts .value
105
+ private val resources: Map <String , RegisteredResource >
106
+ get() = _resources .value
97
107
98
108
init {
99
109
logger.debug { " Initializing MCP server with capabilities: $capabilities " }
@@ -192,7 +202,9 @@ public open class Server(
192
202
throw IllegalStateException (" Server does not support tools capability. Enable it in ServerOptions." )
193
203
}
194
204
logger.info { " Registering tool: $name " }
195
- tools[name] = RegisteredTool (Tool (name, description, inputSchema, toolAnnotations), handler)
205
+ _tools .update { current ->
206
+ current.put(name, RegisteredTool (Tool (name, description, inputSchema, toolAnnotations), handler))
207
+ }
196
208
}
197
209
198
210
/* *
@@ -207,10 +219,7 @@ public open class Server(
207
219
throw IllegalStateException (" Server does not support tools capability." )
208
220
}
209
221
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
- }
222
+ _tools .update { current -> current.putAll(toolsToAdd.associateBy { it.tool.name }) }
214
223
}
215
224
216
225
/* *
@@ -226,7 +235,10 @@ public open class Server(
226
235
throw IllegalStateException (" Server does not support tools capability." )
227
236
}
228
237
logger.info { " Removing tool: $name " }
229
- val removed = tools.remove(name) != null
238
+
239
+ val oldMap = _tools .getAndUpdate { current -> current.remove(name) }
240
+
241
+ val removed = name in oldMap
230
242
logger.debug {
231
243
if (removed) {
232
244
" Tool removed: $name "
@@ -250,18 +262,20 @@ public open class Server(
250
262
throw IllegalStateException (" Server does not support tools capability." )
251
263
}
252
264
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 ++
265
+
266
+ val oldMap = _tools .getAndUpdate { current ->
267
+ toolNames.fold(current) { map, name ->
268
+ logger.debug { " Removing tool: $name " }
269
+ map.remove(name)
258
270
}
259
271
}
272
+
273
+ val removedCount = toolNames.count { it in oldMap }
260
274
logger.info {
261
275
if (removedCount > 0 ) {
262
- " Removed $removedCount tools"
276
+ " Removed $removedCount tools"
263
277
} else {
264
- " No tools were removed"
278
+ " No tools were removed"
265
279
}
266
280
}
267
281
return removedCount
@@ -280,7 +294,7 @@ public open class Server(
280
294
throw IllegalStateException (" Server does not support prompts capability." )
281
295
}
282
296
logger.info { " Registering prompt: ${prompt.name} " }
283
- prompts[ prompt.name] = RegisteredPrompt (prompt, promptProvider)
297
+ _prompts .update { current -> current.put( prompt.name, RegisteredPrompt (prompt, promptProvider)) }
284
298
}
285
299
286
300
/* *
@@ -314,10 +328,7 @@ public open class Server(
314
328
throw IllegalStateException (" Server does not support prompts capability." )
315
329
}
316
330
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
- }
331
+ _prompts .update { current -> current.putAll(promptsToAdd.associateBy { it.prompt.name }) }
321
332
}
322
333
323
334
/* *
@@ -333,7 +344,10 @@ public open class Server(
333
344
throw IllegalStateException (" Server does not support prompts capability." )
334
345
}
335
346
logger.info { " Removing prompt: $name " }
336
- val removed = prompts.remove(name) != null
347
+
348
+ val oldMap = _prompts .getAndUpdate { current -> current.remove(name) }
349
+
350
+ val removed = name in oldMap
337
351
logger.debug {
338
352
if (removed) {
339
353
" Prompt removed: $name "
@@ -357,13 +371,16 @@ public open class Server(
357
371
throw IllegalStateException (" Server does not support prompts capability." )
358
372
}
359
373
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 ++
374
+
375
+ val oldMap = _prompts .getAndUpdate { current ->
376
+ promptNames.fold(current) { map, name ->
377
+ logger.debug { " Removing prompt: $name " }
378
+ map.remove(name)
365
379
}
366
380
}
381
+
382
+ val removedCount = promptNames.count { it in oldMap }
383
+
367
384
logger.info {
368
385
if (removedCount > 0 ) {
369
386
" Removed $removedCount prompts"
@@ -396,7 +413,12 @@ public open class Server(
396
413
throw IllegalStateException (" Server does not support resources capability." )
397
414
}
398
415
logger.info { " Registering resource: $name ($uri )" }
399
- resources[uri] = RegisteredResource (Resource (uri, name, description, mimeType), readHandler)
416
+ _resources .update { current ->
417
+ current.put(
418
+ uri,
419
+ RegisteredResource (Resource (uri, name, description, mimeType), readHandler)
420
+ )
421
+ }
400
422
}
401
423
402
424
/* *
@@ -411,10 +433,7 @@ public open class Server(
411
433
throw IllegalStateException (" Server does not support resources capability." )
412
434
}
413
435
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
- }
436
+ _resources .update { current -> current.putAll(resourcesToAdd.associateBy { it.resource.uri }) }
418
437
}
419
438
420
439
/* *
@@ -430,7 +449,10 @@ public open class Server(
430
449
throw IllegalStateException (" Server does not support resources capability." )
431
450
}
432
451
logger.info { " Removing resource: $uri " }
433
- val removed = resources.remove(uri) != null
452
+
453
+ val oldMap = _resources .getAndUpdate { current -> current.remove(uri) }
454
+
455
+ val removed = uri in oldMap
434
456
logger.debug {
435
457
if (removed) {
436
458
" Resource removed: $uri "
@@ -454,13 +476,16 @@ public open class Server(
454
476
throw IllegalStateException (" Server does not support resources capability." )
455
477
}
456
478
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 ++
479
+
480
+ val oldMap = _resources .getAndUpdate { current ->
481
+ uris.fold(current) { map, uri ->
482
+ logger.debug { " Removing resource: $uri " }
483
+ map.remove(uri)
462
484
}
463
485
}
486
+
487
+ val removedCount = uris.count { it in oldMap }
488
+
464
489
logger.info {
465
490
if (removedCount > 0 ) {
466
491
" Removed $removedCount resources"
0 commit comments