Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ internal class StreamableHttpClientTest : AbstractStreamableHttpClientTest() {
}
},
"required": ["temperature"]
}
},
"_meta": {}
}
]
}
Expand All @@ -124,10 +125,10 @@ internal class StreamableHttpClientTest : AbstractStreamableHttpClientTest() {

val listToolsResult = client.listTools()

listToolsResult.tools shouldContain Tool(
name = "get_weather",
title = "Weather Information Provider",
description = "Get current weather information for a location",
listToolsResult.tools shouldContain Tool.build {
name = "get_weather"
title = "Weather Information Provider"
description = "Get current weather information for a location"
inputSchema = Tool.Input(
properties = buildJsonObject {
putJsonObject("location") {
Expand All @@ -136,7 +137,7 @@ internal class StreamableHttpClientTest : AbstractStreamableHttpClientTest() {
}
},
required = listOf("location"),
),
)
outputSchema = Tool.Output(
properties = buildJsonObject {
putJsonObject("temperature") {
Expand All @@ -145,9 +146,8 @@ internal class StreamableHttpClientTest : AbstractStreamableHttpClientTest() {
}
},
required = listOf("temperature"),
),
annotations = null,
)
)
}

mockMcp.mockUnsubscribeRequest(sessionId = sessionId)

Expand Down
31 changes: 27 additions & 4 deletions kotlin-sdk-core/api/kotlin-sdk-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -2889,24 +2889,27 @@ public final class io/modelcontextprotocol/kotlin/sdk/TextResourceContents$Compa
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/Tool {
public final class io/modelcontextprotocol/kotlin/sdk/Tool : io/modelcontextprotocol/kotlin/sdk/WithMeta {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Tool$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;
public final fun component5 ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;
public final fun component6 ()Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/Tool;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public final fun component7 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/Tool;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public fun equals (Ljava/lang/Object;)Z
public final fun getAnnotations ()Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
public final fun getDescription ()Ljava/lang/String;
public final fun getInputSchema ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;
public final fun getName ()Ljava/lang/String;
public final fun getOutputSchema ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;
public final fun getTitle ()Ljava/lang/String;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand All @@ -2922,7 +2925,27 @@ public final synthetic class io/modelcontextprotocol/kotlin/sdk/Tool$$serializer
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/Tool$Builder {
public fun <init> ()V
public final fun build ()Lio/modelcontextprotocol/kotlin/sdk/Tool;
public final fun getAnnotations ()Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
public final fun getDescription ()Ljava/lang/String;
public final fun getInputSchema ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;
public final fun getName ()Ljava/lang/String;
public final fun getOutputSchema ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;
public final fun getTitle ()Ljava/lang/String;
public final fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public final fun setAnnotations (Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;)V
public final fun setDescription (Ljava/lang/String;)V
public final fun setInputSchema (Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;)V
public final fun setName (Ljava/lang/String;)V
public final fun setOutputSchema (Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;)V
public final fun setTitle (Ljava/lang/String;)V
public final fun set_meta (Lkotlinx/serialization/json/JsonObject;)V
}

public final class io/modelcontextprotocol/kotlin/sdk/Tool$Companion {
public final fun build (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,12 @@ public data class Tool(
* Optional additional tool information.
*/
val annotations: ToolAnnotations?,
) {

/**
* Optional metadata for the tool.
*/
override val _meta: JsonObject = EmptyJsonObject,
) : WithMeta {
@Serializable
public data class Input(val properties: JsonObject = EmptyJsonObject, val required: List<String>? = null) {
@OptIn(ExperimentalSerializationApi::class)
Expand All @@ -1256,6 +1261,32 @@ public data class Tool(
@EncodeDefault
val type: String = "object"
}

public class Builder {
public var name: String? = null
public var title: String? = null
public var description: String? = null
public var inputSchema: Input = Input()
public var outputSchema: Output? = null
public var annotations: ToolAnnotations? = null

@Suppress("PropertyName")
public var _meta: JsonObject = EmptyJsonObject

public fun build(): Tool = Tool(
name = requireNotNull(name) { "Tool name is required" },
title = title,
description = description,
inputSchema = inputSchema,
outputSchema = outputSchema,
annotations = annotations,
_meta = _meta,
)
}

public companion object {
public fun build(builder: Builder.() -> Unit): Tool = Builder().apply(builder).build()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adding a builder in this PR feels a bit out of place compared to the rest of the API

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that was a PR suggestion from @kpavlov . I can revert the builder in order to get the too descriptor _meta field merged. Please advise @devcrocod

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devcrocod I went ahead and removed the builder. If its still desired, it can be, more appropriately, in a separate PR.

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class RequestSerializerTest {
"inputSchema": {
"type": "object",
"properties": {}
}
},
"_meta": {}
}
]
}"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.modelcontextprotocol.kotlin.sdk
import io.kotest.assertions.json.shouldEqualJson
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlin.test.Test
Expand Down Expand Up @@ -44,6 +45,9 @@ class ToolSerializationTest {
}
},
"required": ["temperature", "conditions", "humidity"]
},
"_meta": {
"_for_test_only": true
}
}
""".trimIndent()
Expand Down Expand Up @@ -91,6 +95,9 @@ class ToolSerializationTest {
},
required = listOf("temperature", "conditions", "humidity"),
),
_meta = buildJsonObject {
put("_for_test_only", JsonPrimitive(true))
},
)

//region Serialize
Expand Down Expand Up @@ -411,6 +418,7 @@ class ToolSerializationTest {
name: String = "get_weather",
title: String? = null,
outputSchema: String? = null,
@Suppress("LocalVariableName") _meta: String? = null,
): String {
val stringBuilder = StringBuilder()

Expand Down Expand Up @@ -453,6 +461,14 @@ class ToolSerializationTest {
)
}

stringBuilder
.appendLine(",")
.append(
"""
"_meta": ${_meta ?: "{}"}
""".trimIndent(),
)

stringBuilder
.appendLine()
.appendLine("}")
Expand All @@ -464,12 +480,13 @@ class ToolSerializationTest {
name: String = "get_weather",
title: String? = null,
outputSchema: Tool.Output? = null,
): Tool = Tool(
name = name,
title = title,
description = "Get the current weather in a given location",
annotations = null,
inputSchema = Tool.Input(
@Suppress("LocalVariableName") _meta: JsonObject? = null,
): Tool = Tool.build {
this.name = name
title?.let { this.title = it }
this.description = "Get the current weather in a given location"
this.annotations = null
this.inputSchema = Tool.Input(
properties = buildJsonObject {
put(
"location",
Expand All @@ -480,9 +497,10 @@ class ToolSerializationTest {
)
},
required = listOf("location"),
),
outputSchema = outputSchema,
)
)
outputSchema?.let { this.outputSchema = it }
_meta?.let { this._meta = it }
}

//endregion Private Methods
}
4 changes: 2 additions & 2 deletions kotlin-sdk-server/api/kotlin-sdk-server.api
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server {
public static synthetic fun addResource$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
public final fun addResources (Ljava/util/List;)V
public final fun addTool (Lio/modelcontextprotocol/kotlin/sdk/Tool;Lkotlin/jvm/functions/Function2;)V
public final fun addTool (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlin/jvm/functions/Function2;)V
public static synthetic fun addTool$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
public final fun addTool (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/functions/Function2;)V
public static synthetic fun addTool$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Output;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
public final fun addTools (Ljava/util/List;)V
public final fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun connect (Lio/modelcontextprotocol/kotlin/sdk/shared/Transport;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toPersistentSet
import kotlinx.coroutines.CancellationException
import kotlinx.serialization.json.JsonObject

private val logger = KotlinLogging.logger {}

Expand Down Expand Up @@ -219,6 +220,7 @@ public open class Server(
* @param inputSchema The expected input schema for the tool.
* @param outputSchema The optional expected output schema for the tool.
* @param toolAnnotations Optional additional tool information.
* @param _meta Optional metadata as a [JsonObject].
* @param handler A suspend function that handles executing the tool when called by the client.
* @throws IllegalStateException If the server does not support tools.
*/
Expand All @@ -229,9 +231,18 @@ public open class Server(
title: String? = null,
outputSchema: Tool.Output? = null,
toolAnnotations: ToolAnnotations? = null,
@Suppress("LocalVariableName") _meta: JsonObject? = null,
handler: suspend (CallToolRequest) -> CallToolResult,
) {
val tool = Tool(name, title, description, inputSchema, outputSchema, toolAnnotations)
val tool = Tool.build {
this.name = name
this.description = description
this.inputSchema = inputSchema
title?.let { this.title = it }
outputSchema?.let { this.outputSchema = it }
toolAnnotations?.let { this.annotations = it }
_meta?.let { this._meta = it }
}
addTool(tool, handler)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,14 +614,14 @@ class ClientTest {

val serverListToolsResult = ListToolsResult(
tools = listOf(
Tool(
name = "testTool",
title = "testTool title",
description = "testTool description",
annotations = null,
inputSchema = Tool.Input(),
outputSchema = null,
),
Tool.build {
name = "testTool"
title = "testTool title"
description = "testTool description"
annotations = null
inputSchema = Tool.Input()
outputSchema = null
},
),
nextCursor = null,
)
Expand Down