Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
87 changes: 82 additions & 5 deletions api/kotlin-sdk.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
public final class io/modelcontextprotocol/kotlin/sdk/AudioContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/AudioContent$Companion;
public static final field TYPE Ljava/lang/String;
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/AudioContent;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/AudioContent;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/AudioContent;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Ljava/lang/String;
public final fun getMimeType ()Ljava/lang/String;
public fun getType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public synthetic class io/modelcontextprotocol/kotlin/sdk/AudioContent$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/AudioContent$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/AudioContent;
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/AudioContent;)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/AudioContent$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/BlobResourceContents : io/modelcontextprotocol/kotlin/sdk/ResourceContents {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/BlobResourceContents$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
Expand Down Expand Up @@ -1665,6 +1696,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Compa
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion;
}

public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentTextOrImage : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentTextOrImage$Companion;
}
Expand Down Expand Up @@ -2404,13 +2443,15 @@ public final class io/modelcontextprotocol/kotlin/sdk/TextResourceContents$Compa

public final class io/modelcontextprotocol/kotlin/sdk/Tool {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Tool$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/Tool;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/Tool;
public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;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;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;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;
Expand Down Expand Up @@ -2465,6 +2506,42 @@ public final class io/modelcontextprotocol/kotlin/sdk/Tool$Input$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/ToolAnnotations {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/Boolean;
public final fun component3 ()Ljava/lang/Boolean;
public final fun component4 ()Ljava/lang/Boolean;
public final fun component5 ()Ljava/lang/Boolean;
public final fun copy (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;)Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
public fun equals (Ljava/lang/Object;)Z
public final fun getDestructiveHint ()Ljava/lang/Boolean;
public final fun getIdempotentHint ()Ljava/lang/Boolean;
public final fun getOpenWorldHint ()Ljava/lang/Boolean;
public final fun getReadOnlyHint ()Ljava/lang/Boolean;
public final fun getTitle ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public synthetic class io/modelcontextprotocol/kotlin/sdk/ToolAnnotations$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;
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/ToolAnnotations;)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/ToolAnnotations$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Companion;
public fun <init> ()V
Expand Down Expand Up @@ -2778,8 +2855,8 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server : io/modelcontextp
public final fun addResource (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)V
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 (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;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;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
public final fun addTool (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Tool$Input;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;Lio/modelcontextprotocol/kotlin/sdk/ToolAnnotations;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
public final fun addTools (Ljava/util/List;)V
protected fun assertCapabilityForMethod (Lio/modelcontextprotocol/kotlin/sdk/Method;)V
protected fun assertNotificationCapability (Lio/modelcontextprotocol/kotlin/sdk/Method;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,15 @@ public open class Server(
name: String,
description: String,
inputSchema: Tool.Input = Tool.Input(),
toolAnnotations: ToolAnnotations? = null,
handler: suspend (CallToolRequest) -> CallToolResult
) {
if (capabilities.tools == null) {
logger.error { "Failed to add tool '$name': Server does not support tools capability" }
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)
}

/**
Expand Down
57 changes: 57 additions & 0 deletions src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,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.
*/
Expand All @@ -1010,6 +1062,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(
Expand Down
2 changes: 2 additions & 0 deletions src/commonTest/kotlin/ToolSerializationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions src/jvmTest/kotlin/client/ClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ class ClientTest {
Tool(
name = "testTool",
description = "testTool description",
annotations = null,
inputSchema = Tool.Input()
)
), nextCursor = null
Expand Down
Loading