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
5 changes: 5 additions & 0 deletions kotlin-sdk-client/api/kotlin-sdk-client.api
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public class io/modelcontextprotocol/kotlin/sdk/client/Client : io/modelcontextp
public static synthetic fun unsubscribeResource$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class io/modelcontextprotocol/kotlin/sdk/client/ClientKt {
public static final fun mcpClient (Lio/modelcontextprotocol/kotlin/sdk/types/Implementation;Lio/modelcontextprotocol/kotlin/sdk/client/ClientOptions;Lio/modelcontextprotocol/kotlin/sdk/shared/Transport;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun mcpClient$default (Lio/modelcontextprotocol/kotlin/sdk/types/Implementation;Lio/modelcontextprotocol/kotlin/sdk/client/ClientOptions;Lio/modelcontextprotocol/kotlin/sdk/shared/Transport;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class io/modelcontextprotocol/kotlin/sdk/client/ClientOptions : io/modelcontextprotocol/kotlin/sdk/shared/ProtocolOptions {
public fun <init> ()V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;Z)V
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.client

import io.github.oshai.kotlinlogging.KotlinLogging
import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
import io.modelcontextprotocol.kotlin.sdk.shared.Protocol
import io.modelcontextprotocol.kotlin.sdk.shared.ProtocolOptions
import io.modelcontextprotocol.kotlin.sdk.shared.RequestOptions
Expand Down Expand Up @@ -69,6 +70,30 @@ public class ClientOptions(
enforceStrictCapabilities: Boolean = true,
) : ProtocolOptions(enforceStrictCapabilities = enforceStrictCapabilities)

/**
* Initializes and connects an MCP client using the provided clientInfo [Implementation], client options,
* and transport mechanism.
*
* @param clientInfo The implementation details of the MCP client, including its name, version, and other metadata.
* @param clientOptions Optional client configuration settings, such as supported capabilities
* and strict enforcement options. Defaults to a new instance of [ClientOptions].
* @param transport The transport mechanism used for communication.
* @return An instance of [Client] that is connected and ready for use with the specified transport.
*/
@ExperimentalMcpApi
public suspend fun mcpClient(
clientInfo: Implementation,
clientOptions: ClientOptions = ClientOptions(),
transport: Transport,
): Client {
val client = Client(
clientInfo = clientInfo,
options = clientOptions,
)
client.connect(transport)
return client
}

/**
* An MCP client on top of a pluggable transport.
*
Expand Down
3 changes: 3 additions & 0 deletions kotlin-sdk-core/api/kotlin-sdk-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ public final class io/modelcontextprotocol/kotlin/sdk/ErrorCode$Unknown$Companio
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

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

public final class io/modelcontextprotocol/kotlin/sdk/InitializedNotification {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification;
public final fun Params (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/BaseNotificationParams;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.modelcontextprotocol.kotlin.sdk

import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
import kotlin.annotation.AnnotationTarget.CLASS
import kotlin.annotation.AnnotationTarget.CONSTRUCTOR
import kotlin.annotation.AnnotationTarget.FIELD
import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.annotation.AnnotationTarget.LOCAL_VARIABLE
import kotlin.annotation.AnnotationTarget.PROPERTY
import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER
import kotlin.annotation.AnnotationTarget.TYPEALIAS
import kotlin.annotation.AnnotationTarget.VALUE_PARAMETER

/**
* Annotation marking an API as experimental and subject to changes or removal in the future.
*
* This annotation is used to signal that a particular element, such as a class, function, or property,
* is part of an experimental API. Such APIs may not be stable, and their usage requires opting in
* explicitly.
*
* Users of the annotated API must explicitly accept the opt-in requirement to ensure they are aware
* of the potential instability or unfinished nature of the API.
*
* Targets that can be annotated include:
* - Classes
* - Annotation classes
* - Properties
* - Fields
* - Local variables
* - Value parameters
* - Constructors
* - Functions
* - Property getters
* - Property setters
* - Type aliases
*/
@RequiresOptIn
@MustBeDocumented
@Target(
CLASS,
ANNOTATION_CLASS,
PROPERTY,
FIELD,
LOCAL_VARIABLE,
VALUE_PARAMETER,
CONSTRUCTOR,
FUNCTION,
PROPERTY_GETTER,
PROPERTY_SETTER,
TYPEALIAS,
)
@Retention(AnnotationRetention.BINARY)
public annotation class ExperimentalMcpApi
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ class StdioClientTransportTest : BaseTransportTest() {
val input = process.inputStream.asSource().buffered()
val output = process.outputStream.asSink().buffered()

val client = StdioClientTransport(
val transport = StdioClientTransport(
input = input,
output = output,
)

testTransportOpenClose(client)
testTransportOpenClose(transport)

process.destroy()
}
Expand All @@ -35,12 +35,12 @@ class StdioClientTransportTest : BaseTransportTest() {
val input = process.inputStream.asSource().buffered()
val output = process.outputStream.asSink().buffered()

val client = StdioClientTransport(
val transport = StdioClientTransport(
input = input,
output = output,
)

testTransportRead(client)
testTransportRead(transport)

process.waitFor()
process.destroy()
Expand All @@ -55,12 +55,12 @@ class StdioClientTransportTest : BaseTransportTest() {
val input = process.inputStream.asSource().buffered()
val output = process.outputStream.asSink().buffered()

val client = StdioClientTransport(
val transport = StdioClientTransport(
input = input,
output = output,
)

testTransportRead(client)
testTransportRead(transport)

process.waitFor()
process.destroy()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package io.modelcontextprotocol.kotlin.sdk.server

import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
import io.modelcontextprotocol.kotlin.sdk.Implementation
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.client.Client
import io.modelcontextprotocol.kotlin.sdk.client.ClientOptions
import io.modelcontextprotocol.kotlin.sdk.client.mcpClient
import io.modelcontextprotocol.kotlin.sdk.shared.InMemoryTransport
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertNull
import kotlin.test.assertEquals

@OptIn(ExperimentalMcpApi::class)
class ServerInstructionsTest {

@Test
Expand All @@ -22,10 +26,17 @@ class ServerInstructionsTest {
// The instructions should be stored internally and used in handleInitialize
// We can't directly access the private field, but we can test it through initialization
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(clientInfo = Implementation(name = "test client", version = "1.0"))

server.createSession(serverTransport)
client.connect(clientTransport)

val client = mcpClient(
clientInfo = Implementation(name = "test client", version = "1.0"),
clientOptions = ClientOptions(
capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(listChanged = false),
),
),
transport = clientTransport,
)

assertEquals(instructions, client.serverInstructions)
}
Expand All @@ -41,10 +52,12 @@ class ServerInstructionsTest {
// The instructions should be stored internally and used in handleInitialize
// We can't directly access the private field, but we can test it through initialization
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(clientInfo = Implementation(name = "test client", version = "1.0"))

server.createSession(serverTransport)
client.connect(clientTransport)

val client = mcpClient(
clientInfo = Implementation(name = "test client", version = "1.0"),
transport = clientTransport,
)

assertEquals(instructions, client.serverInstructions)
}
Expand All @@ -58,10 +71,13 @@ class ServerInstructionsTest {
val server = Server(serverInfo, serverOptions)

val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(clientInfo = Implementation(name = "test client", version = "1.0"))

server.createSession(serverTransport)
client.connect(clientTransport)

val client = mcpClient(
clientInfo = Implementation(name = "test client", version = "1.0"),
transport = clientTransport,
)

assertNull(client.serverInstructions)
}
Expand Down
Loading