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 -->
This commit is contained in:
parent
935a1eddbc
commit
f462019c43
4 changed files with 93 additions and 5 deletions
|
|
@ -16,6 +16,10 @@ dependencies {
|
|||
implementation(libs.mcp.kotlin.server)
|
||||
implementation(libs.ktor.server.cio)
|
||||
implementation(libs.slf4j.simple)
|
||||
|
||||
testImplementation(libs.mcp.kotlin.client)
|
||||
testImplementation(libs.ktor.client.cio)
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ kotlin = "2.2.21"
|
|||
ktor = "3.2.3"
|
||||
mcp-kotlin = "0.7.4"
|
||||
slf4j = "2.0.17"
|
||||
serialization = "1.9.0"
|
||||
|
||||
[libraries]
|
||||
ktor-bom = { group = "io.ktor", name = "ktor-bom", version.ref = "ktor" }
|
||||
ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio" }
|
||||
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio" }
|
||||
mcp-kotlin-server = { group = "io.modelcontextprotocol", name = "kotlin-sdk-server", version.ref = "mcp-kotlin" }
|
||||
mcp-kotlin-client = { group = "io.modelcontextprotocol", name = "kotlin-sdk-client", version.ref = "mcp-kotlin" }
|
||||
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
|
||||
|
||||
[plugins]
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ fun configureServer(): Server {
|
|||
return server
|
||||
}
|
||||
|
||||
fun runSseMcpServerWithPlainConfiguration(port: Int) {
|
||||
fun runSseMcpServerWithPlainConfiguration(port: Int, wait: Boolean = true) {
|
||||
val serverSessions = ConcurrentMap<String, ServerSession>()
|
||||
println("Starting SSE server on port $port")
|
||||
println("Use inspector to connect to http://localhost:$port/sse")
|
||||
|
|
@ -136,7 +136,7 @@ fun runSseMcpServerWithPlainConfiguration(port: Int) {
|
|||
transport.handlePostMessage(call)
|
||||
}
|
||||
}
|
||||
}.start(wait = true)
|
||||
}.start(wait = wait)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -147,7 +147,7 @@ fun runSseMcpServerWithPlainConfiguration(port: Int) {
|
|||
*
|
||||
* @param port The port number on which the SSE MCP server will listen for client connections.
|
||||
*/
|
||||
fun runSseMcpServerUsingKtorPlugin(port: Int) {
|
||||
fun runSseMcpServerUsingKtorPlugin(port: Int, wait: Boolean = true) {
|
||||
println("Starting SSE server on port $port")
|
||||
println("Use inspector to connect to http://localhost:$port/sse")
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ fun runSseMcpServerUsingKtorPlugin(port: Int) {
|
|||
mcp {
|
||||
return@mcp configureServer()
|
||||
}
|
||||
}.start(wait = true)
|
||||
}.start(wait = wait)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.client.plugins.sse.SSE
|
||||
import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
|
||||
import io.modelcontextprotocol.kotlin.sdk.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.TextContent
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.Client
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.mcpSseTransport
|
||||
import io.modelcontextprotocol.sample.server.runSseMcpServerUsingKtorPlugin
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class SseServerIntegrationTest {
|
||||
|
||||
companion object {
|
||||
private const val PORT = 3002
|
||||
}
|
||||
|
||||
private lateinit var client: Client
|
||||
|
||||
private fun initClient(port: Int) {
|
||||
client = Client(
|
||||
Implementation(name = "test-client", version = "0.1.0"),
|
||||
)
|
||||
|
||||
val httpClient = HttpClient(CIO) {
|
||||
install(SSE)
|
||||
}
|
||||
|
||||
// Create a transport wrapper that captures the session ID and received messages
|
||||
val transport = httpClient.mcpSseTransport {
|
||||
url {
|
||||
this.host = "127.0.0.1"
|
||||
this.port = port
|
||||
}
|
||||
}
|
||||
runBlocking {
|
||||
client.connect(transport)
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
fun setUp() {
|
||||
runSseMcpServerUsingKtorPlugin(PORT, wait = false)
|
||||
initClient(PORT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should get tools`(): Unit = runBlocking {
|
||||
val tools = client.listTools().tools
|
||||
|
||||
assertEquals(expected = listOf("kotlin-sdk-tool"), actual = tools.map { it.name })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should get prompts`(): Unit = runBlocking {
|
||||
val prompts = client.listPrompts().prompts
|
||||
|
||||
assertEquals(expected = listOf("Kotlin Developer"), actual = prompts.map { it.name })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should get resources`(): Unit = runBlocking {
|
||||
val resources = client.listResources().resources
|
||||
|
||||
assertEquals(expected = listOf("Web Search"), actual = resources.map { it.name })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should call tool`(): Unit = runBlocking {
|
||||
val toolResult = client.callTool("kotlin-sdk-tool", EmptyJsonObject)
|
||||
val content = toolResult?.content?.single()
|
||||
assertIs<TextContent>(content, "Tool result should be a text content")
|
||||
|
||||
assertEquals(expected = "Hello, world!", actual = content.text)
|
||||
assertEquals(expected = "text", actual = content.type)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue