From 59867c54633ac01c62a62998cfb43f9d6df9ab11 Mon Sep 17 00:00:00 2001 From: Stream Date: Mon, 27 Jul 2026 14:59:10 +0800 Subject: [PATCH] feat: migrate to kotlinx.schema --- README.md | 11 +- .../sdk/conformance/ConformanceTools.kt | 59 ++--- gradle/libs.versions.toml | 2 + .../kotlin/sdk/client/ClientTest.kt | 4 +- .../kotlin/AbstractToolIntegrationTest.kt | 245 ++++++------------ ...mableHttpClientTransportIntegrationTest.kt | 32 +-- .../http/KotlinServerForTsClientHttp.kt | 31 +-- .../kotlin/sdk/server/SamplingTest.kt | 11 +- .../sdk/server/ServerBulkFeaturesTest.kt | 20 +- .../sdk/server/ServerToolsNotificationTest.kt | 4 +- .../kotlin/sdk/server/ServerToolsTest.kt | 6 +- .../client/ClientSamplingValidationTest.kt | 5 +- kotlin-sdk-core/api/kotlin-sdk-core.api | 52 +--- kotlin-sdk-core/build.gradle.kts | 1 + .../kotlin/sdk/types/tools.kt | 37 +-- .../kotlin/sdk/types/SamplingTest.kt | 5 +- .../kotlin/sdk/types/ToolsTest.kt | 202 ++++----------- kotlin-sdk-server/api/kotlin-sdk-server.api | 4 +- .../kotlin/sdk/server/Server.kt | 6 +- .../StreamableHttpServerTransportTest.kt | 4 +- .../org/kotlinlang/mcp/KotlinlangServer.kt | 42 ++- .../sample/server/server.kt | 24 +- .../sample/server/McpWeatherServer.kt | 48 ++-- 23 files changed, 284 insertions(+), 571 deletions(-) diff --git a/README.md b/README.md index aefadcf6a..2f2c75f19 100644 --- a/README.md +++ b/README.md @@ -197,9 +197,8 @@ import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult import io.modelcontextprotocol.kotlin.sdk.types.Implementation import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.TextContent -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.put +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition fun main(args: Array) { val port = args.firstOrNull()?.toIntOrNull() ?: 3000 @@ -218,10 +217,8 @@ fun main(args: Array) { mcpServer.addTool( name = "example-tool", description = "An example tool", - inputSchema = ToolSchema( - properties = buildJsonObject { - put("input", buildJsonObject { put("type", "string") }) - } + inputSchema = ObjectPropertyDefinition( + properties = mapOf("input" to StringPropertyDefinition()) ) ) { request -> CallToolResult(content = listOf(TextContent("Hello, world!"))) diff --git a/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt b/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt index c81c98c31..83ba62e4e 100644 --- a/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt +++ b/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt @@ -26,12 +26,13 @@ import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents import io.modelcontextprotocol.kotlin.sdk.types.TitledMultiSelectEnumSchema import io.modelcontextprotocol.kotlin.sdk.types.TitledSingleSelectEnumSchema -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import io.modelcontextprotocol.kotlin.sdk.types.UntitledMultiSelectEnumSchema import io.modelcontextprotocol.kotlin.sdk.types.UntitledSingleSelectEnumSchema import kotlinx.coroutines.delay +import kotlinx.schema.json.NumericPropertyDefinition +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.double import kotlinx.serialization.json.jsonPrimitive import kotlin.time.Duration.Companion.milliseconds @@ -163,10 +164,8 @@ fun Server.registerConformanceTools() { addTool( name = "test_sampling", description = "test_sampling", - inputSchema = ToolSchema( - properties = buildJsonObject { - put("prompt", buildJsonObject { put("type", JsonPrimitive("string")) }) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf("prompt" to StringPropertyDefinition()), required = listOf("prompt"), ), ) { request -> @@ -192,10 +191,8 @@ fun Server.registerConformanceTools() { addTool( name = "test_elicitation", description = "test_elicitation", - inputSchema = ToolSchema( - properties = buildJsonObject { - put("message", buildJsonObject { put("type", JsonPrimitive("string")) }) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf("message" to StringPropertyDefinition()), required = listOf("message"), ), ) { request -> @@ -352,11 +349,11 @@ fun Server.registerConformanceTools() { addTool( name = "add_numbers", description = "Adds two numbers together", - inputSchema = ToolSchema( - properties = buildJsonObject { - put("a", buildJsonObject { put("type", JsonPrimitive("number")) }) - put("b", buildJsonObject { put("type", JsonPrimitive("number")) }) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "a" to NumericPropertyDefinition(type = listOf("number")), + "b" to NumericPropertyDefinition(type = listOf("number")), + ), required = listOf("a", "b"), ), ) { request -> @@ -389,28 +386,16 @@ fun Server.registerConformanceTools() { addTool( name = "json_schema_2020_12_tool", description = "Tool with JSON Schema 2020-12 features for conformance testing (SEP-1613)", - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "name", - buildJsonObject { - put("type", JsonPrimitive("string")) - }, - ) - put( - "address", - buildJsonObject { - put("type", JsonPrimitive("object")) - put( - "properties", - buildJsonObject { - put("street", buildJsonObject { put("type", JsonPrimitive("string")) }) - put("city", buildJsonObject { put("type", JsonPrimitive("string")) }) - }, - ) - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "name" to StringPropertyDefinition(), + "address" to ObjectPropertyDefinition( + properties = mapOf( + "street" to StringPropertyDefinition(), + "city" to StringPropertyDefinition(), + ), + ), + ), ), ) { request -> CallToolResult( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d3c5a8d5..0495d6412 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ collections-immutable = "0.5.1" coroutines = "1.11.0" kotest = "6.2.2" kotlinx-io = "0.9.1" +kotlinx-schema = "0.5.0" ktor = "3.5.1" logging = "8.0.4" mockk = "1.14.11" @@ -40,6 +41,7 @@ kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotli kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-coroutines-core-wasm = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core-wasm-js", version.ref = "coroutines" } kotlinx-io-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-io-core", version.ref = "kotlinx-io" } +kotlinx-schema-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-schema-json", version.ref = "kotlinx-schema" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" } # Ktor diff --git a/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt b/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt index 099329d79..615652963 100644 --- a/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt +++ b/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt @@ -47,7 +47,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.StringSchema import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.TitledMultiSelectEnumSchema import io.modelcontextprotocol.kotlin.sdk.types.Tool -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import io.modelcontextprotocol.kotlin.sdk.types.UntitledMultiSelectEnumSchema import io.modelcontextprotocol.kotlin.sdk.types.UntitledSingleSelectEnumSchema import io.modelcontextprotocol.kotlin.sdk.types.UrlElicitationRequiredException @@ -60,6 +59,7 @@ import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout +import kotlinx.schema.json.ObjectPropertyDefinition import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.buildJsonObject @@ -719,7 +719,7 @@ class ClientTest { title = "testTool title", description = "testTool description", annotations = null, - inputSchema = ToolSchema(), + inputSchema = ObjectPropertyDefinition(), outputSchema = null, ), ), diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt index b80212293..b269c87f0 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt @@ -15,12 +15,16 @@ import io.modelcontextprotocol.kotlin.sdk.types.RPCError import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.Tool -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import kotlinx.schema.json.ArrayPropertyDefinition +import kotlinx.schema.json.BooleanPropertyDefinition +import kotlinx.schema.json.NumericPropertyDefinition +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.add @@ -78,16 +82,10 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = testToolName, description = testToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "text", - buildJsonObject { - put("type", "string") - put("description", "The text to echo back") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "text" to StringPropertyDefinition(description = "The text to echo back"), + ), required = listOf("text"), ), ) { request -> @@ -106,16 +104,10 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = basicToolName, description = basicToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "text", - buildJsonObject { - put("type", "string") - put("description", "The text to echo back") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "text" to StringPropertyDefinition(description = "The text to echo back"), + ), required = listOf("text"), ), ) { request -> @@ -132,16 +124,10 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = specialCharsToolName, description = specialCharsToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "special", - buildJsonObject { - put("type", "string") - put("description", "Special characters to process") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "special" to StringPropertyDefinition(description = "Special characters to process"), + ), ), ) { request -> val special = (request.params.arguments?.get("special") as? JsonPrimitive)?.content ?: specialCharsContent @@ -158,16 +144,13 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = slowToolName, description = slowToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "delay", - buildJsonObject { - put("type", "integer") - put("description", "Delay in milliseconds") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "delay" to NumericPropertyDefinition( + type = listOf("integer"), + description = "Delay in milliseconds", + ), + ), ), ) { request -> val delay = (request.params.arguments?.get("delay") as? JsonPrimitive)?.content?.toIntOrNull() ?: 1000 @@ -188,16 +171,13 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = largeToolName, description = largeToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "size", - buildJsonObject { - put("type", "integer") - put("description", "Size multiplier") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "size" to NumericPropertyDefinition( + type = listOf("integer"), + description = "Size multiplier", + ), + ), ), ) { request -> val size = (request.params.arguments?.get("size") as? JsonPrimitive)?.content?.toIntOrNull() ?: 1 @@ -214,68 +194,34 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = complexToolName, description = complexToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "operation", - buildJsonObject { - put("type", "string") - put("description", "The operation to perform (add, subtract, multiply, divide)") - put( - "enum", - buildJsonArray { - add("add") - add("subtract") - add("multiply") - add("divide") - }, - ) - }, - ) - put( - "a", - buildJsonObject { - put("type", "number") - put("description", "First operand") - }, - ) - put( - "b", - buildJsonObject { - put("type", "number") - put("description", "Second operand") - }, - ) - put( - "precision", - buildJsonObject { - put("type", "integer") - put("description", "Number of decimal places (optional)") - put("default", 2) - }, - ) - put( - "showSteps", - buildJsonObject { - put("type", "boolean") - put("description", "Whether to show calculation steps") - put("default", false) - }, - ) - put( - "tags", - buildJsonObject { - put("type", "array") - put("description", "Optional tags for the calculation") - put( - "items", - buildJsonObject { - put("type", "string") - }, - ) - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "operation" to StringPropertyDefinition( + description = "The operation to perform (add, subtract, multiply, divide)", + enum = listOf("add", "subtract", "multiply", "divide"), + ), + "a" to NumericPropertyDefinition( + type = listOf("number"), + description = "First operand", + ), + "b" to NumericPropertyDefinition( + type = listOf("number"), + description = "Second operand", + ), + "precision" to NumericPropertyDefinition( + type = listOf("integer"), + description = "Number of decimal places (optional)", + default = JsonPrimitive(2), + ), + "showSteps" to BooleanPropertyDefinition( + description = "Whether to show calculation steps", + default = JsonPrimitive(false), + ), + "tags" to ArrayPropertyDefinition( + description = "Optional tags for the calculation", + items = StringPropertyDefinition(), + ), + ), required = listOf("operation", "a", "b"), ), ) { request -> @@ -329,32 +275,17 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = errorToolName, description = errorToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "errorType", - buildJsonObject { - put("type", "string") - put("description", "Type of error to simulate (none, exception, error)") - put( - "enum", - buildJsonArray { - add("none") - add("exception") - add("error") - }, - ) - }, - ) - put( - "message", - buildJsonObject { - put("type", "string") - put("description", "Custom error message") - put("default", "An error occurred") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "errorType" to StringPropertyDefinition( + description = "Type of error to simulate (none, exception, error)", + enum = listOf("none", "exception", "error"), + ), + "message" to StringPropertyDefinition( + description = "Custom error message", + default = JsonPrimitive("An error occurred"), + ), + ), required = listOf("errorType"), ), ) { request -> @@ -387,24 +318,14 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { server.addTool( name = multiContentToolName, description = multiContentToolDescription, - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "text", - buildJsonObject { - put("type", "string") - put("description", "Text to include in the response") - }, - ) - put( - "includeImage", - buildJsonObject { - put("type", "boolean") - put("description", "Whether to include an image in the response") - put("default", true) - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "text" to StringPropertyDefinition(description = "Text to include in the response"), + "includeImage" to BooleanPropertyDefinition( + description = "Whether to include an image in the response", + default = JsonPrimitive(true), + ), + ), required = listOf("text"), ), ) { request -> @@ -805,14 +726,14 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() { fun testListToolsPagination() = runBlocking(Dispatchers.IO) { val receivedCursors = CopyOnWriteArrayList() val page1 = listOf( - Tool(name = "t-1", inputSchema = ToolSchema()), - Tool(name = "t-2", inputSchema = ToolSchema()), + Tool(name = "t-1", inputSchema = ObjectPropertyDefinition()), + Tool(name = "t-2", inputSchema = ObjectPropertyDefinition()), ) val page2 = listOf( - Tool(name = "t-3", inputSchema = ToolSchema()), - Tool(name = "t-4", inputSchema = ToolSchema()), + Tool(name = "t-3", inputSchema = ObjectPropertyDefinition()), + Tool(name = "t-4", inputSchema = ObjectPropertyDefinition()), ) - val page3 = listOf(Tool(name = "t-5", inputSchema = ToolSchema())) + val page3 = listOf(Tool(name = "t-5", inputSchema = ObjectPropertyDefinition())) server.sessions.forEach { (_, session) -> session.setRequestHandler(Method.Defined.ToolsList) { request, _ -> diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt index 598825f13..39c8ba68a 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt @@ -24,18 +24,17 @@ import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject import io.modelcontextprotocol.kotlin.sdk.types.Implementation import io.modelcontextprotocol.kotlin.sdk.types.Tool -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import io.modelcontextprotocol.kotlin.test.utils.actualPort import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking +import kotlinx.schema.json.NumericPropertyDefinition +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.Json -import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonObject import kotlin.test.Test import io.ktor.client.engine.cio.CIO as ClientCIO import io.ktor.server.cio.CIO as ServerCIO @@ -113,22 +112,19 @@ internal class StreamableHttpClientTransportIntegrationTest { name = "get_weather", title = "Weather Information Provider", description = "Get current weather information for a location", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("location") { - put("type", "string") - put("description", "City name or zip code") - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "location" to StringPropertyDefinition(description = "City name or zip code"), + ), required = listOf("location"), ), - outputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("temperature") { - put("type", "number") - put("description", "Temperature, Celsius") - } - }, + outputSchema = ObjectPropertyDefinition( + properties = mapOf( + "temperature" to NumericPropertyDefinition( + type = listOf("number"), + description = "Temperature, Celsius", + ), + ), required = listOf("temperature"), ), annotations = null, diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/http/KotlinServerForTsClientHttp.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/http/KotlinServerForTsClientHttp.kt index ebd049985..c30b76a35 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/http/KotlinServerForTsClientHttp.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/http/KotlinServerForTsClientHttp.kt @@ -39,13 +39,14 @@ import io.modelcontextprotocol.kotlin.sdk.types.Role import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull @@ -213,16 +214,10 @@ class KotlinServerForTsClient { server.addTool( name = "greet", description = "A simple greeting tool", - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "name", - buildJsonObject { - put("type", JsonPrimitive("string")) - put("description", JsonPrimitive("Name to greet")) - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "name" to StringPropertyDefinition(description = "Name to greet"), + ), required = listOf("name"), ), ) { request -> @@ -238,16 +233,10 @@ class KotlinServerForTsClient { server.addTool( name = "multi-greet", description = "A greeting tool that sends multiple notifications", - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "name", - buildJsonObject { - put("type", JsonPrimitive("string")) - put("description", JsonPrimitive("Name to greet")) - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "name" to StringPropertyDefinition(description = "Name to greet"), + ), required = listOf("name"), ), ) { request -> diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt index f161d8ec0..e7ee20ae4 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt @@ -19,11 +19,12 @@ import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.Tool import io.modelcontextprotocol.kotlin.sdk.types.ToolChoice import io.modelcontextprotocol.kotlin.sdk.types.ToolResultContent -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import io.modelcontextprotocol.kotlin.sdk.types.ToolUseContent import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.buildJsonObject import org.junit.jupiter.api.assertDoesNotThrow @@ -35,16 +36,14 @@ class SamplingTest { private val dummyTool = Tool( name = "t", - inputSchema = ToolSchema(properties = buildJsonObject { }, required = emptyList()), + inputSchema = ObjectPropertyDefinition(properties = emptyMap(), required = emptyList()), ) private val weatherTool = Tool( name = "get_weather", description = "Return the current temperature in Celsius.", - inputSchema = ToolSchema( - properties = buildJsonObject { - put("location", buildJsonObject { put("type", JsonPrimitive("string")) }) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf("location" to StringPropertyDefinition()), required = listOf("location"), ), ) diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt index 52f6908a7..bc859c590 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt @@ -12,8 +12,8 @@ import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult import io.modelcontextprotocol.kotlin.sdk.types.Resource import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.Tool -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import kotlinx.coroutines.test.runTest +import kotlinx.schema.json.ObjectPropertyDefinition import org.junit.jupiter.api.Test class ServerBulkFeaturesTest : AbstractServerFeaturesTest() { @@ -30,9 +30,9 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() { fun `addTools should register all provided tools`() = runTest { server.addTools( listOf( - RegisteredTool(Tool("bulk-a", ToolSchema(), "Tool A")) { CallToolResult(emptyList()) }, - RegisteredTool(Tool("bulk-b", ToolSchema(), "Tool B")) { CallToolResult(emptyList()) }, - RegisteredTool(Tool("bulk-c", ToolSchema(), "Tool C")) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("bulk-a", ObjectPropertyDefinition(), "Tool A")) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("bulk-b", ObjectPropertyDefinition(), "Tool B")) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("bulk-c", ObjectPropertyDefinition(), "Tool C")) { CallToolResult(emptyList()) }, ), ) @@ -47,8 +47,8 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() { server.addTool("existing", "Existing") { CallToolResult(emptyList()) } server.addTools( listOf( - RegisteredTool(Tool("new-a", ToolSchema())) { CallToolResult(emptyList()) }, - RegisteredTool(Tool("new-b", ToolSchema())) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("new-a", ObjectPropertyDefinition())) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("new-b", ObjectPropertyDefinition())) { CallToolResult(emptyList()) }, ), ) @@ -65,8 +65,8 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() { shouldThrow { server.addTools( listOf( - RegisteredTool(Tool("new-a", ToolSchema())) { CallToolResult(emptyList()) }, - RegisteredTool(Tool("existing", ToolSchema())) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("new-a", ObjectPropertyDefinition())) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("existing", ObjectPropertyDefinition())) { CallToolResult(emptyList()) }, ), ) } @@ -82,8 +82,8 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() { shouldThrow { server.addTools( listOf( - RegisteredTool(Tool("dup", ToolSchema())) { CallToolResult(emptyList()) }, - RegisteredTool(Tool("dup", ToolSchema())) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("dup", ObjectPropertyDefinition())) { CallToolResult(emptyList()) }, + RegisteredTool(Tool("dup", ObjectPropertyDefinition())) { CallToolResult(emptyList()) }, ), ) } diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt index 9ac744bf2..4ae1fdaf7 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt @@ -5,9 +5,9 @@ import io.modelcontextprotocol.kotlin.sdk.types.Method 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 kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest +import kotlinx.schema.json.ObjectPropertyDefinition import org.awaitility.kotlin.await import org.awaitility.kotlin.untilAsserted import org.junit.jupiter.api.Test @@ -32,7 +32,7 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() { } // Add a tool - server.addTool("test-tool", "Test Tool", ToolSchema()) { + server.addTool("test-tool", "Test Tool", ObjectPropertyDefinition()) { CallToolResult(listOf(TextContent("Test result"))) } diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt index 1b3df9179..9111ff813 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt @@ -9,9 +9,9 @@ import io.modelcontextprotocol.kotlin.sdk.types.Method 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 kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest +import kotlinx.schema.json.ObjectPropertyDefinition import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals @@ -34,7 +34,7 @@ class ServerToolsTest : AbstractServerFeaturesTest() { } // Add a tool - server.addTool("test-tool", "Test Tool", ToolSchema()) { + server.addTool("test-tool", "Test Tool", ObjectPropertyDefinition()) { CallToolResult(listOf(TextContent("Test result"))) } @@ -103,7 +103,7 @@ class ServerToolsTest : AbstractServerFeaturesTest() { @Test fun `addTool should succeed with non-conforming tool name`() = runTest { - server.addTool("my invalid tool!", "Tool with non-conforming name", ToolSchema()) { + server.addTool("my invalid tool!", "Tool with non-conforming name", ObjectPropertyDefinition()) { CallToolResult(listOf(TextContent("It works"))) } diff --git a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt index 7775a08f3..b8dc406ec 100644 --- a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt +++ b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt @@ -11,8 +11,7 @@ import io.modelcontextprotocol.kotlin.sdk.types.SamplingMessage import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.Tool import io.modelcontextprotocol.kotlin.sdk.types.ToolChoice -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema -import kotlinx.serialization.json.buildJsonObject +import kotlinx.schema.json.ObjectPropertyDefinition import org.junit.jupiter.api.assertDoesNotThrow import kotlin.test.Test import kotlin.test.assertEquals @@ -22,7 +21,7 @@ class ClientSamplingValidationTest { private val dummyTool = Tool( name = "t", - inputSchema = ToolSchema(properties = buildJsonObject { }, required = emptyList()), + inputSchema = ObjectPropertyDefinition(properties = emptyMap(), required = emptyList()), ) private val minimalMessages = listOf(SamplingMessage(Role.User, TextContent("hi"))) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 28d67372a..5a7a1d132 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -5331,28 +5331,28 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/TitledSingleSelectEn public final class io/modelcontextprotocol/kotlin/sdk/types/Tool : io/modelcontextprotocol/kotlin/sdk/types/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/Tool$Companion; - public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; + public final fun component2 ()Lkotlinx/schema/json/ObjectPropertyDefinition; public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; + public final fun component4 ()Lkotlinx/schema/json/ObjectPropertyDefinition; public final fun component5 ()Ljava/lang/String; public final fun component6 ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations; public final fun component7 ()Ljava/util/List; public final fun component8 ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution; public final fun component9 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/Tool; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/Tool;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/Tool; + public final fun copy (Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/Tool; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/Tool;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/Tool; public fun equals (Ljava/lang/Object;)Z public final fun getAnnotations ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations; public final fun getDescription ()Ljava/lang/String; public final fun getExecution ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution; public final fun getIcons ()Ljava/util/List; - public final fun getInputSchema ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; + public final fun getInputSchema ()Lkotlinx/schema/json/ObjectPropertyDefinition; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getName ()Ljava/lang/String; - public final fun getOutputSchema ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; + public final fun getOutputSchema ()Lkotlinx/schema/json/ObjectPropertyDefinition; public final fun getTitle ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -5550,42 +5550,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ToolResultContent$Co public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema$Companion; - public fun ()V - public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lkotlinx/serialization/json/JsonObject; - public final fun component3 ()Ljava/util/List; - public final fun component4 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; - public fun equals (Ljava/lang/Object;)Z - public final fun getDefs ()Lkotlinx/serialization/json/JsonObject; - public final fun getProperties ()Lkotlinx/serialization/json/JsonObject; - public final fun getRequired ()Ljava/util/List; - public final fun getSchema ()Ljava/lang/String; - public final fun getType ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema$$serializer; - public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; -} - -public final class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public final class io/modelcontextprotocol/kotlin/sdk/types/ToolUseContent : io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V diff --git a/kotlin-sdk-core/build.gradle.kts b/kotlin-sdk-core/build.gradle.kts index 1d19f5d3e..b8d03b161 100644 --- a/kotlin-sdk-core/build.gradle.kts +++ b/kotlin-sdk-core/build.gradle.kts @@ -57,6 +57,7 @@ kotlin { api(libs.kotlinx.coroutines.core) api(libs.kotlinx.io.core) api(libs.kotlinx.collections.immutable) + api(libs.kotlinx.schema.json) implementation(libs.ktor.server.websockets) implementation(libs.kotlin.logging) } diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tools.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tools.kt index 1f0f54e3c..346aca8a0 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tools.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tools.kt @@ -2,6 +2,7 @@ package io.modelcontextprotocol.kotlin.sdk.types +import kotlinx.schema.json.ObjectPropertyDefinition import kotlinx.serialization.EncodeDefault import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName @@ -39,14 +40,14 @@ public fun CallToolResult.Companion.error(content: String, meta: JsonObject? = n * if both [title] and [annotations].[title] are not provided. * @property inputSchema A JSON Schema object defining the expected parameters for the tool. * Must be an object type schema. Defines what arguments the tool accepts. - * Defaults to the JSON Schema 2020-12 dialect when no explicit `$schema` is provided. + * Uses the JSON Schema 2020-12 dialect. * @property description A human-readable description of the tool and when to use it. * Clients can use this to improve the LLM's understanding of available tools. * It can be thought of like a "hint" to the model. * @property outputSchema An optional JSON Schema object defining the structure of the tool's output * returned in the [structuredContent][CallToolResult.structuredContent] field of a [CallToolResult]. * Must be an object type schema if provided. - * Defaults to the JSON Schema 2020-12 dialect when no explicit `$schema` is provided. + * Uses the JSON Schema 2020-12 dialect. * @property title Optional human-readable display name for this tool. * Intended for UI and end-user contexts, optimized to be easily understood * even by those unfamiliar with domain-specific terminology. @@ -64,9 +65,9 @@ public fun CallToolResult.Companion.error(content: String, meta: JsonObject? = n @Serializable public data class Tool( val name: String, - val inputSchema: ToolSchema, + val inputSchema: ObjectPropertyDefinition, val description: String? = null, - val outputSchema: ToolSchema? = null, + val outputSchema: ObjectPropertyDefinition? = null, val title: String? = null, val annotations: ToolAnnotations? = null, val icons: List? = null, @@ -75,34 +76,6 @@ public data class Tool( override val meta: JsonObject? = null, ) : WithMeta -/** - * A JSON Schema for tool input or output parameters. - * - * This is a simplified schema structure that must be of type "object". - * Defaults to the JSON Schema 2020-12 dialect when no explicit [schema] (`$schema`) is provided. - * - * @property schema Optional URI identifying the JSON Schema dialect (e.g., - * `https://json-schema.org/draft/2020-12/schema`). Serialized as `$schema`. - * When absent, JSON Schema 2020-12 is assumed by default. - * @property properties Optional map of property names to their schema definitions. - * @property required Optional list of property names that are required. - * @property defs Optional schema definitions available to references in [properties]. Serialized as `$defs`. - */ -@Serializable -public data class ToolSchema( - @SerialName("\$schema") - val schema: String? = null, - val properties: JsonObject? = null, - val required: List? = null, - @SerialName("\$defs") - val defs: JsonObject? = null, -) { - /** Always `"object"` for tool schemas. */ - @OptIn(ExperimentalSerializationApi::class) - @EncodeDefault - val type: String = "object" -} - /** * Execution-related properties for a tool. * diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt index b7bc5cf08..3893d2058 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt @@ -4,6 +4,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization import io.modelcontextprotocol.kotlin.test.utils.verifySerialization +import kotlinx.schema.json.ObjectPropertyDefinition import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException import kotlinx.serialization.json.JsonObject @@ -29,8 +30,8 @@ class SamplingTest { private val dummyTool = Tool( name = "get_weather", description = "returns weather", - inputSchema = ToolSchema( - properties = buildJsonObject { }, + inputSchema = ObjectPropertyDefinition( + properties = emptyMap(), required = emptyList(), ), ) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt index 358866a10..0ea92ae25 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt @@ -2,6 +2,11 @@ package io.modelcontextprotocol.kotlin.sdk.types import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization import io.modelcontextprotocol.kotlin.test.utils.verifySerialization +import kotlinx.schema.json.ArrayPropertyDefinition +import kotlinx.schema.json.NumericPropertyDefinition +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.ReferencePropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.Json import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.int @@ -19,7 +24,7 @@ class ToolsTest { fun `should serialize Tool with minimal fields`() { val tool = Tool( name = "search", - inputSchema = ToolSchema(), + inputSchema = ObjectPropertyDefinition(), ) verifySerialization( @@ -38,27 +43,16 @@ class ToolsTest { @Test fun `should serialize Tool with annotations and schemas`() { - val inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "query", - buildJsonObject { - put("type", "string") - put("description", "Search query") - }, - ) - }, + val inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "query" to StringPropertyDefinition(description = "Search query"), + ), required = listOf("query"), ) - val outputSchema = ToolSchema( - properties = buildJsonObject { - put( - "results", - buildJsonObject { - put("type", "array") - }, - ) - }, + val outputSchema = ObjectPropertyDefinition( + properties = mapOf( + "results" to ArrayPropertyDefinition(), + ), ) val tool = Tool( name = "web-search", @@ -158,69 +152,14 @@ class ToolsTest { } @Test - fun `should serialize ToolSchema with schema field`() { - val tool = Tool( - name = "typed-tool", - inputSchema = ToolSchema( - schema = "https://json-schema.org/draft/2020-12/schema", - properties = buildJsonObject { - put("name", buildJsonObject { put("type", "string") }) - }, - ), - ) - - verifySerialization( - tool, - McpJson, - $$""" - { - "name": "typed-tool", - "inputSchema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - } - } - """.trimIndent(), - ) - } - - @Test - fun `should deserialize ToolSchema with schema field`() { - val json = $$""" - { - "name": "typed-tool", - "inputSchema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - } - } - """.trimIndent() - - val tool = verifyDeserialization(McpJson, json) - - assertEquals("https://json-schema.org/draft/2020-12/schema", tool.inputSchema.schema) - assertNotNull(tool.inputSchema.properties) - } - - @Test - fun `should serialize ToolSchema with defs`() { + fun `should serialize ObjectPropertyDefinition with defs`() { val tool = toolWithDefs() verifySerialization(tool, McpJson, toolWithDefsJson()) } @Test - fun `should deserialize ToolSchema with defs`() { + fun `should deserialize ObjectPropertyDefinition with defs`() { val json = $$""" { "name": "create-page", @@ -251,12 +190,12 @@ class ToolsTest { val schema = tool.inputSchema val defs = schema.defs assertNotNull(defs) - val parentRequest = defs["parentRequest"]?.jsonObject - assertNotNull(parentRequest) - assertEquals("object", parentRequest["type"]?.jsonPrimitive?.content) + val parentRequest = assertIs(defs["parentRequest"]) + assertEquals(listOf("object"), parentRequest.type) + val parent = assertIs(schema.properties?.get("parent")) assertEquals( $$"#/$defs/parentRequest", - schema.properties?.get("parent")?.jsonObject?.get($$"$ref")?.jsonPrimitive?.content, + parent.ref, ) assertEquals(listOf("parent"), schema.required) } @@ -487,8 +426,8 @@ class ToolsTest { fun `should serialize ListToolsResult`() { val result = ListToolsResult( tools = listOf( - Tool(name = "search", inputSchema = ToolSchema()), - Tool(name = "summarize", inputSchema = ToolSchema()), + Tool(name = "search", inputSchema = ObjectPropertyDefinition()), + Tool(name = "summarize", inputSchema = ObjectPropertyDefinition()), ), nextCursor = "cursor-2", meta = buildJsonObject { put("page", 1) }, @@ -550,7 +489,7 @@ class ToolsTest { val tool = tools.first() assertEquals("search", tool.name) assertEquals("Search the workspace", tool.description) - assertEquals("object", tool.inputSchema.type) + assertEquals(listOf("object"), tool.inputSchema.type) assertEquals(3, result.meta?.get("page")?.jsonPrimitive?.int) } @@ -632,7 +571,7 @@ class ToolsTest { fun `should serialize Tool with execution`() { val tool = Tool( name = "long-running-task", - inputSchema = ToolSchema(), + inputSchema = ObjectPropertyDefinition(), description = "A tool that supports task-augmented execution", execution = ToolExecution(taskSupport = TaskSupport.Required), ) @@ -707,42 +646,28 @@ class ToolsTest { name = "get_weather", title = "Get weather", description = "Get the current weather in a given location", - inputSchema = ToolSchema( - properties = buildJsonObject { - put( - "location", - buildJsonObject { - put("type", "string") - put("description", "The city and state, e.g. San Francisco, CA") - }, - ) - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "location" to StringPropertyDefinition( + description = "The city and state, e.g. San Francisco, CA", + ), + ), required = listOf("location"), ), - outputSchema = ToolSchema( - properties = buildJsonObject { - put( - "temperature", - buildJsonObject { - put("type", "number") - put("description", "Temperature in celsius") - }, - ) - put( - "conditions", - buildJsonObject { - put("type", "string") - put("description", "Weather conditions description") - }, - ) - put( - "humidity", - buildJsonObject { - put("type", "number") - put("description", "Humidity percentage") - }, - ) - }, + outputSchema = ObjectPropertyDefinition( + properties = mapOf( + "temperature" to NumericPropertyDefinition( + type = listOf("number"), + description = "Temperature in celsius", + ), + "conditions" to StringPropertyDefinition( + description = "Weather conditions description", + ), + "humidity" to NumericPropertyDefinition( + type = listOf("number"), + description = "Humidity percentage", + ), + ), required = listOf("temperature", "conditions", "humidity"), ), meta = buildJsonObject { put("_for_test_only", true) }, @@ -792,35 +717,18 @@ class ToolsTest { inputSchema = toolSchemaWithDefs(), ) - private fun toolSchemaWithDefs(): ToolSchema = ToolSchema( - properties = buildJsonObject { - put( - "parent", - buildJsonObject { - put($$"$ref", $$"#/$defs/parentRequest") - }, - ) - }, + private fun toolSchemaWithDefs(): ObjectPropertyDefinition = ObjectPropertyDefinition( + properties = mapOf( + "parent" to ReferencePropertyDefinition(ref = $$"#/$defs/parentRequest"), + ), required = listOf("parent"), - defs = buildJsonObject { - put( - "parentRequest", - buildJsonObject { - put("type", "object") - put( - "properties", - buildJsonObject { - put( - "page_id", - buildJsonObject { - put("type", "string") - }, - ) - }, - ) - }, - ) - }, + defs = mapOf( + "parentRequest" to ObjectPropertyDefinition( + properties = mapOf( + "page_id" to StringPropertyDefinition(), + ), + ), + ), ) private fun toolWithDefsJson(): String = $$""" diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index 1ec2efd2f..6d2c374ab 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -121,8 +121,8 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server { public static synthetic fun addResourceTemplate$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function4;ILjava/lang/Object;)V public final fun addResources (Ljava/util/List;)V public final fun addTool (Lio/modelcontextprotocol/kotlin/sdk/types/Tool;Lkotlin/jvm/functions/Function3;)V - public final fun addTool (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/functions/Function3;)V - public static synthetic fun addTool$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)V + public final fun addTool (Ljava/lang/String;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/functions/Function3;)V + public static synthetic fun addTool$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Ljava/lang/String;Lkotlinx/schema/json/ObjectPropertyDefinition;Lio/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations;Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)V public final fun addTools (Ljava/util/List;)V public final fun clientConnection (Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/server/ClientConnection; public final fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index 51a238918..774ec8d3d 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -44,7 +44,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.Tool import io.modelcontextprotocol.kotlin.sdk.types.ToolAnnotations import io.modelcontextprotocol.kotlin.sdk.types.ToolExecution -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import io.modelcontextprotocol.kotlin.sdk.types.UnsubscribeRequest import io.modelcontextprotocol.kotlin.sdk.types.UrlElicitationRequiredException import io.modelcontextprotocol.kotlin.sdk.utils.MatchResult @@ -54,6 +53,7 @@ import io.modelcontextprotocol.kotlin.sdk.utils.ResourceTemplateMatcherFactory import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers +import kotlinx.schema.json.ObjectPropertyDefinition import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put @@ -333,9 +333,9 @@ public open class Server( public fun addTool( name: String, description: String, - inputSchema: ToolSchema = ToolSchema(), + inputSchema: ObjectPropertyDefinition = ObjectPropertyDefinition(), title: String? = null, - outputSchema: ToolSchema? = null, + outputSchema: ObjectPropertyDefinition? = null, toolAnnotations: ToolAnnotations? = null, execution: ToolExecution? = null, meta: JsonObject? = null, diff --git a/kotlin-sdk-server/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransportTest.kt b/kotlin-sdk-server/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransportTest.kt index bb522d6cb..19defb217 100644 --- a/kotlin-sdk-server/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransportTest.kt +++ b/kotlin-sdk-server/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransportTest.kt @@ -51,7 +51,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.RPCError import io.modelcontextprotocol.kotlin.sdk.types.RequestId import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.Tool -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import io.modelcontextprotocol.kotlin.sdk.types.toJSON import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope @@ -63,6 +62,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout +import kotlinx.schema.json.ObjectPropertyDefinition import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put @@ -349,7 +349,7 @@ class StreamableHttpServerTransportTest { val firstResult = ListToolsResult( tools = listOf( - Tool(name = "tool-1", inputSchema = ToolSchema()), + Tool(name = "tool-1", inputSchema = ObjectPropertyDefinition()), ), meta = buildJsonObject { put("label", "first") }, ) diff --git a/samples/kotlinlang-mcp-server/src/main/kotlin/org/kotlinlang/mcp/KotlinlangServer.kt b/samples/kotlinlang-mcp-server/src/main/kotlin/org/kotlinlang/mcp/KotlinlangServer.kt index 081434295..dbc046cf0 100644 --- a/samples/kotlinlang-mcp-server/src/main/kotlin/org/kotlinlang/mcp/KotlinlangServer.kt +++ b/samples/kotlinlang-mcp-server/src/main/kotlin/org/kotlinlang/mcp/KotlinlangServer.kt @@ -8,6 +8,8 @@ import io.ktor.serialization.kotlinx.json.* import io.modelcontextprotocol.kotlin.sdk.server.Server import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions import io.modelcontextprotocol.kotlin.sdk.types.* +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.* import org.kotlinlang.mcp.algolia.AlgoliaClient import org.kotlinlang.mcp.cache.TtlCache @@ -54,17 +56,13 @@ internal class KotlinlangServer(config: ServerConfig) : Closeable { "other topic covered in the official Kotlin documentation. Returns up to 5 results with page " + "titles, paths, and text snippets. To get the full content of a specific page, use the " + "get_kotlinlang_page tool with the page path from the search results.", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("query") { - put("type", "string") - put( - "description", - "A search query to find relevant Kotlin documentation pages " + - "(e.g. 'coroutines', 'sealed classes', 'multiplatform setup')", - ) - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "query" to StringPropertyDefinition( + description = "A search query to find relevant Kotlin documentation pages " + + "(e.g. 'coroutines', 'sealed classes', 'multiplatform setup')", + ), + ), required = listOf("query"), ), toolAnnotations = ToolAnnotations(readOnlyHint = true, openWorldHint = true), @@ -85,19 +83,15 @@ internal class KotlinlangServer(config: ServerConfig) : Closeable { "page path (e.g., from search results) and need the complete content of that page rather " + "than just a snippet. If the page is not found, use search_kotlinlang to discover the " + "correct path.", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("path") { - put("type", "string") - put( - "description", - "Page path relative to /docs/, without extension. Use the page paths " + - "returned from search_kotlinlang results " + - "(e.g. 'coroutines-overview', " + - "'multiplatform/compose-multiplatform-and-jetpack-compose')", - ) - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "path" to StringPropertyDefinition( + description = "Page path relative to /docs/, without extension. Use the page paths " + + "returned from search_kotlinlang results " + + "(e.g. 'coroutines-overview', " + + "'multiplatform/compose-multiplatform-and-jetpack-compose')", + ), + ), required = listOf("path"), ), toolAnnotations = ToolAnnotations(readOnlyHint = true, openWorldHint = true), diff --git a/samples/simple-streamable-server/src/main/kotlin/io/modelcontextprotocol/sample/server/server.kt b/samples/simple-streamable-server/src/main/kotlin/io/modelcontextprotocol/sample/server/server.kt index cdffdae7f..9a4876e8c 100644 --- a/samples/simple-streamable-server/src/main/kotlin/io/modelcontextprotocol/sample/server/server.kt +++ b/samples/simple-streamable-server/src/main/kotlin/io/modelcontextprotocol/sample/server/server.kt @@ -40,13 +40,11 @@ import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents import io.modelcontextprotocol.kotlin.sdk.types.ToolAnnotations -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import kotlinx.coroutines.delay +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonObject import kotlin.time.Duration.Companion.milliseconds private const val MCP_SESSION_ID_HEADER = "mcp-session-id" @@ -193,13 +191,8 @@ private fun createMcpServer(): Server { server.addTool( name = "greet", description = "A simple greeting tool", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("name") { - put("type", "string") - put("description", "Name to greet") - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf("name" to StringPropertyDefinition(description = "Name to greet")), required = listOf("name"), ), ) { request -> @@ -211,13 +204,8 @@ private fun createMcpServer(): Server { server.addTool( name = "multi-greet", description = "A tool that sends different greetings with delays between them", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("name") { - put("type", "string") - put("description", "Name to greet") - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf("name" to StringPropertyDefinition(description = "Name to greet")), required = listOf("name"), ), toolAnnotations = ToolAnnotations(readOnlyHint = true, openWorldHint = false), diff --git a/samples/weather-stdio-server/src/main/kotlin/io/modelcontextprotocol/sample/server/McpWeatherServer.kt b/samples/weather-stdio-server/src/main/kotlin/io/modelcontextprotocol/sample/server/McpWeatherServer.kt index 1dee706f7..68774caec 100644 --- a/samples/weather-stdio-server/src/main/kotlin/io/modelcontextprotocol/sample/server/McpWeatherServer.kt +++ b/samples/weather-stdio-server/src/main/kotlin/io/modelcontextprotocol/sample/server/McpWeatherServer.kt @@ -17,17 +17,16 @@ import io.modelcontextprotocol.kotlin.sdk.types.Implementation import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.types.TextContent import io.modelcontextprotocol.kotlin.sdk.types.ToolAnnotations -import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema import kotlinx.coroutines.Job import kotlinx.coroutines.runBlocking import kotlinx.io.asSink import kotlinx.io.buffered +import kotlinx.schema.json.NumericPropertyDefinition +import kotlinx.schema.json.ObjectPropertyDefinition +import kotlinx.schema.json.StringPropertyDefinition import kotlinx.serialization.json.Json -import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.doubleOrNull import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonObject /** * Starts an MCP server that provides weather-related tools for fetching active @@ -87,13 +86,10 @@ private fun Server.registerTools(httpClient: HttpClient) { addTool( name = "get_alerts", description = "Get weather alerts for a US state. Input is a two-letter US state code (e.g. CA, NY)", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("state") { - put("type", "string") - put("description", "Two-letter US state code (e.g. CA, NY)") - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "state" to StringPropertyDefinition(description = "Two-letter US state code (e.g. CA, NY)"), + ), required = listOf("state"), ), toolAnnotations = ToolAnnotations(readOnlyHint = true, openWorldHint = true), @@ -134,21 +130,21 @@ private fun Server.registerTools(httpClient: HttpClient) { addTool( name = "get_forecast", description = "Get weather forecast for a location. Note: only US locations are supported by the NWS API.", - inputSchema = ToolSchema( - properties = buildJsonObject { - putJsonObject("latitude") { - put("type", "number") - put("description", "Latitude of the location") - put("minimum", -90) - put("maximum", 90) - } - putJsonObject("longitude") { - put("type", "number") - put("description", "Longitude of the location") - put("minimum", -180) - put("maximum", 180) - } - }, + inputSchema = ObjectPropertyDefinition( + properties = mapOf( + "latitude" to NumericPropertyDefinition( + type = listOf("number"), + description = "Latitude of the location", + minimum = -90.0, + maximum = 90.0, + ), + "longitude" to NumericPropertyDefinition( + type = listOf("number"), + description = "Longitude of the location", + minimum = -180.0, + maximum = 180.0, + ), + ), required = listOf("latitude", "longitude"), ), toolAnnotations = ToolAnnotations(readOnlyHint = true, openWorldHint = true),