From f5f24facf710fca01e9ba0a6d935a77cae8a4ae1 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Thu, 25 Sep 2025 08:09:52 -0700 Subject: [PATCH 01/12] Adds Instructions to the server and propagates to the InitializeResult --- kotlin-sdk-core/api/kotlin-sdk-core.api | 12 ++-- .../modelcontextprotocol/kotlin/sdk/types.kt | 4 ++ .../kotlin/sdk/TypesTest.kt | 59 +++++++++++++++++++ kotlin-sdk-server/api/kotlin-sdk-server.api | 3 +- .../kotlin/sdk/server/Server.kt | 8 ++- .../kotlin/sdk/server/ServerTest.kt | 54 +++++++++++++++++ 6 files changed, 133 insertions(+), 7 deletions(-) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index ed9a268c..d4d4b9d5 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -975,16 +975,18 @@ public final class io/modelcontextprotocol/kotlin/sdk/InitializeRequest$Companio public final class io/modelcontextprotocol/kotlin/sdk/InitializeResult : io/modelcontextprotocol/kotlin/sdk/ServerResult { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/InitializeResult$Companion; - public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities; public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; - public final fun component4 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/InitializeResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Lkotlinx/serialization/json/JsonObject; + public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/InitializeResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; public fun equals (Ljava/lang/Object;)Z public final fun getCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities; + public final fun getInstructions ()Ljava/lang/String; public final fun getProtocolVersion ()Ljava/lang/String; public final fun getServerInfo ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; public fun get_meta ()Lkotlinx/serialization/json/JsonObject; diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index b6879241..927967af 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -512,6 +512,10 @@ public data class InitializeResult( val protocolVersion: String = LATEST_PROTOCOL_VERSION, val capabilities: ServerCapabilities = ServerCapabilities(), val serverInfo: Implementation, + /** + * Optional instructions from the server to the client about how to use this server. + */ + val instructions: String? = null, override val _meta: JsonObject = EmptyJsonObject, ) : ServerResult diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index 23e2ffb7..7a4938a8 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -394,4 +394,63 @@ class TypesTest { assertEquals("id", request.argument.name) assertEquals("123", request.argument.value) } + + // InitializeResult Tests + @Test + fun `should create InitializeResult with default instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val result = InitializeResult( + serverInfo = serverInfo, + ) + + assertEquals(LATEST_PROTOCOL_VERSION, result.protocolVersion) + assertEquals(serverInfo, result.serverInfo) + assertEquals(null, result.instructions) + } + + @Test + fun `should create InitializeResult with custom instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val instructions = "Use this server to perform calculations. Call the 'add' tool to add numbers." + val result = InitializeResult( + serverInfo = serverInfo, + instructions = instructions, + ) + + assertEquals(LATEST_PROTOCOL_VERSION, result.protocolVersion) + assertEquals(serverInfo, result.serverInfo) + assertEquals(instructions, result.instructions) + } + + @Test + fun `should serialize and deserialize InitializeResult with instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val instructions = "This server provides file system access. Use the 'read' tool to read files." + val result = InitializeResult( + serverInfo = serverInfo, + instructions = instructions, + ) + + val json = McpJson.encodeToString(result) + val decoded = McpJson.decodeFromString(json) + + assertEquals(LATEST_PROTOCOL_VERSION, decoded.protocolVersion) + assertEquals(serverInfo, decoded.serverInfo) + assertEquals(instructions, decoded.instructions) + } + + @Test + fun `should serialize and deserialize InitializeResult without instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val result = InitializeResult( + serverInfo = serverInfo, + ) + + val json = McpJson.encodeToString(result) + val decoded = McpJson.decodeFromString(json) + + assertEquals(LATEST_PROTOCOL_VERSION, decoded.protocolVersion) + assertEquals(serverInfo, decoded.serverInfo) + assertEquals(null, decoded.instructions) + } } diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index ec09fea2..ca9e1616 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -45,7 +45,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/server/RegisteredTool { } public class io/modelcontextprotocol/kotlin/sdk/server/Server : io/modelcontextprotocol/kotlin/sdk/shared/Protocol { - public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;)V + public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPrompt (Lio/modelcontextprotocol/kotlin/sdk/Prompt;Lkotlin/jvm/functions/Function2;)V public final fun addPrompt (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;)V public static synthetic fun addPrompt$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index ac71b5fe..fe0d187e 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -76,8 +76,13 @@ public class ServerOptions(public val capabilities: ServerCapabilities, enforceS * * @param serverInfo Information about this server implementation (name, version). * @param options Configuration options for the server. + * @param instructions Optional instructions from the server to the client about how to use this server. */ -public open class Server(private val serverInfo: Implementation, options: ServerOptions) : Protocol(options) { +public open class Server( + private val serverInfo: Implementation, + options: ServerOptions, + private val instructions: String? = null, +) : Protocol(options) { @Suppress("ktlint:standard:backing-property-naming") private var _onInitialized: (() -> Unit) = {} @@ -613,6 +618,7 @@ public open class Server(private val serverInfo: Implementation, options: Server protocolVersion = protocolVersion, capabilities = capabilities, serverInfo = serverInfo, + instructions = instructions, ) } diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt index 77175b47..e36fac1a 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt @@ -471,4 +471,58 @@ class ServerTest { } assertEquals("Server does not support resources capability.", exception.message) } + + @Test + fun `Server constructor should accept instructions parameter`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + val instructions = "This is a test server. Use it for testing purposes only." + + val server = Server(serverInfo, serverOptions, instructions) + + // 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")) + + launch { client.connect(clientTransport) } + launch { server.connect(serverTransport) } + + // The test passes if the server can be created with instructions without throwing + assertTrue(true, "Server should accept instructions parameter") + } + + @Test + fun `Server constructor should work with null instructions`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + + val server = Server(serverInfo, serverOptions, null) + + // Test that server works with null instructions + val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() + val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) + + launch { client.connect(clientTransport) } + launch { server.connect(serverTransport) } + + assertTrue(true, "Server should accept null instructions parameter") + } + + @Test + fun `Server constructor should work with default instructions parameter`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + + // Test that server works when instructions parameter is omitted (defaults to null) + val server = Server(serverInfo, serverOptions) + + val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() + val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) + + launch { client.connect(clientTransport) } + launch { server.connect(serverTransport) } + + assertTrue(true, "Server should work with default instructions parameter") + } } From 574c435c9fa5c45c24611fd875990a05ae35d3ee Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Thu, 25 Sep 2025 08:20:47 -0700 Subject: [PATCH 02/12] Adds documentation updates for examples --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4fd0609e..299e7ee6 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,8 @@ val server = Server( listChanged = true ) ) - ) + ), + instructions = "This server provides example resources and demonstrates MCP capabilities." ) // Add a resource @@ -156,7 +157,8 @@ fun Application.module() { prompts = ServerCapabilities.Prompts(listChanged = null), resources = ServerCapabilities.Resources(subscribe = null, listChanged = null) ) - ) + ), + instructions = "This SSE server provides prompts and resources via Server-Sent Events." ) } } @@ -184,7 +186,8 @@ fun Application.module() { prompts = ServerCapabilities.Prompts(listChanged = null), resources = ServerCapabilities.Resources(subscribe = null, listChanged = null) ) - ) + ), + instructions = "Connect via SSE to interact with this MCP server." ) } } From 0d8253e6fc42380e7d9ce3e92f3038ed8c4337a2 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Fri, 26 Sep 2025 12:41:09 -0500 Subject: [PATCH 03/12] refactors the serverInfo and instructions to protected --- kotlin-sdk-server/api/kotlin-sdk-server.api | 2 ++ .../io/modelcontextprotocol/kotlin/sdk/server/Server.kt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index ca9e1616..a325ba03 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -67,8 +67,10 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server : io/modelcontextp public static synthetic fun createMessage$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun getClientCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; public final fun getClientVersion ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; + protected final fun getInstructions ()Ljava/lang/String; public final fun getPrompts ()Ljava/util/Map; public final fun getResources ()Ljava/util/Map; + protected final fun getServerInfo ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; public final fun getTools ()Ljava/util/Map; public final fun listRoots (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun listRoots$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index fe0d187e..afd71e71 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -79,9 +79,9 @@ public class ServerOptions(public val capabilities: ServerCapabilities, enforceS * @param instructions Optional instructions from the server to the client about how to use this server. */ public open class Server( - private val serverInfo: Implementation, + protected val serverInfo: Implementation, options: ServerOptions, - private val instructions: String? = null, + protected val instructions: String? = null, ) : Protocol(options) { @Suppress("ktlint:standard:backing-property-naming") private var _onInitialized: (() -> Unit) = {} From c0249a5f2830725fb61ddcbc5852b1f6c8d72834 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Thu, 25 Sep 2025 08:09:52 -0700 Subject: [PATCH 04/12] Adds Instructions to the server and propagates to the InitializeResult --- kotlin-sdk-core/api/kotlin-sdk-core.api | 12 ++-- .../modelcontextprotocol/kotlin/sdk/types.kt | 4 ++ .../kotlin/sdk/TypesTest.kt | 59 +++++++++++++++++++ kotlin-sdk-server/api/kotlin-sdk-server.api | 3 +- .../kotlin/sdk/server/Server.kt | 8 ++- .../kotlin/sdk/server/ServerTest.kt | 54 +++++++++++++++++ 6 files changed, 133 insertions(+), 7 deletions(-) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index ed9a268c..d4d4b9d5 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -975,16 +975,18 @@ public final class io/modelcontextprotocol/kotlin/sdk/InitializeRequest$Companio public final class io/modelcontextprotocol/kotlin/sdk/InitializeResult : io/modelcontextprotocol/kotlin/sdk/ServerResult { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/InitializeResult$Companion; - public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities; public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; - public final fun component4 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/InitializeResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Lkotlinx/serialization/json/JsonObject; + public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/InitializeResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/InitializeResult; public fun equals (Ljava/lang/Object;)Z public final fun getCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities; + public final fun getInstructions ()Ljava/lang/String; public final fun getProtocolVersion ()Ljava/lang/String; public final fun getServerInfo ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; public fun get_meta ()Lkotlinx/serialization/json/JsonObject; diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index b6879241..927967af 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -512,6 +512,10 @@ public data class InitializeResult( val protocolVersion: String = LATEST_PROTOCOL_VERSION, val capabilities: ServerCapabilities = ServerCapabilities(), val serverInfo: Implementation, + /** + * Optional instructions from the server to the client about how to use this server. + */ + val instructions: String? = null, override val _meta: JsonObject = EmptyJsonObject, ) : ServerResult diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index 23e2ffb7..7a4938a8 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -394,4 +394,63 @@ class TypesTest { assertEquals("id", request.argument.name) assertEquals("123", request.argument.value) } + + // InitializeResult Tests + @Test + fun `should create InitializeResult with default instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val result = InitializeResult( + serverInfo = serverInfo, + ) + + assertEquals(LATEST_PROTOCOL_VERSION, result.protocolVersion) + assertEquals(serverInfo, result.serverInfo) + assertEquals(null, result.instructions) + } + + @Test + fun `should create InitializeResult with custom instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val instructions = "Use this server to perform calculations. Call the 'add' tool to add numbers." + val result = InitializeResult( + serverInfo = serverInfo, + instructions = instructions, + ) + + assertEquals(LATEST_PROTOCOL_VERSION, result.protocolVersion) + assertEquals(serverInfo, result.serverInfo) + assertEquals(instructions, result.instructions) + } + + @Test + fun `should serialize and deserialize InitializeResult with instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val instructions = "This server provides file system access. Use the 'read' tool to read files." + val result = InitializeResult( + serverInfo = serverInfo, + instructions = instructions, + ) + + val json = McpJson.encodeToString(result) + val decoded = McpJson.decodeFromString(json) + + assertEquals(LATEST_PROTOCOL_VERSION, decoded.protocolVersion) + assertEquals(serverInfo, decoded.serverInfo) + assertEquals(instructions, decoded.instructions) + } + + @Test + fun `should serialize and deserialize InitializeResult without instructions`() { + val serverInfo = Implementation(name = "test-server", version = "1.0.0") + val result = InitializeResult( + serverInfo = serverInfo, + ) + + val json = McpJson.encodeToString(result) + val decoded = McpJson.decodeFromString(json) + + assertEquals(LATEST_PROTOCOL_VERSION, decoded.protocolVersion) + assertEquals(serverInfo, decoded.serverInfo) + assertEquals(null, decoded.instructions) + } } diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index ec09fea2..ca9e1616 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -45,7 +45,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/server/RegisteredTool { } public class io/modelcontextprotocol/kotlin/sdk/server/Server : io/modelcontextprotocol/kotlin/sdk/shared/Protocol { - public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;)V + public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPrompt (Lio/modelcontextprotocol/kotlin/sdk/Prompt;Lkotlin/jvm/functions/Function2;)V public final fun addPrompt (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;)V public static synthetic fun addPrompt$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index ac71b5fe..fe0d187e 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -76,8 +76,13 @@ public class ServerOptions(public val capabilities: ServerCapabilities, enforceS * * @param serverInfo Information about this server implementation (name, version). * @param options Configuration options for the server. + * @param instructions Optional instructions from the server to the client about how to use this server. */ -public open class Server(private val serverInfo: Implementation, options: ServerOptions) : Protocol(options) { +public open class Server( + private val serverInfo: Implementation, + options: ServerOptions, + private val instructions: String? = null, +) : Protocol(options) { @Suppress("ktlint:standard:backing-property-naming") private var _onInitialized: (() -> Unit) = {} @@ -613,6 +618,7 @@ public open class Server(private val serverInfo: Implementation, options: Server protocolVersion = protocolVersion, capabilities = capabilities, serverInfo = serverInfo, + instructions = instructions, ) } diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt index 77175b47..e36fac1a 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt @@ -471,4 +471,58 @@ class ServerTest { } assertEquals("Server does not support resources capability.", exception.message) } + + @Test + fun `Server constructor should accept instructions parameter`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + val instructions = "This is a test server. Use it for testing purposes only." + + val server = Server(serverInfo, serverOptions, instructions) + + // 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")) + + launch { client.connect(clientTransport) } + launch { server.connect(serverTransport) } + + // The test passes if the server can be created with instructions without throwing + assertTrue(true, "Server should accept instructions parameter") + } + + @Test + fun `Server constructor should work with null instructions`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + + val server = Server(serverInfo, serverOptions, null) + + // Test that server works with null instructions + val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() + val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) + + launch { client.connect(clientTransport) } + launch { server.connect(serverTransport) } + + assertTrue(true, "Server should accept null instructions parameter") + } + + @Test + fun `Server constructor should work with default instructions parameter`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + + // Test that server works when instructions parameter is omitted (defaults to null) + val server = Server(serverInfo, serverOptions) + + val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() + val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) + + launch { client.connect(clientTransport) } + launch { server.connect(serverTransport) } + + assertTrue(true, "Server should work with default instructions parameter") + } } From b8a59459247c916921451609484a81e54ee9b375 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Thu, 25 Sep 2025 08:20:47 -0700 Subject: [PATCH 05/12] Adds documentation updates for examples --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4fd0609e..299e7ee6 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,8 @@ val server = Server( listChanged = true ) ) - ) + ), + instructions = "This server provides example resources and demonstrates MCP capabilities." ) // Add a resource @@ -156,7 +157,8 @@ fun Application.module() { prompts = ServerCapabilities.Prompts(listChanged = null), resources = ServerCapabilities.Resources(subscribe = null, listChanged = null) ) - ) + ), + instructions = "This SSE server provides prompts and resources via Server-Sent Events." ) } } @@ -184,7 +186,8 @@ fun Application.module() { prompts = ServerCapabilities.Prompts(listChanged = null), resources = ServerCapabilities.Resources(subscribe = null, listChanged = null) ) - ) + ), + instructions = "Connect via SSE to interact with this MCP server." ) } } From 6a95a597c9e628160fb5db9f55d0917939e04ca4 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Fri, 26 Sep 2025 12:41:09 -0500 Subject: [PATCH 06/12] refactors the serverInfo and instructions to protected --- kotlin-sdk-server/api/kotlin-sdk-server.api | 2 ++ .../io/modelcontextprotocol/kotlin/sdk/server/Server.kt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index ca9e1616..a325ba03 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -67,8 +67,10 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server : io/modelcontextp public static synthetic fun createMessage$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun getClientCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; public final fun getClientVersion ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; + protected final fun getInstructions ()Ljava/lang/String; public final fun getPrompts ()Ljava/util/Map; public final fun getResources ()Ljava/util/Map; + protected final fun getServerInfo ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; public final fun getTools ()Ljava/util/Map; public final fun listRoots (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun listRoots$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index fe0d187e..afd71e71 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -79,9 +79,9 @@ public class ServerOptions(public val capabilities: ServerCapabilities, enforceS * @param instructions Optional instructions from the server to the client about how to use this server. */ public open class Server( - private val serverInfo: Implementation, + protected val serverInfo: Implementation, options: ServerOptions, - private val instructions: String? = null, + protected val instructions: String? = null, ) : Protocol(options) { @Suppress("ktlint:standard:backing-property-naming") private var _onInitialized: (() -> Unit) = {} From 74aeaff8c9885514ced3a36775cc813637a002c4 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Mon, 29 Sep 2025 13:18:31 -0500 Subject: [PATCH 07/12] Added the returned serverInstructions to the client and added verification to the ServerTests (addresses PR feedback) --- .../kotlin/sdk/client/Client.kt | 8 +++++ .../kotlin/sdk/server/ServerSession.kt | 10 +++--- .../kotlin/sdk/server/ServerTest.kt | 32 +++++-------------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt index e21696bc..e468fb49 100644 --- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt +++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt @@ -91,6 +91,13 @@ public open class Client(private val clientInfo: Implementation, options: Client public var serverCapabilities: ServerCapabilities? = null private set + /** + * Optional human-readable instructions or description for the client. + * This can be used to provide context or usage guidelines for users interacting with the client. + */ + public var serverInstructions: String? = null + private set + /** * Retrieves the server's reported version information after initialization. * @@ -154,6 +161,7 @@ public open class Client(private val clientInfo: Implementation, options: Client serverCapabilities = result.capabilities serverVersion = result.serverInfo + serverInstructions = result.instructions notification(InitializedNotification()) } catch (error: Throwable) { diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSession.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSession.kt index 2ff532a7..18cbf089 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSession.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSession.kt @@ -255,7 +255,7 @@ public open class ServerSession( Defined.NotificationsResourcesUpdated, Defined.NotificationsResourcesListChanged, - -> { + -> { if (serverCapabilities.resources == null) { throw IllegalStateException( "Server does not support notifying about resources (required for ${method.value})", @@ -281,7 +281,7 @@ public open class ServerSession( Defined.NotificationsCancelled, Defined.NotificationsProgress, - -> { + -> { // Always allowed } @@ -316,7 +316,7 @@ public open class ServerSession( Defined.PromptsGet, Defined.PromptsList, - -> { + -> { if (serverCapabilities.prompts == null) { throw IllegalStateException("Server does not support prompts (required for $method)") } @@ -327,7 +327,7 @@ public open class ServerSession( Defined.ResourcesRead, Defined.ResourcesSubscribe, Defined.ResourcesUnsubscribe, - -> { + -> { if (serverCapabilities.resources == null) { throw IllegalStateException("Server does not support resources (required for $method)") } @@ -335,7 +335,7 @@ public open class ServerSession( Defined.ToolsCall, Defined.ToolsList, - -> { + -> { if (serverCapabilities.tools == null) { throw IllegalStateException("Server does not support tools (required for $method)") } diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt index e36fac1a..9a2d178f 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertNull import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -485,32 +486,15 @@ class ServerTest { val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) - launch { client.connect(clientTransport) } - launch { server.connect(serverTransport) } + server.connect(serverTransport) + client.connect(clientTransport) - // The test passes if the server can be created with instructions without throwing - assertTrue(true, "Server should accept instructions parameter") + assertEquals(instructions, client.serverInstructions) } - @Test - fun `Server constructor should work with null instructions`() = runTest { - val serverInfo = Implementation(name = "test server", version = "1.0") - val serverOptions = ServerOptions(capabilities = ServerCapabilities()) - - val server = Server(serverInfo, serverOptions, null) - - // Test that server works with null instructions - val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() - val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) - - launch { client.connect(clientTransport) } - launch { server.connect(serverTransport) } - - assertTrue(true, "Server should accept null instructions parameter") - } @Test - fun `Server constructor should work with default instructions parameter`() = runTest { + fun `Server constructor should work without instructions parameter`() = runTest { val serverInfo = Implementation(name = "test server", version = "1.0") val serverOptions = ServerOptions(capabilities = ServerCapabilities()) @@ -520,9 +504,9 @@ class ServerTest { val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair() val client = Client(clientInfo = Implementation(name = "test client", version = "1.0")) - launch { client.connect(clientTransport) } - launch { server.connect(serverTransport) } + server.connect(serverTransport) + client.connect(clientTransport) - assertTrue(true, "Server should work with default instructions parameter") + assertNull(client.serverInstructions) } } From 3e682eccc07783664e5db11f248dd74d4bc59729 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Mon, 29 Sep 2025 13:24:05 -0500 Subject: [PATCH 08/12] Modifies top level Server instructions to be a provider so it can be sourced dynamically for each session if desired --- kotlin-sdk-server/api/kotlin-sdk-server.api | 6 +++--- .../io/modelcontextprotocol/kotlin/sdk/server/Server.kt | 7 ++++--- .../modelcontextprotocol/kotlin/sdk/server/ServerTest.kt | 3 +-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index 431b112d..dd719bf0 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -45,8 +45,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/server/RegisteredTool { } public class io/modelcontextprotocol/kotlin/sdk/server/Server { - public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;)V - public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Lkotlin/jvm/functions/Function0;)V + public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPrompt (Lio/modelcontextprotocol/kotlin/sdk/Prompt;Lkotlin/jvm/functions/Function2;)V public final fun addPrompt (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;)V public static synthetic fun addPrompt$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V @@ -60,7 +60,7 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server { public final fun addTools (Ljava/util/List;)V public final fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun connect (Lio/modelcontextprotocol/kotlin/sdk/shared/Transport;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - protected final fun getInstructions ()Ljava/lang/String; + protected final fun getInstructionsProvider ()Lkotlin/jvm/functions/Function0; protected final fun getOptions ()Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions; public final fun getPrompts ()Ljava/util/Map; public final fun getResources ()Ljava/util/Map; diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index 5cbb2908..61ae3ea5 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -53,13 +53,14 @@ public class ServerOptions(public val capabilities: ServerCapabilities, enforceS * * @param serverInfo Information about this server implementation (name, version). * @param options Configuration options for the server. - * @param instructions Optional instructions from the server to the client about how to use this server. + * @param instructionsProvider Optional provider for instructions from the server to the client about how to use + * this server. The provider is called each time a new session is started to support dynamic instructions. */ public open class Server( protected val serverInfo: Implementation, protected val options: ServerOptions, - protected val instructions: String? = null, + protected val instructionsProvider: (() -> String)? = null, ) { private val sessions = atomic(persistentListOf()) @@ -96,7 +97,7 @@ public open class Server( * @return The initialized and connected server session. */ public suspend fun connect(transport: Transport): ServerSession { - val session = ServerSession(serverInfo, options, instructions) + val session = ServerSession(serverInfo, options, instructionsProvider?.invoke()) // Internal handlers for tools if (options.capabilities.tools != null) { diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt index 9a2d178f..4f856a09 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt @@ -479,7 +479,7 @@ class ServerTest { val serverOptions = ServerOptions(capabilities = ServerCapabilities()) val instructions = "This is a test server. Use it for testing purposes only." - val server = Server(serverInfo, serverOptions, instructions) + val server = Server(serverInfo, serverOptions) { instructions } // 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 @@ -492,7 +492,6 @@ class ServerTest { assertEquals(instructions, client.serverInstructions) } - @Test fun `Server constructor should work without instructions parameter`() = runTest { val serverInfo = Implementation(name = "test server", version = "1.0") From 083ff48beff84548b4cd5598130cb67bfadd89d8 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Mon, 29 Sep 2025 13:41:37 -0500 Subject: [PATCH 09/12] updates client API to add serverInstructions --- kotlin-sdk-client/api/kotlin-sdk-client.api | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-sdk-client/api/kotlin-sdk-client.api b/kotlin-sdk-client/api/kotlin-sdk-client.api index f0782da5..a785a916 100644 --- a/kotlin-sdk-client/api/kotlin-sdk-client.api +++ b/kotlin-sdk-client/api/kotlin-sdk-client.api @@ -17,6 +17,7 @@ public class io/modelcontextprotocol/kotlin/sdk/client/Client : io/modelcontextp public final fun getPrompt (Lio/modelcontextprotocol/kotlin/sdk/GetPromptRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun getPrompt$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/GetPromptRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun getServerCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities; + public final fun getServerInstructions ()Ljava/lang/String; public final fun getServerVersion ()Lio/modelcontextprotocol/kotlin/sdk/Implementation; public final fun listPrompts (Lio/modelcontextprotocol/kotlin/sdk/ListPromptsRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun listPrompts$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/ListPromptsRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; From b05a1967d32477d1fc726d835b9952c0a134a7fb Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Mon, 29 Sep 2025 13:55:30 -0500 Subject: [PATCH 10/12] Fixes documentation inconsistencies --- README.md | 17 ++++++++++------- .../kotlin/sdk/client/Client.kt | 5 +++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 299e7ee6..34cd83ff 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,10 @@ val server = Server( listChanged = true ) ) - ), - instructions = "This server provides example resources and demonstrates MCP capabilities." -) + ) +){ + "This server provides example resources and demonstrates MCP capabilities." +} // Add a resource server.addResource( @@ -158,8 +159,9 @@ fun Application.module() { resources = ServerCapabilities.Resources(subscribe = null, listChanged = null) ) ), - instructions = "This SSE server provides prompts and resources via Server-Sent Events." - ) + ) { + "This SSE server provides prompts and resources via Server-Sent Events." + } } } ``` @@ -187,8 +189,9 @@ fun Application.module() { resources = ServerCapabilities.Resources(subscribe = null, listChanged = null) ) ), - instructions = "Connect via SSE to interact with this MCP server." - ) + ) { + "Connect via SSE to interact with this MCP server." + } } } } diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt index e468fb49..95a5bc5b 100644 --- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt +++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt @@ -92,8 +92,9 @@ public open class Client(private val clientInfo: Implementation, options: Client private set /** - * Optional human-readable instructions or description for the client. - * This can be used to provide context or usage guidelines for users interacting with the client. + * Optional human-readable instructions or description from the server. + * + * @return Instructions provided by the server, or `null` if none were given or initialization is not yet complete. */ public var serverInstructions: String? = null private set From 86000543da9df06cd58142dd3df8ff4862cc2aa0 Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Thu, 2 Oct 2025 11:29:55 -0500 Subject: [PATCH 11/12] Adds alternative constructor that takes a static instructions string --- .../kotlin/sdk/server/Server.kt | 13 ++++++++++++ .../kotlin/sdk/server/ServerTest.kt | 21 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index 61ae3ea5..18283e47 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -62,6 +62,19 @@ public open class Server( protected val options: ServerOptions, protected val instructionsProvider: (() -> String)? = null, ) { + /** + * Alternative constructor that provides the instructions directly as a string. + * + * @param serverInfo Information about this server implementation (name, version). + * @param options Configuration options for the server. + * @param instructions Instructions from the server to the client about how to use this server. + */ + public constructor( + serverInfo: Implementation, + options: ServerOptions, + instructions: String, + ) : this(serverInfo, options, { instructions }) + private val sessions = atomic(persistentListOf()) @Suppress("ktlint:standard:backing-property-naming") diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt index 4f856a09..088444d6 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerTest.kt @@ -473,13 +473,32 @@ class ServerTest { assertEquals("Server does not support resources capability.", exception.message) } + @Test + fun `Server constructor should accept instructions provider parameter`() = runTest { + val serverInfo = Implementation(name = "test server", version = "1.0") + val serverOptions = ServerOptions(capabilities = ServerCapabilities()) + val instructions = "This is a test server. Use it for testing purposes only." + + val server = Server(serverInfo, serverOptions, { instructions }) + + // 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.connect(serverTransport) + client.connect(clientTransport) + + assertEquals(instructions, client.serverInstructions) + } + @Test fun `Server constructor should accept instructions parameter`() = runTest { val serverInfo = Implementation(name = "test server", version = "1.0") val serverOptions = ServerOptions(capabilities = ServerCapabilities()) val instructions = "This is a test server. Use it for testing purposes only." - val server = Server(serverInfo, serverOptions) { instructions } + val server = Server(serverInfo, serverOptions, instructions) // 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 From 7a02d567b57c31eb5635582afc0e6e51c80a008c Mon Sep 17 00:00:00 2001 From: Jeff Clyne Date: Fri, 3 Oct 2025 08:25:20 -0500 Subject: [PATCH 12/12] Updated API dump --- kotlin-sdk-server/api/kotlin-sdk-server.api | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-sdk-server/api/kotlin-sdk-server.api b/kotlin-sdk-server/api/kotlin-sdk-server.api index dd719bf0..8ee5af28 100644 --- a/kotlin-sdk-server/api/kotlin-sdk-server.api +++ b/kotlin-sdk-server/api/kotlin-sdk-server.api @@ -45,6 +45,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/server/RegisteredTool { } public class io/modelcontextprotocol/kotlin/sdk/server/Server { + public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Ljava/lang/String;)V public fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Lkotlin/jvm/functions/Function0;)V public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lio/modelcontextprotocol/kotlin/sdk/server/ServerOptions;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPrompt (Lio/modelcontextprotocol/kotlin/sdk/Prompt;Lkotlin/jvm/functions/Function2;)V