feat: add tool annotations according to 2025-03-26 spec (#71)

* feat: add tool annotations

* chore: commit api dump

* fix import and api

---------

Co-authored-by: devcrocod <devcrocod@gmail.com>
Co-authored-by: Leonid Stashevsky <e5l@users.noreply.github.com>
This commit is contained in:
SeanChinJunKai 2025-07-07 14:52:16 +08:00 • committed by GitHub
commit 89c20379ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 105 additions and 7 deletions

View file

@ -39,6 +39,7 @@ import io.modelcontextprotocol.kotlin.sdk.ResourceUpdatedNotification
import io.modelcontextprotocol.kotlin.sdk.SUPPORTED_PROTOCOL_VERSIONS
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.Tool
import io.modelcontextprotocol.kotlin.sdk.ToolAnnotations
import io.modelcontextprotocol.kotlin.sdk.ToolListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.shared.Protocol
import io.modelcontextprotocol.kotlin.sdk.shared.ProtocolOptions
@ -183,6 +184,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) {
@ -190,7 +192,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)
}
/**

View file

@ -1031,6 +1031,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.
*/
@ -1048,6 +1100,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(

View file

@ -2,7 +2,6 @@ package io.modelcontextprotocol.kotlin.sdk
import io.kotest.assertions.json.shouldEqualJson
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
@ -33,6 +32,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 {

View file

@ -583,6 +583,7 @@ class ClientTest {
Tool(
name = "testTool",
description = "testTool description",
annotations = null,
inputSchema = Tool.Input()
)
), nextCursor = null