Skip to content
Merged
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
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ MCP Kotlin SDK — Kotlin Multiplatform implementation of the Model Context Prot
- All tests for each module are located in `src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/`
- Platform-specific tests go in `src/jvmTest/`, `src/jsTest/`, etc.
- Use Kotest assertions (`shouldBe`, `shouldContain`, etc.) for readable test failures.
- For nullable objects with nested properties, prefer `shouldNotBeNull { ... }` blocks:
```kotlin
content.annotations shouldNotBeNull {
priority shouldBe 1.0
}
```
- Use `shouldMatchJson` from Kotest for JSON validation.
- Mock Ktor HTTP clients using `MockEngine` from `io.ktor:ktor-client-mock`.
- Always add tests for new features or bug fixes, even if not explicitly requested.
Expand Down
43 changes: 43 additions & 0 deletions kotlin-sdk-core/api/kotlin-sdk-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CompleteResult$Compl
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Completion_dslKt {
public static final fun buildCompleteRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/CompleteRequest;
}

public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/ContentBlock : io/modelcontextprotocol/kotlin/sdk/types/WithMeta {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ContentBlock$Companion;
public abstract fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
Expand Down Expand Up @@ -1584,6 +1588,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitResult$Compani
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Elicitation_dslKt {
public static final fun buildElicitRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/EmbeddedResource : io/modelcontextprotocol/kotlin/sdk/types/ContentBlock {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/EmbeddedResource$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ResourceContents;Lio/modelcontextprotocol/kotlin/sdk/types/Annotations;Lkotlinx/serialization/json/JsonObject;)V
Expand Down Expand Up @@ -2016,6 +2024,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/InitializeResult$Com
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Initialize_dslKt {
public static final fun buildInitializeRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/InitializeRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/InitializedNotification : io/modelcontextprotocol/kotlin/sdk/types/ClientNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/InitializedNotification$Companion;
public fun <init> ()V
Expand Down Expand Up @@ -2660,6 +2672,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/LoggingMessageNotifi
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Logging_dslKt {
public static final fun buildSetLevelRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/SetLevelRequest;
}

public abstract interface annotation class io/modelcontextprotocol/kotlin/sdk/types/McpDsl : java/lang/annotation/Annotation {
}

Expand Down Expand Up @@ -2957,6 +2973,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/PingRequestBuilder :
public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/PingRequest_dslKt {
public static final fun buildPingRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/PingRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Progress {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/Progress$Companion;
public fun <init> (DLjava/lang/Double;Ljava/lang/String;)V
Expand Down Expand Up @@ -3211,6 +3231,11 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/PromptReference$Comp
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Prompts_dslKt {
public static final fun buildGetPromptRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest;
public static final fun buildListPromptsRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ListPromptsRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/RPCError {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/RPCError$Companion;
public fun <init> (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
Expand Down Expand Up @@ -3809,6 +3834,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceUpdatedNotif
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Resources_dslKt {
public static final fun buildListResourceTemplatesRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ListResourceTemplatesRequest;
public static final fun buildListResourcesRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ListResourcesRequest;
public static final fun buildReadResourceRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ReadResourceRequest;
public static final fun buildSubscribeRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/SubscribeRequest;
public static final fun buildUnsubscribeRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Role : java/lang/Enum {
public static final field Assistant Lio/modelcontextprotocol/kotlin/sdk/types/Role;
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/Role$Companion;
Expand Down Expand Up @@ -3886,6 +3919,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/RootsListChangedNoti
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Roots_dslKt {
public static final fun buildListRootsRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ListRootsRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/SamplingMessage {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;)V
Expand Down Expand Up @@ -3927,6 +3964,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Sampling_dslKt {
public static final fun assistantAudio (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function1;)V
public static final fun assistantImage (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function1;)V
public static final fun assistantText (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function1;)V
public static final fun buildCreateMessageRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest;
public static final fun user (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function0;)V
public static final fun userAudio (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function1;)V
public static final fun userImage (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function1;)V
Expand Down Expand Up @@ -4490,6 +4528,11 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ToolsKt {
public static synthetic fun success$default (Lio/modelcontextprotocol/kotlin/sdk/types/CallToolResult$Companion;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolResult;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/Tools_dslKt {
public static final fun buildCallToolRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequest;
public static final fun buildListToolsRequest (Lkotlin/jvm/functions/Function1;)Lio/modelcontextprotocol/kotlin/sdk/types/ListToolsRequest;
}

public final class io/modelcontextprotocol/kotlin/sdk/types/UnknownResourceContents : io/modelcontextprotocol/kotlin/sdk/types/ResourceContents {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/UnknownResourceContents$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildCompleteRequest(block: CompleteRequestBuilder.() -> Unit): CompleteRequest {
public inline fun buildCompleteRequest(block: CompleteRequestBuilder.() -> Unit): CompleteRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return CompleteRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildElicitRequest(block: ElicitRequestBuilder.() -> Unit): ElicitRequest {
public inline fun buildElicitRequest(block: ElicitRequestBuilder.() -> Unit): ElicitRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ElicitRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildInitializeRequest(block: InitializeRequestBuilder.() -> Unit): InitializeRequest {
public inline fun buildInitializeRequest(block: InitializeRequestBuilder.() -> Unit): InitializeRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return InitializeRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildSetLevelRequest(block: SetLevelRequestBuilder.() -> Unit): SetLevelRequest {
public inline fun buildSetLevelRequest(block: SetLevelRequestBuilder.() -> Unit): SetLevelRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return SetLevelRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildPingRequest(block: PingRequestBuilder.() -> Unit): PingRequest {
public inline fun buildPingRequest(block: PingRequestBuilder.() -> Unit): PingRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return PingRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildGetPromptRequest(block: GetPromptRequestBuilder.() -> Unit): GetPromptRequest {
public inline fun buildGetPromptRequest(block: GetPromptRequestBuilder.() -> Unit): GetPromptRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return GetPromptRequestBuilder().apply(block).build()
}
Expand Down Expand Up @@ -116,7 +116,7 @@ public class GetPromptRequestBuilder @PublishedApi internal constructor() : Requ
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildListPromptsRequest(block: ListPromptsRequestBuilder.() -> Unit): ListPromptsRequest {
public inline fun buildListPromptsRequest(block: ListPromptsRequestBuilder.() -> Unit): ListPromptsRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListPromptsRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildListResourcesRequest(block: ListResourcesRequestBuilder.() -> Unit): ListResourcesRequest {
public inline fun buildListResourcesRequest(block: ListResourcesRequestBuilder.() -> Unit): ListResourcesRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListResourcesRequestBuilder().apply(block).build()
}
Expand Down Expand Up @@ -82,7 +82,7 @@ public class ListResourcesRequestBuilder @PublishedApi internal constructor() :
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildReadResourceRequest(block: ReadResourceRequestBuilder.() -> Unit): ReadResourceRequest {
public inline fun buildReadResourceRequest(block: ReadResourceRequestBuilder.() -> Unit): ReadResourceRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ReadResourceRequestBuilder().apply(block).build()
}
Expand Down Expand Up @@ -144,7 +144,7 @@ public class ReadResourceRequestBuilder @PublishedApi internal constructor() : R
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildSubscribeRequest(block: SubscribeRequestBuilder.() -> Unit): SubscribeRequest {
public inline fun buildSubscribeRequest(block: SubscribeRequestBuilder.() -> Unit): SubscribeRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return SubscribeRequestBuilder().apply(block).build()
}
Expand Down Expand Up @@ -206,7 +206,7 @@ public class SubscribeRequestBuilder @PublishedApi internal constructor() : Requ
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildUnsubscribeRequest(block: UnsubscribeRequestBuilder.() -> Unit): UnsubscribeRequest {
public inline fun buildUnsubscribeRequest(block: UnsubscribeRequestBuilder.() -> Unit): UnsubscribeRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return UnsubscribeRequestBuilder().apply(block).build()
}
Expand Down Expand Up @@ -271,7 +271,7 @@ public class UnsubscribeRequestBuilder @PublishedApi internal constructor() : Re
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildListResourceTemplatesRequest(
public inline fun buildListResourceTemplatesRequest(
block: ListResourceTemplatesRequestBuilder.() -> Unit,
): ListResourceTemplatesRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildListRootsRequest(block: ListRootsRequestBuilder.() -> Unit): ListRootsRequest {
public inline fun buildListRootsRequest(block: ListRootsRequestBuilder.() -> Unit): ListRootsRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListRootsRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildCreateMessageRequest(block: CreateMessageRequestBuilder.() -> Unit): CreateMessageRequest {
public inline fun buildCreateMessageRequest(block: CreateMessageRequestBuilder.() -> Unit): CreateMessageRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return CreateMessageRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import kotlin.contracts.contract
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildCallToolRequest(block: CallToolRequestBuilder.() -> Unit): CallToolRequest {
public inline fun buildCallToolRequest(block: CallToolRequestBuilder.() -> Unit): CallToolRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return CallToolRequestBuilder().apply(block).build()
}
Expand Down Expand Up @@ -143,7 +143,7 @@ public class CallToolRequestBuilder @PublishedApi internal constructor() : Reque
*/
@OptIn(ExperimentalContracts::class)
@ExperimentalMcpApi
internal inline fun buildListToolsRequest(block: ListToolsRequestBuilder.() -> Unit): ListToolsRequest {
public inline fun buildListToolsRequest(block: ListToolsRequestBuilder.() -> Unit): ListToolsRequest {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListToolsRequestBuilder().apply(block).build()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.modelcontextprotocol.kotlin.sdk.types.dsl

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
import io.modelcontextprotocol.kotlin.sdk.types.PromptReference
import io.modelcontextprotocol.kotlin.sdk.types.ResourceTemplateReference
import io.modelcontextprotocol.kotlin.sdk.types.buildCompleteRequest
import kotlin.test.Test

@OptIn(ExperimentalMcpApi::class)
class CompletionDslTest {
@Test
fun `buildCompleteRequest should build with prompt reference and context`() {
val request = buildCompleteRequest {
argument("query", "user input")
ref(PromptReference("searchPrompt"))
context {
put("userId", "123")
}
}

request.params.argument.name shouldBe "query"
request.params.argument.value shouldBe "user input"
(request.params.ref as PromptReference).name shouldBe "searchPrompt"
request.params.context shouldNotBeNull {
arguments?.get("userId") shouldBe "123"
}
}

@Test
fun `buildCompleteRequest should build with resource template reference and map context`() {
val request = buildCompleteRequest {
argument("path", "/users/123")
ref(ResourceTemplateReference("file:///{path}"))
context(mapOf("role" to "admin"))
}

request.params.argument.name shouldBe "path"
request.params.argument.value shouldBe "/users/123"
(request.params.ref as ResourceTemplateReference).uri shouldBe "file:///{path}"
request.params.context shouldNotBeNull {
arguments?.get("role") shouldBe "admin"
}
}

@Test
fun `buildCompleteRequest should throw if argument is missing`() {
shouldThrow<IllegalArgumentException> {
buildCompleteRequest {
ref(PromptReference("name"))
}
}
}

@Test
fun `buildCompleteRequest should throw if ref is missing`() {
shouldThrow<IllegalArgumentException> {
buildCompleteRequest {
argument("name", "value")
}
}
}
}
Loading
Loading