feat(core): add extensions field to ClientCapabilities and ServerCapabilities (#678)
<!-- Provide a brief summary of your changes -->
Add support for MCP extension negotiation during the initialize
handshake, as defined in the MCP extensions specification. Clients and
servers can now advertise supported extensions via a new `extensions:
Map<String, JsonObject>?` field on their respective capabilities
objects.
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
Extensions are supported part of the [MCP spec
](https://modelcontextprotocol.io/extensions/overview) and are available
in other language libraries e.g. [Golang
](862d78a265/mcp/protocol.go (L215)).
## How Has This Been Tested?
Existing and added unit tests
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
Adding a new field to existing ClientCapabilities and ServerCapabilities
fields will break already compiled clients. They will need to recompile
on upgrading.
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] 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
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [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
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
Co-authored-by: Pavel Gorgulov <devcrocod@gmail.com>
This commit is contained in:
parent
f6ad332228
commit
bf8dc6d6b1
6 changed files with 217 additions and 8 deletions
|
|
@ -606,17 +606,19 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CancelledNotificatio
|
|||
public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities {
|
||||
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Companion;
|
||||
public fun <init> ()V
|
||||
public fun <init> (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
|
||||
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun <init> (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)V
|
||||
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;
|
||||
public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun copy (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
|
||||
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
|
||||
public final fun component5 ()Ljava/util/Map;
|
||||
public final fun copy (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
|
||||
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getElicitation ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun getExperimental ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun getExtensions ()Ljava/util/Map;
|
||||
public final fun getRoots ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;
|
||||
public final fun getSampling ()Lkotlinx/serialization/json/JsonObject;
|
||||
public fun hashCode ()I
|
||||
|
|
@ -676,6 +678,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilitiesBu
|
|||
public final fun elicitation (Lkotlinx/serialization/json/JsonObject;)V
|
||||
public final fun experimental (Lkotlin/jvm/functions/Function1;)V
|
||||
public final fun experimental (Lkotlinx/serialization/json/JsonObject;)V
|
||||
public final fun extensions (Ljava/util/Map;)V
|
||||
public final fun roots (Ljava/lang/Boolean;)V
|
||||
public static synthetic fun roots$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilitiesBuilder;Ljava/lang/Boolean;ILjava/lang/Object;)V
|
||||
public final fun sampling (Lkotlin/jvm/functions/Function1;)V
|
||||
|
|
@ -4245,19 +4248,21 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Sampling_dslKt {
|
|||
public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities {
|
||||
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Companion;
|
||||
public fun <init> ()V
|
||||
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
|
||||
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)V
|
||||
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;
|
||||
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;
|
||||
public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;
|
||||
public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun component5 ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun component6 ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
|
||||
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
|
||||
public final fun component7 ()Ljava/util/Map;
|
||||
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
|
||||
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getCompletions ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun getExperimental ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun getExtensions ()Ljava/util/Map;
|
||||
public final fun getLogging ()Lkotlinx/serialization/json/JsonObject;
|
||||
public final fun getPrompts ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;
|
||||
public final fun getResources ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public class ClientCapabilitiesBuilder @PublishedApi internal constructor() {
|
|||
private var sampling: JsonObject? = null
|
||||
private var roots: ClientCapabilities.Roots? = null
|
||||
private var elicitation: JsonObject? = null
|
||||
private var extensions: Map<String, JsonObject>? = null
|
||||
private var experimental: JsonObject? = null
|
||||
|
||||
/**
|
||||
|
|
@ -132,6 +133,30 @@ public class ClientCapabilitiesBuilder @PublishedApi internal constructor() {
|
|||
*/
|
||||
public fun elicitation(block: JsonObjectBuilder.() -> Unit): Unit = elicitation(buildJsonObject(block))
|
||||
|
||||
/**
|
||||
* Defines extensions that the client supports.
|
||||
*
|
||||
* Extension identifiers use the format `{vendor-prefix}/{extension-name}`,
|
||||
* e.g., `"io.modelcontextprotocol/ui"`. Each value is an extension-specific
|
||||
* settings object; an empty [JsonObject] indicates no settings.
|
||||
*
|
||||
* Example:
|
||||
* ```kotlin
|
||||
* capabilities {
|
||||
* extensions(mapOf(
|
||||
* "io.modelcontextprotocol/ui" to buildJsonObject {
|
||||
* put("mimeTypes", JsonArray(listOf(JsonPrimitive("text/html"))))
|
||||
* }
|
||||
* ))
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param value The map of extension identifiers to their settings
|
||||
*/
|
||||
public fun extensions(value: Map<String, JsonObject>) {
|
||||
this.extensions = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines experimental, non-standard capabilities that the client supports.
|
||||
*
|
||||
|
|
@ -177,6 +202,7 @@ public class ClientCapabilitiesBuilder @PublishedApi internal constructor() {
|
|||
sampling = sampling,
|
||||
roots = roots,
|
||||
elicitation = elicitation,
|
||||
extensions = extensions,
|
||||
experimental = experimental,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ public data class Implementation(
|
|||
* @property elicitation Present if the client supports elicitation from the server.
|
||||
* @property experimental Experimental, non-standard capabilities that the client supports.
|
||||
* Keys are capability names, values are capability-specific configuration objects.
|
||||
* @property extensions Optional extensions that the client supports.
|
||||
* Keys are extension identifiers (e.g., `"io.modelcontextprotocol/ui"`),
|
||||
* values are extension-specific settings objects.
|
||||
*/
|
||||
@Serializable
|
||||
public data class ClientCapabilities(
|
||||
|
|
@ -52,6 +55,7 @@ public data class ClientCapabilities(
|
|||
public val roots: Roots? = null,
|
||||
public val elicitation: JsonObject? = null,
|
||||
public val experimental: JsonObject? = null,
|
||||
public val extensions: Map<String, JsonObject>? = null,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
@ -93,6 +97,9 @@ public data class ClientCapabilities(
|
|||
* @property completions Present if the server supports argument autocompletion suggestions.
|
||||
* Keys are capability names, values are capability-specific configuration objects.
|
||||
* @property experimental Experimental, non-standard capabilities that the server supports.
|
||||
* @property extensions Optional extensions that the server supports.
|
||||
* Keys are extension identifiers (e.g., `"io.modelcontextprotocol/ui"`),
|
||||
* values are extension-specific settings objects.
|
||||
*/
|
||||
@Serializable
|
||||
public data class ServerCapabilities(
|
||||
|
|
@ -102,6 +109,7 @@ public data class ServerCapabilities(
|
|||
val logging: JsonObject? = null,
|
||||
val completions: JsonObject? = null,
|
||||
val experimental: JsonObject? = null,
|
||||
val extensions: Map<String, JsonObject>? = null,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -182,6 +182,53 @@ class CapabilitiesTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ClientCapabilities with extensions`() {
|
||||
val extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to buildJsonObject {
|
||||
put("mimeTypes", buildJsonObject { put("text/html", true) })
|
||||
},
|
||||
)
|
||||
val capabilities = ClientCapabilities(
|
||||
extensions = extensions,
|
||||
)
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"extensions": {
|
||||
"io.modelcontextprotocol/ui": {
|
||||
"mimeTypes": {
|
||||
"text/html": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ClientCapabilities with empty extensions settings`() {
|
||||
val capabilities = ClientCapabilities(
|
||||
extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
),
|
||||
)
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"extensions": {
|
||||
"io.modelcontextprotocol/ui": {}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ClientCapabilities with experimental`() {
|
||||
val experimental = buildJsonObject {
|
||||
|
|
@ -215,11 +262,15 @@ class CapabilitiesTest {
|
|||
val experimental = buildJsonObject {
|
||||
put("feature1", buildJsonObject { put("enabled", true) })
|
||||
}
|
||||
val extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
)
|
||||
val capabilities = ClientCapabilities(
|
||||
sampling = ClientCapabilities.sampling,
|
||||
roots = ClientCapabilities.Roots(listChanged = true),
|
||||
elicitation = ClientCapabilities.elicitation,
|
||||
experimental = experimental,
|
||||
extensions = extensions,
|
||||
)
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
|
|
@ -235,6 +286,9 @@ class CapabilitiesTest {
|
|||
"feature1": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"io.modelcontextprotocol/ui": {}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
|
|
@ -270,6 +324,7 @@ class CapabilitiesTest {
|
|||
assertNull(capabilities.roots)
|
||||
assertNull(capabilities.elicitation)
|
||||
assertNull(capabilities.experimental)
|
||||
assertNull(capabilities.extensions)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -278,6 +333,11 @@ class CapabilitiesTest {
|
|||
sampling = ClientCapabilities.sampling,
|
||||
roots = ClientCapabilities.Roots(listChanged = false),
|
||||
elicitation = ClientCapabilities.elicitation,
|
||||
extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to buildJsonObject {
|
||||
put("mimeTypes", buildJsonObject { put("text/html", true) })
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
verifySerializationRoundTrip(original, McpJson)
|
||||
|
|
@ -466,6 +526,32 @@ class CapabilitiesTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ServerCapabilities with extensions`() {
|
||||
val capabilities = ServerCapabilities(
|
||||
extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
"com.example/custom" to buildJsonObject {
|
||||
put("setting", "value")
|
||||
},
|
||||
),
|
||||
)
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"extensions": {
|
||||
"io.modelcontextprotocol/ui": {},
|
||||
"com.example/custom": {
|
||||
"setting": "value"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ServerCapabilities with experimental`() {
|
||||
val experimental = buildJsonObject {
|
||||
|
|
@ -499,6 +585,9 @@ class CapabilitiesTest {
|
|||
val experimental = buildJsonObject {
|
||||
put("feature1", buildJsonObject { put("enabled", true) })
|
||||
}
|
||||
val extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
)
|
||||
val capabilities = ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(listChanged = true),
|
||||
resources = ServerCapabilities.Resources(
|
||||
|
|
@ -509,6 +598,7 @@ class CapabilitiesTest {
|
|||
logging = ServerCapabilities.Logging,
|
||||
completions = ServerCapabilities.Completions,
|
||||
experimental = experimental,
|
||||
extensions = extensions,
|
||||
)
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
|
|
@ -531,6 +621,9 @@ class CapabilitiesTest {
|
|||
"feature1": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"io.modelcontextprotocol/ui": {}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
|
|
@ -578,6 +671,7 @@ class CapabilitiesTest {
|
|||
assertNull(capabilities.logging)
|
||||
assertNull(capabilities.completions)
|
||||
assertNull(capabilities.experimental)
|
||||
assertNull(capabilities.extensions)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -590,6 +684,9 @@ class CapabilitiesTest {
|
|||
),
|
||||
prompts = ServerCapabilities.Prompts(listChanged = true),
|
||||
logging = ServerCapabilities.Logging,
|
||||
extensions = mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
),
|
||||
)
|
||||
|
||||
verifySerializationRoundTrip(original, McpJson)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import io.kotest.matchers.nulls.shouldBeNull
|
|||
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.buildInitializeRequest
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.double
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
|
@ -31,6 +33,7 @@ class CapabilitiesDslTest {
|
|||
roots.shouldBeNull()
|
||||
elicitation.shouldBeNull()
|
||||
experimental.shouldBeNull()
|
||||
extensions.shouldBeNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +59,13 @@ class CapabilitiesDslTest {
|
|||
put("beta", false)
|
||||
put("maxConcurrency", 10)
|
||||
}
|
||||
extensions(
|
||||
mapOf(
|
||||
"io.modelcontextprotocol/ui" to buildJsonObject {
|
||||
put("mimeTypes", "text/html")
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
info("Test", "1.0")
|
||||
}
|
||||
|
|
@ -80,6 +90,11 @@ class CapabilitiesDslTest {
|
|||
get("beta")?.jsonPrimitive?.content shouldBe "false"
|
||||
get("maxConcurrency")?.jsonPrimitive?.content shouldBe "10"
|
||||
}
|
||||
extensions shouldNotBeNull {
|
||||
get("io.modelcontextprotocol/ui") shouldNotBeNull {
|
||||
get("mimeTypes")?.jsonPrimitive?.content shouldBe "text/html"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,6 +125,50 @@ class CapabilitiesDslTest {
|
|||
requestNull.params.capabilities.roots?.listChanged.shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `capabilities should build with extensions containing empty settings`() {
|
||||
val request = buildInitializeRequest {
|
||||
protocolVersion = "2024-11-05"
|
||||
capabilities {
|
||||
extensions(
|
||||
mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
"com.example/custom" to EmptyJsonObject,
|
||||
),
|
||||
)
|
||||
}
|
||||
info("Test", "1.0")
|
||||
}
|
||||
|
||||
request.params.capabilities.shouldNotBeNull {
|
||||
extensions shouldNotBeNull {
|
||||
size shouldBe 2
|
||||
get("io.modelcontextprotocol/ui") shouldBe EmptyJsonObject
|
||||
get("com.example/custom") shouldBe EmptyJsonObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `capabilities should overwrite extensions when set multiple times`() {
|
||||
val request = buildInitializeRequest {
|
||||
protocolVersion = "2024-11-05"
|
||||
capabilities {
|
||||
extensions(mapOf("io.modelcontextprotocol/ui" to EmptyJsonObject))
|
||||
extensions(mapOf("com.example/custom" to EmptyJsonObject)) // Should overwrite
|
||||
}
|
||||
info("Test", "1.0")
|
||||
}
|
||||
|
||||
request.params.capabilities.shouldNotBeNull {
|
||||
extensions shouldNotBeNull {
|
||||
size shouldBe 1
|
||||
get("com.example/custom") shouldBe EmptyJsonObject
|
||||
get("io.modelcontextprotocol/ui").shouldBeNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `capabilities should overwrite when same field set multiple times`() {
|
||||
val request = buildInitializeRequest {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import io.kotest.matchers.nulls.shouldNotBeNull
|
|||
import io.kotest.matchers.shouldBe
|
||||
import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.buildInitializeRequest
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
|
|
@ -30,6 +31,11 @@ class InitializeDslTest {
|
|||
experimental {
|
||||
put("custom", true)
|
||||
}
|
||||
extensions(
|
||||
mapOf(
|
||||
"io.modelcontextprotocol/ui" to EmptyJsonObject,
|
||||
),
|
||||
)
|
||||
}
|
||||
info(
|
||||
name = "TestClient",
|
||||
|
|
@ -45,6 +51,9 @@ class InitializeDslTest {
|
|||
roots?.listChanged shouldBe true
|
||||
elicitation?.get("mode")?.jsonPrimitive?.content shouldBe "interactive"
|
||||
experimental?.get("custom")?.jsonPrimitive?.content shouldBe "true"
|
||||
extensions shouldNotBeNull {
|
||||
get("io.modelcontextprotocol/ui") shouldBe EmptyJsonObject
|
||||
}
|
||||
}
|
||||
request.params.clientInfo.shouldNotBeNull {
|
||||
name shouldBe "TestClient"
|
||||
|
|
@ -74,6 +83,9 @@ class InitializeDslTest {
|
|||
val samplingObj = buildJsonObject { put("key", "value") }
|
||||
val elicitationObj = buildJsonObject { put("key", "value") }
|
||||
val experimentalObj = buildJsonObject { put("key", "value") }
|
||||
val extensionsMap = mapOf(
|
||||
"io.modelcontextprotocol/ui" to buildJsonObject { put("key", "value") },
|
||||
)
|
||||
|
||||
val request = buildInitializeRequest {
|
||||
protocolVersion = "1.0"
|
||||
|
|
@ -81,6 +93,7 @@ class InitializeDslTest {
|
|||
sampling(samplingObj)
|
||||
elicitation(elicitationObj)
|
||||
experimental(experimentalObj)
|
||||
extensions(extensionsMap)
|
||||
}
|
||||
info("Test", "1.0")
|
||||
}
|
||||
|
|
@ -89,6 +102,7 @@ class InitializeDslTest {
|
|||
sampling shouldBe samplingObj
|
||||
elicitation shouldBe elicitationObj
|
||||
experimental shouldBe experimentalObj
|
||||
extensions shouldBe extensionsMap
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue