From ae91c991168e7b661433a27278318afc3028fd04 Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Wed, 9 Apr 2025 16:04:12 +0800 Subject: [PATCH] feat: add tool annotations --- .../kotlin/sdk/server/Server.kt | 3 +- .../modelcontextprotocol/kotlin/sdk/types.kt | 57 +++++++++++++++++++ .../kotlin/ToolSerializationTest.kt | 2 + src/jvmTest/kotlin/client/ClientTest.kt | 1 + 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index e24419e..f5a603c 100644 --- a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -145,6 +145,7 @@ public open class Server( name: String, description: String, inputSchema: Tool.Input = Tool.Input(), + toolAnnotations: ToolAnnotations? = null, handler: suspend (CallToolRequest) -> CallToolResult ) { if (capabilities.tools == null) { @@ -152,7 +153,7 @@ public open class Server( throw IllegalStateException("Server does not support tools capability. Enable it in ServerOptions.") } logger.info { "Registering tool: $name" } - tools[name] = RegisteredTool(Tool(name, description, inputSchema), handler) + tools[name] = RegisteredTool(Tool(name, description, inputSchema, toolAnnotations), handler) } /** diff --git a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index d2fa2cb..88c1643 100644 --- a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -991,6 +991,58 @@ public class PromptListChangedNotification : ServerNotification { } /* Tools */ +/** + * Additional properties describing a Tool to clients. + * + * NOTE: all properties in ToolAnnotations are **hints**. + * They are not guaranteed to provide a faithful description of + * tool behavior (including descriptive properties like `title`). + * + * Clients should never make tool use decisions based on ToolAnnotations + * received from untrusted servers. + */ +@Serializable +public data class ToolAnnotations( + /** + * A human-readable title for the tool. + */ + val title: String?, + /** + * If true, the tool does not modify its environment. + * + * Default: false + */ + val readOnlyHint: Boolean? = false, + /** + * If true, the tool may perform destructive updates to its environment. + * If false, the tool performs only additive updates. + * + * (This property is meaningful only when `readOnlyHint == false`) + * + * Default: true + */ + val destructiveHint: Boolean? = true, + /** + * If true, calling the tool repeatedly with the same arguments + * will have no additional effect on the its environment. + * + * (This property is meaningful only when `readOnlyHint == false`) + * + * Default: false + */ + val idempotentHint: Boolean? = false, + /** + * If true, this tool may interact with an "open world" of external + * entities. If false, the tool's domain of interaction is closed. + * For example, the world of a web search tool is open, whereas that + * of a memory tool is not. + * + * Default: true + */ + val openWorldHint: Boolean? = true, +) + + /** * Definition for a tool the client can call. */ @@ -1008,6 +1060,11 @@ public data class Tool( * A JSON object defining the expected parameters for the tool. */ val inputSchema: Input, + + /** + * Optional additional tool information. + */ + val annotations: ToolAnnotations?, ) { @Serializable public data class Input( diff --git a/src/commonTest/kotlin/ToolSerializationTest.kt b/src/commonTest/kotlin/ToolSerializationTest.kt index 90c5c10..cc32290 100644 --- a/src/commonTest/kotlin/ToolSerializationTest.kt +++ b/src/commonTest/kotlin/ToolSerializationTest.kt @@ -2,6 +2,7 @@ package io.modelcontextprotocol.kotlin.sdk import io.kotest.assertions.json.shouldEqualJson import io.modelcontextprotocol.kotlin.sdk.shared.McpJson +import kotlinx.atomicfu.atomicArrayOfNulls import kotlinx.serialization.encodeToString import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.buildJsonObject @@ -32,6 +33,7 @@ class ToolSerializationTest { val getWeatherTool = Tool( name = "get_weather", description = "Get the current weather in a given location", + annotations = null, inputSchema = Tool.Input( properties = buildJsonObject { put("location", buildJsonObject { diff --git a/src/jvmTest/kotlin/client/ClientTest.kt b/src/jvmTest/kotlin/client/ClientTest.kt index ef60fb9..31c5bcc 100644 --- a/src/jvmTest/kotlin/client/ClientTest.kt +++ b/src/jvmTest/kotlin/client/ClientTest.kt @@ -523,6 +523,7 @@ class ClientTest { Tool( name = "testTool", description = "testTool description", + annotations = null, inputSchema = Tool.Input() ) ), nextCursor = null