feat: add $schema field to ToolSchema per SEP-1613 (#696)

Add optional `$schema` field to `ToolSchema`, establishing JSON Schema
2020-12 as the default dialect per SEP-1613.

closes #422 

## How Has This Been Tested?
unit tests 

## Breaking Changes
none

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
This commit is contained in:
Pavel Gorgulov 2026-04-14 13:44:19 +02:00 • committed by GitHub
commit 61fab2ffe0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 72 additions and 7 deletions

View file

@ -5135,17 +5135,19 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ToolListChangedNotif
public final class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun component2 ()Ljava/util/List;
public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (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;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (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;

View file

@ -39,12 +39,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.
* @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.
* @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.
@ -77,7 +79,11 @@ public data class Tool(
* 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 type Always "object" for tool schemas.
* @property properties Optional map of property names to their schema definitions.
* @property required Optional list of property names that are required.
@ -85,6 +91,8 @@ public data class Tool(
*/
@Serializable
public data class ToolSchema(
@SerialName("\$schema")
val schema: String? = null,
val properties: JsonObject? = null,
val required: List<String>? = null,
@SerialName("\$defs")

View file

@ -158,6 +158,61 @@ class ToolsTest {
assertEquals("language", tool.meta?.get("category")?.jsonPrimitive?.content)
}
@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<Tool>(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`() {
val tool = toolWithDefs()