fix: support freeform stopReason in sampling (#327)

- Add string values and JsonCreator method to StopReason enum
- Map unknown stop reason values to UNKNOWN constant for forward compatibility
- Add test coverage for unknown stop reason deserialization

Spec: f5ccad944f/schema/2024-11-05/schema.ts (L807)

Resolves #328
This commit is contained in:
Luca Chang 2025-06-19 14:02:27 -07:00 • committed by Christian Tzolov
commit aff24fdf2f
2 changed files with 37 additions and 3 deletions

View file

@ -6,10 +6,12 @@ package io.modelcontextprotocol.spec;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@ -1136,9 +1138,24 @@ public final class McpSchema {
@JsonProperty("stopReason") StopReason stopReason) {
public enum StopReason {
@JsonProperty("endTurn") END_TURN,
@JsonProperty("stopSequence") STOP_SEQUENCE,
@JsonProperty("maxTokens") MAX_TOKENS
@JsonProperty("endTurn") END_TURN("endTurn"),
@JsonProperty("stopSequence") STOP_SEQUENCE("stopSequence"),
@JsonProperty("maxTokens") MAX_TOKENS("maxTokens"),
@JsonProperty("unknown") UNKNOWN("unknown");
private final String value;
StopReason(String value) {
this.value = value;
}
@JsonCreator
private static StopReason of(String value) {
return Arrays.stream(StopReason.values())
.filter(stopReason -> stopReason.value.equals(value))
.findFirst()
.orElse(StopReason.UNKNOWN);
}
}
public static Builder builder() {

View file

@ -883,6 +883,23 @@ public class McpSchemaTests {
{"role":"assistant","content":{"type":"text","text":"Assistant response"},"model":"gpt-4","stopReason":"endTurn"}"""));
}
@Test
void testCreateMessageResultUnknownStopReason() throws Exception {
String input = """
{"role":"assistant","content":{"type":"text","text":"Assistant response"},"model":"gpt-4","stopReason":"arbitrary value"}""";
McpSchema.CreateMessageResult value = mapper.readValue(input, McpSchema.CreateMessageResult.class);
McpSchema.TextContent expectedContent = new McpSchema.TextContent("Assistant response");
McpSchema.CreateMessageResult expected = McpSchema.CreateMessageResult.builder()
.role(McpSchema.Role.ASSISTANT)
.content(expectedContent)
.model("gpt-4")
.stopReason(McpSchema.CreateMessageResult.StopReason.UNKNOWN)
.build();
assertThat(value).isEqualTo(expected);
}
// Elicitation Tests
@Test