samples: update mcp-kotlin to 0.10.0 across sample projects (#645)
## Update mcp-kotlin to 0.10.0 across sample projects ## How Has This Been Tested? Locally, added ServerIntegrationTest to simple-streamable-server ## Breaking Changes No ## Types of changes - [ ] 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) - [x] Documentation update - [x] Test update ## 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
This commit is contained in:
parent
6e247f902d
commit
ad072325c9
6 changed files with 453 additions and 233 deletions
|
|
@ -2,7 +2,7 @@
|
|||
anthropic = "2.18.0"
|
||||
kotlin = "2.2.21"
|
||||
ktor = "3.2.3"
|
||||
mcp-kotlin = "0.9.0"
|
||||
mcp-kotlin = "0.10.0"
|
||||
shadow = "9.4.1"
|
||||
slf4j = "2.0.17"
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -22,6 +22,15 @@ dependencies {
|
|||
implementation(libs.ktor.server.sse)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
implementation(libs.slf4j.simple)
|
||||
|
||||
testImplementation(libs.mcp.kotlin.client)
|
||||
testImplementation(libs.ktor.client.cio)
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
[versions]
|
||||
kotlin = "2.2.21"
|
||||
ktor = "3.2.3"
|
||||
mcp-kotlin = "0.9.0"
|
||||
junit = "6.0.3"
|
||||
mcp-kotlin = "0.10.0"
|
||||
slf4j = "2.0.17"
|
||||
shadow = "9.4.1"
|
||||
|
||||
|
|
@ -13,7 +14,10 @@ ktor-server-content-negotiation = { group = "io.ktor", name = "ktor-server-conte
|
|||
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json" }
|
||||
ktor-server-auth = { group = "io.ktor", name = "ktor-server-auth" }
|
||||
ktor-server-sse = { group = "io.ktor", name = "ktor-server-sse" }
|
||||
mcp-kotlin-client = { group = "io.modelcontextprotocol", name = "kotlin-sdk-client", version.ref = "mcp-kotlin" }
|
||||
mcp-kotlin-server = { group = "io.modelcontextprotocol", name = "kotlin-sdk-server", version.ref = "mcp-kotlin" }
|
||||
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio" }
|
||||
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" }
|
||||
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
|
||||
|
||||
[plugins]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
package io.modelcontextprotocol.sample.server
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.client.plugins.sse.SSE
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.Client
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.mcpStreamableHttp
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequestParams
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequestParams
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Role
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class ServerIntegrationTest {
|
||||
|
||||
private val server = embeddedServer(Netty, host = "127.0.0.1", port = 0) {
|
||||
configureServer()
|
||||
}
|
||||
|
||||
private val httpClient = HttpClient(CIO) {
|
||||
install(SSE)
|
||||
}
|
||||
|
||||
private lateinit var mcpClient: Client
|
||||
|
||||
@BeforeAll
|
||||
suspend fun beforeAll() {
|
||||
server.start(wait = false)
|
||||
val port = server.engine.resolvedConnectors().single().port
|
||||
|
||||
mcpClient = httpClient.mcpStreamableHttp(
|
||||
url = "http://127.0.0.1:$port/mcp",
|
||||
)
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
suspend fun afterAll() {
|
||||
mcpClient.close()
|
||||
httpClient.close()
|
||||
server.stop(500, 1000)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should negotiate server capabilities`() {
|
||||
val capabilities = mcpClient.serverCapabilities
|
||||
assertNotNull(capabilities)
|
||||
assertNotNull(capabilities.tools)
|
||||
assertNotNull(capabilities.prompts)
|
||||
assertNotNull(capabilities.resources)
|
||||
assertNotNull(capabilities.logging)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should report server version`() {
|
||||
val version = mcpClient.serverVersion
|
||||
assertNotNull(version)
|
||||
assertEquals("simple-streamable-http-server", version.name)
|
||||
assertEquals("1.0.0", version.version)
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should list tools`() {
|
||||
val result = mcpClient.listTools()
|
||||
val toolNames = result.tools.map { it.name }.sorted()
|
||||
assertEquals(listOf("greet", "multi-greet"), toolNames)
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should call greet tool`() {
|
||||
val result = mcpClient.callTool(name = "greet", arguments = mapOf("name" to "Alice"))
|
||||
val content = result.content.single()
|
||||
assertIs<TextContent>(content)
|
||||
assertEquals("Hello, Alice!", content.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should call greet tool with default name`() {
|
||||
val result = mcpClient.callTool(name = "greet", arguments = emptyMap())
|
||||
val content = result.content.single()
|
||||
assertIs<TextContent>(content)
|
||||
assertEquals("Hello, World!", content.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should call multi-greet tool with logging notifications`() {
|
||||
val result = mcpClient.callTool(name = "multi-greet", arguments = mapOf("name" to "Bob"))
|
||||
val content = result.content.single()
|
||||
assertIs<TextContent>(content)
|
||||
assertEquals("Good morning, Bob!", content.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should list prompts`() {
|
||||
val result = mcpClient.listPrompts()
|
||||
assertEquals(listOf("greeting-template"), result.prompts.map { it.name })
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should get greeting template prompt`() {
|
||||
val result = mcpClient.getPrompt(
|
||||
GetPromptRequest(
|
||||
GetPromptRequestParams(
|
||||
name = "greeting-template",
|
||||
arguments = mapOf("name" to "Charlie"),
|
||||
),
|
||||
),
|
||||
)
|
||||
val message = result.messages.single()
|
||||
assertEquals(Role.User, message.role)
|
||||
val content = message.content
|
||||
assertIs<TextContent>(content)
|
||||
assertEquals("Please greet Charlie in a friendly manner.", content.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should list resources`() {
|
||||
val result = mcpClient.listResources()
|
||||
assertEquals(listOf("Default Greeting"), result.resources.map { it.name })
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `should read default greeting resource`() {
|
||||
val result = mcpClient.readResource(
|
||||
ReadResourceRequest(
|
||||
ReadResourceRequestParams(uri = "https://example.com/greetings/default"),
|
||||
),
|
||||
)
|
||||
val content = result.contents.single()
|
||||
assertIs<TextResourceContents>(content)
|
||||
assertEquals("Hello, world!", content.text)
|
||||
assertEquals("text/plain", content.mimeType)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
org.slf4j.simpleLogger.defaultLogLevel=INFO
|
||||
org.slf4j.simpleLogger.showThreadName=true
|
||||
org.slf4j.simpleLogger.showDateTime=false
|
||||
|
||||
# Log level for specific packages or classes
|
||||
org.slf4j.simpleLogger.log.io.modelcontextprotocol.sample.server=DEBUG
|
||||
Loading…
Reference in a new issue