@@ -44,6 +44,12 @@ import io.modelcontextprotocol.kotlin.sdk.ToolListChangedNotification
4444import io.modelcontextprotocol.kotlin.sdk.shared.Protocol
4545import io.modelcontextprotocol.kotlin.sdk.shared.ProtocolOptions
4646import 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
4753import kotlinx.coroutines.CompletableDeferred
4854import kotlinx.serialization.json.JsonObject
4955
@@ -91,9 +97,15 @@ public open class Server(
9197
9298 private val capabilities: ServerCapabilities = options.capabilities
9399
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
97109
98110 init {
99111 logger.debug { " Initializing MCP server with capabilities: $capabilities " }
@@ -192,7 +204,9 @@ public open class Server(
192204 throw IllegalStateException (" Server does not support tools capability. Enable it in ServerOptions." )
193205 }
194206 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+ }
196210 }
197211
198212 /* *
@@ -207,10 +221,7 @@ public open class Server(
207221 throw IllegalStateException (" Server does not support tools capability." )
208222 }
209223 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 }) }
214225 }
215226
216227 /* *
@@ -226,7 +237,10 @@ public open class Server(
226237 throw IllegalStateException (" Server does not support tools capability." )
227238 }
228239 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
230244 logger.debug {
231245 if (removed) {
232246 " Tool removed: $name "
@@ -250,18 +264,15 @@ public open class Server(
250264 throw IllegalStateException (" Server does not support tools capability." )
251265 }
252266 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 }
260271 logger.info {
261272 if (removedCount > 0 ) {
262- " Removed $removedCount tools"
273+ " Removed $removedCount tools"
263274 } else {
264- " No tools were removed"
275+ " No tools were removed"
265276 }
266277 }
267278 return removedCount
@@ -280,7 +291,7 @@ public open class Server(
280291 throw IllegalStateException (" Server does not support prompts capability." )
281292 }
282293 logger.info { " Registering prompt: ${prompt.name} " }
283- prompts[ prompt.name] = RegisteredPrompt (prompt, promptProvider)
294+ _prompts .update { current -> current.put( prompt.name, RegisteredPrompt (prompt, promptProvider)) }
284295 }
285296
286297 /* *
@@ -314,10 +325,7 @@ public open class Server(
314325 throw IllegalStateException (" Server does not support prompts capability." )
315326 }
316327 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 }) }
321329 }
322330
323331 /* *
@@ -333,7 +341,10 @@ public open class Server(
333341 throw IllegalStateException (" Server does not support prompts capability." )
334342 }
335343 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
337348 logger.debug {
338349 if (removed) {
339350 " Prompt removed: $name "
@@ -357,13 +368,11 @@ public open class Server(
357368 throw IllegalStateException (" Server does not support prompts capability." )
358369 }
359370 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+
367376 logger.info {
368377 if (removedCount > 0 ) {
369378 " Removed $removedCount prompts"
@@ -396,7 +405,12 @@ public open class Server(
396405 throw IllegalStateException (" Server does not support resources capability." )
397406 }
398407 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+ }
400414 }
401415
402416 /* *
@@ -411,10 +425,7 @@ public open class Server(
411425 throw IllegalStateException (" Server does not support resources capability." )
412426 }
413427 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 }) }
418429 }
419430
420431 /* *
@@ -430,7 +441,10 @@ public open class Server(
430441 throw IllegalStateException (" Server does not support resources capability." )
431442 }
432443 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
434448 logger.debug {
435449 if (removed) {
436450 " Resource removed: $uri "
@@ -454,13 +468,11 @@ public open class Server(
454468 throw IllegalStateException (" Server does not support resources capability." )
455469 }
456470 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+
464476 logger.info {
465477 if (removedCount > 0 ) {
466478 " Removed $removedCount resources"
@@ -586,7 +598,7 @@ public open class Server(
586598
587599 private suspend fun handleCallTool (request : CallToolRequest ): CallToolResult {
588600 logger.debug { " Handling tool call request for tool: ${request.name} " }
589- val tool = tools [request.name]
601+ val tool = _tools .value [request.name]
590602 ? : run {
591603 logger.error { " Tool not found: ${request.name} " }
592604 throw IllegalArgumentException (" Tool not found: ${request.name} " )
0 commit comments