diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/FeatureNotificationService.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/FeatureNotificationService.kt index 397e6e19..d996b2db 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/FeatureNotificationService.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/FeatureNotificationService.kt @@ -348,7 +348,7 @@ internal class FeatureNotificationService( job.start() } - /** Returns the current timestamp in milliseconds. */ + /** Returns the current timestamp in milliseconds since Unix epoch. */ @OptIn(ExperimentalTime::class) private fun getCurrentTimestamp(): Long = clock.now().toEpochMilliseconds() diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsNotificationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsNotificationTest.kt index e2cfe294..3674401a 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsNotificationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsNotificationTest.kt @@ -23,9 +23,9 @@ class ServerPromptsNotificationTest : AbstractServerFeaturesTest() { @Test fun `addPrompt should send notification`() = runTest { // Configure notification handler - var promptListChangedNotificationReceived = false + val notifications = mutableListOf() client.setNotificationHandler(Method.Defined.NotificationsPromptsListChanged) { - promptListChangedNotificationReceived = true + notifications.add(it) CompletableDeferred(Unit) } @@ -46,16 +46,16 @@ class ServerPromptsNotificationTest : AbstractServerFeaturesTest() { // Verify that the notification was sent await untilAsserted { - assertTrue(promptListChangedNotificationReceived, "Notification should be sent when prompt is added") + assertTrue(notifications.isNotEmpty(), "Notification should be sent when prompt is added") } } @Test fun `removePrompts should remove multiple prompts and send two notifications`() = runTest { // Configure notification handler - var promptListChangedNotificationReceivedCount = 0 + val notifications = mutableListOf() client.setNotificationHandler(Method.Defined.NotificationsPromptsListChanged) { - promptListChangedNotificationReceivedCount += 1 + notifications.add(it) CompletableDeferred(Unit) } @@ -86,7 +86,7 @@ class ServerPromptsNotificationTest : AbstractServerFeaturesTest() { await untilAsserted { assertEquals( 4, - promptListChangedNotificationReceivedCount, + notifications.size, "Two notifications should be sent when prompts are added and two when removed", ) } @@ -95,9 +95,9 @@ class ServerPromptsNotificationTest : AbstractServerFeaturesTest() { @Test fun `notification should not be send when removed prompt does not exists`() = runTest { // Track notifications - var promptListChangedNotificationReceived = false + val notifications = mutableListOf() client.setNotificationHandler(Method.Defined.NotificationsPromptsListChanged) { - promptListChangedNotificationReceived = true + notifications.add(it) CompletableDeferred(Unit) } @@ -107,8 +107,8 @@ class ServerPromptsNotificationTest : AbstractServerFeaturesTest() { // Verify the result assertFalse(result, "Removing non-existent prompt should return false") await untilAsserted { - assertFalse( - promptListChangedNotificationReceived, + assertTrue( + notifications.isEmpty(), "No notification should be sent when prompt doesn't exist", ) } diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationSubscribeTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationSubscribeTest.kt index 62844501..fbc5ce41 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationSubscribeTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationSubscribeTest.kt @@ -2,20 +2,18 @@ package io.modelcontextprotocol.kotlin.sdk.server import io.modelcontextprotocol.kotlin.sdk.types.Method import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult -import io.modelcontextprotocol.kotlin.sdk.types.ResourceListChangedNotification import io.modelcontextprotocol.kotlin.sdk.types.ResourceUpdatedNotification import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.SubscribeRequest import io.modelcontextprotocol.kotlin.sdk.types.SubscribeRequestParams import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents +import kotlin.test.assertFalse +import kotlin.test.assertTrue import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest import org.awaitility.kotlin.await import org.awaitility.kotlin.untilAsserted import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { @@ -52,8 +50,6 @@ class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { ) } - client.subscribeResource(SubscribeRequest(SubscribeRequestParams(uri = testResourceUri1))) - server.addResource( uri = testResourceUri2, name = "Test Resource 2", @@ -71,6 +67,8 @@ class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { ) } + client.subscribeResource(SubscribeRequest(SubscribeRequestParams(uri = testResourceUri1))) + // Remove the resource val result = server.removeResource(testResourceUri1) @@ -79,9 +77,11 @@ class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { // Verify that the notification was sent await untilAsserted { - assertEquals(1, notifications.size, "Notification should be sent when resource 1 was deleted") + assertTrue( + notifications.any { it.params.uri == testResourceUri1 }, + "Notification should be sent when resource 1 was deleted" + ) } - assertEquals(testResourceUri1, notifications[0].params.uri, "Notification should contain the resource 1 URI") } @Test @@ -144,9 +144,8 @@ class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { println(notifications.map { it.params.uri }) // Verify that the notification was sent await untilAsserted { - assertEquals( - 2, - notifications.size, + assertTrue( + notifications.any { it.params.uri == testResourceUri1 }, "Notification should be sent when resource 1 and resource 2 was deleted", ) } @@ -165,11 +164,9 @@ class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { @Test fun `notification should not be send when removed resource does not exists`() = runTest { // Track notifications - var resourceListChangedNotificationReceived = false - client.setNotificationHandler( - Method.Defined.NotificationsResourcesListChanged, - ) { - resourceListChangedNotificationReceived = true + val notifications = mutableListOf() + client.setNotificationHandler(Method.Defined.NotificationsResourcesUpdated) { + notifications.add(it) CompletableDeferred(Unit) } @@ -178,8 +175,8 @@ class ServerResourcesNotificationSubscribeTest : AbstractServerFeaturesTest() { // Verify the result assertFalse(result, "Removing non-existent resource should return false") - assertFalse( - resourceListChangedNotificationReceived, + assertTrue( + notifications.isEmpty(), "No notification should be sent when resource doesn't exist", ) } diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationTest.kt index 52a42613..6337479f 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesNotificationTest.kt @@ -23,11 +23,11 @@ class ServerResourcesNotificationTest : AbstractServerFeaturesTest() { @Test fun `addResource should send notification`() = runTest { // Configure notification handler - var resourceListChangedNotificationReceived = false + val notifications = mutableListOf() client.setNotificationHandler( Method.Defined.NotificationsResourcesListChanged, ) { - resourceListChangedNotificationReceived = true + notifications.add(it) CompletableDeferred(Unit) } @@ -58,18 +58,18 @@ class ServerResourcesNotificationTest : AbstractServerFeaturesTest() { // Verify that the notification was sent await untilAsserted { - assertTrue(resourceListChangedNotificationReceived, "Notification should be sent when resource is added") + assertTrue(notifications.isNotEmpty(), "Notification should be sent when resource is added") } } @Test fun `removeResources should remove multiple resources and send two notifications`() = runTest { // Configure notification handler - var resourceListChangedNotificationReceivedCount = 0 + val notifications = mutableListOf() client.setNotificationHandler( Method.Defined.NotificationsResourcesListChanged, ) { - resourceListChangedNotificationReceivedCount += 1 + notifications.add(it) CompletableDeferred(Unit) } @@ -120,7 +120,7 @@ class ServerResourcesNotificationTest : AbstractServerFeaturesTest() { await untilAsserted { assertEquals( 4, - resourceListChangedNotificationReceivedCount, + notifications.size, "Two notifications should be sent when resources are added and two when removed", ) } @@ -129,11 +129,11 @@ class ServerResourcesNotificationTest : AbstractServerFeaturesTest() { @Test fun `notification should not be send when removed resource does not exists`() = runTest { // Track notifications - var resourceListChangedNotificationReceived = false + val notifications = mutableListOf() client.setNotificationHandler( Method.Defined.NotificationsResourcesListChanged, ) { - resourceListChangedNotificationReceived = true + notifications.add(it) CompletableDeferred(Unit) } @@ -142,8 +142,8 @@ class ServerResourcesNotificationTest : AbstractServerFeaturesTest() { // Verify the result assertFalse(result, "Removing non-existent resource should return false") - assertFalse( - resourceListChangedNotificationReceived, + assertTrue( + notifications.isEmpty(), "No notification should be sent when resource doesn't exist", ) } diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt index 5892532e..6f319092 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt @@ -6,14 +6,14 @@ import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.ToolListChangedNotification import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest import org.awaitility.kotlin.await import org.awaitility.kotlin.untilAsserted import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue class ServerToolsNotificationTest : AbstractServerFeaturesTest() { @@ -24,9 +24,9 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() { @Test fun `addTool should send notification`() = runTest { // Configure notification handler - var toolListChangedNotificationReceived = false + val notifications = mutableListOf() client.setNotificationHandler(Method.Defined.NotificationsToolsListChanged) { - toolListChangedNotificationReceived = true + notifications.add(it) CompletableDeferred(Unit) } @@ -43,16 +43,16 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() { // Verify that the notification was sent await untilAsserted { - assertTrue(toolListChangedNotificationReceived, "Notification should be sent when tool is added") + assertTrue(notifications.isNotEmpty(), "Notification should be sent when tool is added") } } @Test fun `removeTools should remove multiple tools and send two notifications`() = runTest { // Configure notification handler - var toolListChangedNotificationReceivedCount = 0 + val notifications = mutableListOf() client.setNotificationHandler(Method.Defined.NotificationsToolsListChanged) { - toolListChangedNotificationReceivedCount += 1 + notifications.add(it) CompletableDeferred(Unit) } @@ -73,7 +73,7 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() { await untilAsserted { assertEquals( 4, - toolListChangedNotificationReceivedCount, + notifications.size, "Two notifications should be sent when tools are added and two when removed", ) } @@ -82,9 +82,9 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() { @Test fun `notification should not be send when removed tool does not exists`() = runTest { // Track notifications - var toolListChangedNotificationReceived = false + val notifications = mutableListOf() client.setNotificationHandler(Method.Defined.NotificationsToolsListChanged) { - toolListChangedNotificationReceived = true + notifications.add(it) CompletableDeferred(Unit) } @@ -95,6 +95,6 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() { // Verify the result assertFalse(result, "Removing non-existent tool should return false") - assertFalse(toolListChangedNotificationReceived, "No notification should be sent when tool doesn't exist") + assertTrue(notifications.isEmpty(), "No notification should be sent when tool doesn't exist") } }