feat!: SEP-1577 sampling with tools (#718)

Implements SEP-1577 sampling With tools

closes #409 

## How Has This Been Tested?
unit + integration 

## Breaking Changes
- binary changes
- MediaContent -> SamplingMessageContent
- 

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
- [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
This commit is contained in:
Pavel Gorgulov 2026-04-28 17:51:48 +02:00 • committed by GitHub
commit f434fcaff1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -0,0 +1,98 @@
package io.modelcontextprotocol.kotlin.sdk.client
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
import io.modelcontextprotocol.kotlin.sdk.types.McpException
import io.modelcontextprotocol.kotlin.sdk.types.RPCError
import io.modelcontextprotocol.kotlin.sdk.types.Role
import io.modelcontextprotocol.kotlin.sdk.types.SamplingMessage
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
import io.modelcontextprotocol.kotlin.sdk.types.Tool
import io.modelcontextprotocol.kotlin.sdk.types.ToolChoice
import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
import kotlinx.serialization.json.buildJsonObject
import org.junit.jupiter.api.assertDoesNotThrow
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class ClientSamplingValidationTest {
private val dummyTool = Tool(
name = "t",
inputSchema = ToolSchema(properties = buildJsonObject { }, required = emptyList()),
)
private val minimalMessages = listOf(SamplingMessage(Role.User, TextContent("hi")))
private val noToolsCaps = ClientCapabilities(sampling = ClientCapabilities.sampling)
private val withToolsCaps = ClientCapabilities(
sampling = ClientCapabilities.Sampling(tools = EmptyJsonObject),
)
@Test
fun `request without tools or toolChoice is always accepted`() {
val request = CreateMessageRequest(
CreateMessageRequestParams(maxTokens = 10, messages = minimalMessages),
)
assertDoesNotThrow { validateSamplingToolsCapability(request, noToolsCaps) }
}
@Test
fun `tools without sampling tools capability throws InvalidParams`() {
val request = CreateMessageRequest(
CreateMessageRequestParams(
maxTokens = 10,
messages = minimalMessages,
tools = listOf(dummyTool),
),
)
val exception = assertFailsWith<McpException> {
validateSamplingToolsCapability(request, noToolsCaps)
}
assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
check(exception.message?.contains("tools") == true)
}
@Test
fun `toolChoice without sampling tools capability throws InvalidParams`() {
val request = CreateMessageRequest(
CreateMessageRequestParams(
maxTokens = 10,
messages = minimalMessages,
toolChoice = ToolChoice(mode = ToolChoice.Mode.Required),
),
)
val exception = assertFailsWith<McpException> {
validateSamplingToolsCapability(request, noToolsCaps)
}
assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
check(exception.message?.contains("toolChoice") == true)
}
@Test
fun `tools with sampling tools capability is accepted`() {
val request = CreateMessageRequest(
CreateMessageRequestParams(
maxTokens = 10,
messages = minimalMessages,
tools = listOf(dummyTool),
),
)
assertDoesNotThrow { validateSamplingToolsCapability(request, withToolsCaps) }
}
@Test
fun `toolChoice with sampling tools capability is accepted`() {
val request = CreateMessageRequest(
CreateMessageRequestParams(
maxTokens = 10,
messages = minimalMessages,
toolChoice = ToolChoice(mode = ToolChoice.Mode.Auto),
),
)
assertDoesNotThrow { validateSamplingToolsCapability(request, withToolsCaps) }
}
}