Skip to content

Commit f462019

Browse files
authored
Add tests to server samples (#369)
Add test for sse server TODO: Add tests for the other protocols <!-- Provide a brief summary of your changes --> ## Motivation and Context <!-- Why is this change needed? What problem does it solve? --> ## How Has This Been Tested? <!-- Have you tested this in a real application? Which scenarios were tested? --> ## Breaking Changes <!-- Will users need to update their code or configurations? --> ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [x] Tests ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context <!-- Add any other context, implementation notes, or design decisions -->
1 parent 935a1ed commit f462019

File tree

4 files changed

+93
-5
lines changed

4 files changed

+93
-5
lines changed

samples/kotlin-mcp-server/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ dependencies {
1616
implementation(libs.mcp.kotlin.server)
1717
implementation(libs.ktor.server.cio)
1818
implementation(libs.slf4j.simple)
19+
20+
testImplementation(libs.mcp.kotlin.client)
21+
testImplementation(libs.ktor.client.cio)
22+
testImplementation(kotlin("test"))
1923
}
2024

2125
tasks.test {

samples/kotlin-mcp-server/gradle/libs.versions.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ kotlin = "2.2.21"
33
ktor = "3.2.3"
44
mcp-kotlin = "0.7.4"
55
slf4j = "2.0.17"
6-
serialization = "1.9.0"
76

87
[libraries]
98
ktor-bom = { group = "io.ktor", name = "ktor-bom", version.ref = "ktor" }
109
ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio" }
10+
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio" }
1111
mcp-kotlin-server = { group = "io.modelcontextprotocol", name = "kotlin-sdk-server", version.ref = "mcp-kotlin" }
12+
mcp-kotlin-client = { group = "io.modelcontextprotocol", name = "kotlin-sdk-client", version.ref = "mcp-kotlin" }
1213
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
1314

1415
[plugins]

samples/kotlin-mcp-server/src/main/kotlin/io/modelcontextprotocol/sample/server/server.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ fun configureServer(): Server {
100100
return server
101101
}
102102

103-
fun runSseMcpServerWithPlainConfiguration(port: Int) {
103+
fun runSseMcpServerWithPlainConfiguration(port: Int, wait: Boolean = true) {
104104
val serverSessions = ConcurrentMap<String, ServerSession>()
105105
println("Starting SSE server on port $port")
106106
println("Use inspector to connect to http://localhost:$port/sse")
@@ -136,7 +136,7 @@ fun runSseMcpServerWithPlainConfiguration(port: Int) {
136136
transport.handlePostMessage(call)
137137
}
138138
}
139-
}.start(wait = true)
139+
}.start(wait = wait)
140140
}
141141

142142
/**
@@ -147,15 +147,15 @@ fun runSseMcpServerWithPlainConfiguration(port: Int) {
147147
*
148148
* @param port The port number on which the SSE MCP server will listen for client connections.
149149
*/
150-
fun runSseMcpServerUsingKtorPlugin(port: Int) {
150+
fun runSseMcpServerUsingKtorPlugin(port: Int, wait: Boolean = true) {
151151
println("Starting SSE server on port $port")
152152
println("Use inspector to connect to http://localhost:$port/sse")
153153

154154
embeddedServer(CIO, host = "127.0.0.1", port = port) {
155155
mcp {
156156
return@mcp configureServer()
157157
}
158-
}.start(wait = true)
158+
}.start(wait = wait)
159159
}
160160

161161
/**
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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

Comments
 (0)