Adds 'instructions' string to the Server and propagates to the InitializeResult (#290)

<!-- Provide a brief summary of your changes -->

## Motivation and Context
The
[schema](cca15204f6/schema/2025-03-26/schema.ts (L192))
for the InitializeResult contains an optional 'instructions' string

## Additional context
The instructions string is included in the schema for for version
"2025-03-26"

The instructions in the Server class are given as a provider function
with the result of the provider propagated to the underlying
ServerSession. As the ServerSession change is meant to allow for a
'Singleton scoped' server class, this provides the ability to
dynamically modify the instructions at the server level but have them be
immutable per session.
 
This also refactors the `serverInfo` and `instructions` properties to be
protected. This `Server` class is open and meant to be overridden, there
is no harm in having these immutable properties available to subclasses.

This adds the serverInstructions to the client as returned from the
InitializeResult. This is used to verify the response in the Server
tests.
This commit is contained in:
Jeff Clyne 2025-10-03 11:12:37 -05:00 • committed by GitHub
commit 2f1b659efd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 184 additions and 15 deletions

View file

@ -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 <init> (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/Implementation;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (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;

View file

@ -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

View file

@ -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<InitializeResult>(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<InitializeResult>(json)
assertEquals(LATEST_PROTOCOL_VERSION, decoded.protocolVersion)
assertEquals(serverInfo, decoded.serverInfo)
assertEquals(null, decoded.instructions)
}
}