|
| 1 | +import io.ktor.client.HttpClient |
| 2 | +import io.ktor.client.engine.cio.CIO |
| 3 | +import io.ktor.client.plugins.sse.SSE |
| 4 | +import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject |
| 5 | +import io.modelcontextprotocol.kotlin.sdk.Implementation |
| 6 | +import io.modelcontextprotocol.kotlin.sdk.TextContent |
| 7 | +import io.modelcontextprotocol.kotlin.sdk.client.Client |
| 8 | +import io.modelcontextprotocol.kotlin.sdk.client.mcpSseTransport |
| 9 | +import io.modelcontextprotocol.sample.server.runSseMcpServerUsingKtorPlugin |
| 10 | +import kotlinx.coroutines.runBlocking |
| 11 | +import org.junit.jupiter.api.BeforeAll |
| 12 | +import org.junit.jupiter.api.TestInstance |
| 13 | +import kotlin.test.Test |
| 14 | +import kotlin.test.assertEquals |
| 15 | +import kotlin.test.assertIs |
| 16 | + |
| 17 | +@TestInstance(TestInstance.Lifecycle.PER_CLASS) |
| 18 | +class SseServerIntegrationTest { |
| 19 | + |
| 20 | + companion object { |
| 21 | + private const val PORT = 3002 |
| 22 | + } |
| 23 | + |
| 24 | + private lateinit var client: Client |
| 25 | + |
| 26 | + private fun initClient(port: Int) { |
| 27 | + client = Client( |
| 28 | + Implementation(name = "test-client", version = "0.1.0"), |
| 29 | + ) |
| 30 | + |
| 31 | + val httpClient = HttpClient(CIO) { |
| 32 | + install(SSE) |
| 33 | + } |
| 34 | + |
| 35 | + // Create a transport wrapper that captures the session ID and received messages |
| 36 | + val transport = httpClient.mcpSseTransport { |
| 37 | + url { |
| 38 | + this.host = "127.0.0.1" |
| 39 | + this.port = port |
| 40 | + } |
| 41 | + } |
| 42 | + runBlocking { |
| 43 | + client.connect(transport) |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + @BeforeAll |
| 48 | + fun setUp() { |
| 49 | + runSseMcpServerUsingKtorPlugin(PORT, wait = false) |
| 50 | + initClient(PORT) |
| 51 | + } |
| 52 | + |
| 53 | + @Test |
| 54 | + fun `should get tools`(): Unit = runBlocking { |
| 55 | + val tools = client.listTools().tools |
| 56 | + |
| 57 | + assertEquals(expected = listOf("kotlin-sdk-tool"), actual = tools.map { it.name }) |
| 58 | + } |
| 59 | + |
| 60 | + @Test |
| 61 | + fun `should get prompts`(): Unit = runBlocking { |
| 62 | + val prompts = client.listPrompts().prompts |
| 63 | + |
| 64 | + assertEquals(expected = listOf("Kotlin Developer"), actual = prompts.map { it.name }) |
| 65 | + } |
| 66 | + |
| 67 | + @Test |
| 68 | + fun `should get resources`(): Unit = runBlocking { |
| 69 | + val resources = client.listResources().resources |
| 70 | + |
| 71 | + assertEquals(expected = listOf("Web Search"), actual = resources.map { it.name }) |
| 72 | + } |
| 73 | + |
| 74 | + @Test |
| 75 | + fun `should call tool`(): Unit = runBlocking { |
| 76 | + val toolResult = client.callTool("kotlin-sdk-tool", EmptyJsonObject) |
| 77 | + val content = toolResult?.content?.single() |
| 78 | + assertIs<TextContent>(content, "Tool result should be a text content") |
| 79 | + |
| 80 | + assertEquals(expected = "Hello, world!", actual = content.text) |
| 81 | + assertEquals(expected = "text", actual = content.type) |
| 82 | + } |
| 83 | +} |
0 commit comments