feat(mcp): Add builder for CreateMessageRequest (#60)

- Implements a builder pattern for CreateMessageRequest
- Updates corresponding tests to use the new builder syntax
- Add ModelPreferences builder and ModelHint helper
- Use builder pattern for CreateMessageRequest in integration tests

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov 2025-03-21 16:43:27 +01:00 • committed by GitHub
commit 33329bfabb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 163 additions and 32 deletions

View file

@ -24,6 +24,7 @@ import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
import io.modelcontextprotocol.spec.McpSchema.ModelPreferences;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.Root;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
@ -45,6 +46,7 @@ import org.springframework.web.reactive.function.server.RouterFunctions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
public class WebFluxSseIntegrationTests {
@ -142,13 +144,16 @@ public class WebFluxSseIntegrationTests {
McpServerFeatures.AsyncToolSpecification tool = new McpServerFeatures.AsyncToolSpecification(
new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema), (exchange, request) -> {
var messages = List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message")));
var modelPrefs = new McpSchema.ModelPreferences(List.of(), 1.0, 1.0, 1.0);
var craeteMessageRequest = new McpSchema.CreateMessageRequest(messages, modelPrefs, null,
McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, null, 100, List.of(),
Map.of());
var craeteMessageRequest = McpSchema.CreateMessageRequest.builder()
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message"))))
.modelPreferences(ModelPreferences.builder()
.hints(List.of())
.costPriority(1.0)
.speedPriority(1.0)
.intelligencePriority(1.0)
.build())
.build();
StepVerifier.create(exchange.createMessage(craeteMessageRequest)).consumeNextWith(result -> {
assertThat(result).isNotNull();

View file

@ -20,6 +20,7 @@ import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
import io.modelcontextprotocol.spec.McpSchema.ModelPreferences;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.Root;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
@ -45,6 +46,7 @@ import org.springframework.web.servlet.function.ServerResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
public class WebMvcSseIntegrationTests {
@ -199,13 +201,16 @@ public class WebMvcSseIntegrationTests {
McpServerFeatures.AsyncToolSpecification tool = new McpServerFeatures.AsyncToolSpecification(
new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema), (exchange, request) -> {
var messages = List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message")));
var modelPrefs = new McpSchema.ModelPreferences(List.of(), 1.0, 1.0, 1.0);
var craeteMessageRequest = new McpSchema.CreateMessageRequest(messages, modelPrefs, null,
McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, null, 100, List.of(),
Map.of());
var craeteMessageRequest = McpSchema.CreateMessageRequest.builder()
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message"))))
.modelPreferences(ModelPreferences.builder()
.hints(List.of())
.costPriority(1.0)
.speedPriority(1.0)
.intelligencePriority(1.0)
.build())
.build();
StepVerifier.create(exchange.createMessage(craeteMessageRequest)).consumeNextWith(result -> {
assertThat(result).isNotNull();

View file

@ -5,6 +5,7 @@
package io.modelcontextprotocol.spec;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -763,15 +764,61 @@ public final class McpSchema {
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
public record ModelPreferences(// @formatter:off
@JsonProperty("hints") List<ModelHint> hints,
@JsonProperty("costPriority") Double costPriority,
@JsonProperty("speedPriority") Double speedPriority,
@JsonProperty("intelligencePriority") Double intelligencePriority) {
} // @formatter:on
@JsonProperty("hints") List<ModelHint> hints,
@JsonProperty("costPriority") Double costPriority,
@JsonProperty("speedPriority") Double speedPriority,
@JsonProperty("intelligencePriority") Double intelligencePriority) {
public static Builder builder() {
return new Builder();
}
public static class Builder {
private List<ModelHint> hints;
private Double costPriority;
private Double speedPriority;
private Double intelligencePriority;
public Builder hints(List<ModelHint> hints) {
this.hints = hints;
return this;
}
public Builder addHint(String name) {
if (this.hints == null) {
this.hints = new ArrayList<>();
}
this.hints.add(new ModelHint(name));
return this;
}
public Builder costPriority(Double costPriority) {
this.costPriority = costPriority;
return this;
}
public Builder speedPriority(Double speedPriority) {
this.speedPriority = speedPriority;
return this;
}
public Builder intelligencePriority(Double intelligencePriority) {
this.intelligencePriority = intelligencePriority;
return this;
}
public ModelPreferences build() {
return new ModelPreferences(hints, costPriority, speedPriority, intelligencePriority);
}
}
} // @formatter:on
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
public record ModelHint(@JsonProperty("name") String name) {
public static ModelHint of(String name) {
return new ModelHint(name);
}
}
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@ -799,6 +846,66 @@ public final class McpSchema {
@JsonProperty("thisServer") THIS_SERVER,
@JsonProperty("allServers") ALL_SERVERS
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private List<SamplingMessage> messages;
private ModelPreferences modelPreferences;
private String systemPrompt;
private ContextInclusionStrategy includeContext;
private Double temperature;
private int maxTokens;
private List<String> stopSequences;
private Map<String, Object> metadata;
public Builder messages(List<SamplingMessage> messages) {
this.messages = messages;
return this;
}
public Builder modelPreferences(ModelPreferences modelPreferences) {
this.modelPreferences = modelPreferences;
return this;
}
public Builder systemPrompt(String systemPrompt) {
this.systemPrompt = systemPrompt;
return this;
}
public Builder includeContext(ContextInclusionStrategy includeContext) {
this.includeContext = includeContext;
return this;
}
public Builder temperature(Double temperature) {
this.temperature = temperature;
return this;
}
public Builder maxTokens(int maxTokens) {
this.maxTokens = maxTokens;
return this;
}
public Builder stopSequences(List<String> stopSequences) {
this.stopSequences = stopSequences;
return this;
}
public Builder metadata(Map<String, Object> metadata) {
this.metadata = metadata;
return this;
}
public CreateMessageRequest build() {
return new CreateMessageRequest(messages, modelPreferences, systemPrompt,
includeContext, temperature, maxTokens, stopSequences, metadata);
}
}
}// @formatter:on
@JsonInclude(JsonInclude.Include.NON_ABSENT)

View file

@ -21,6 +21,7 @@ import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
import io.modelcontextprotocol.spec.McpSchema.ModelPreferences;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.Root;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
@ -162,13 +163,16 @@ public class HttpServletSseServerTransportProviderIntegrationTests {
McpServerFeatures.AsyncToolSpecification tool = new McpServerFeatures.AsyncToolSpecification(
new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema), (exchange, request) -> {
var messages = List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message")));
var modelPrefs = new McpSchema.ModelPreferences(List.of(), 1.0, 1.0, 1.0);
var craeteMessageRequest = new McpSchema.CreateMessageRequest(messages, modelPrefs, null,
McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, null, 100, List.of(),
Map.of());
var craeteMessageRequest = McpSchema.CreateMessageRequest.builder()
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message"))))
.modelPreferences(ModelPreferences.builder()
.hints(List.of())
.costPriority(1.0)
.speedPriority(1.0)
.intelligencePriority(1.0)
.build())
.build();
StepVerifier.create(exchange.createMessage(craeteMessageRequest)).consumeNextWith(result -> {
assertThat(result).isNotNull();

View file

@ -524,10 +524,16 @@ public class McpSchemaTests {
Map<String, Object> metadata = new HashMap<>();
metadata.put("session", "test-session");
McpSchema.CreateMessageRequest request = new McpSchema.CreateMessageRequest(Collections.singletonList(message),
preferences, "You are a helpful assistant",
McpSchema.CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER, 0.7, 1000,
Arrays.asList("STOP", "END"), metadata);
McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder()
.messages(Collections.singletonList(message))
.modelPreferences(preferences)
.systemPrompt("You are a helpful assistant")
.includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER)
.temperature(0.7)
.maxTokens(1000)
.stopSequences(Arrays.asList("STOP", "END"))
.metadata(metadata)
.build();
String value = mapper.writeValueAsString(request);
@ -543,8 +549,12 @@ public class McpSchemaTests {
void testCreateMessageResult() throws Exception {
McpSchema.TextContent content = new McpSchema.TextContent("Assistant response");
McpSchema.CreateMessageResult result = new McpSchema.CreateMessageResult(McpSchema.Role.ASSISTANT, content,
"gpt-4", McpSchema.CreateMessageResult.StopReason.END_TURN);
McpSchema.CreateMessageResult result = McpSchema.CreateMessageResult.builder()
.role(McpSchema.Role.ASSISTANT)
.content(content)
.model("gpt-4")
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
.build();
String value = mapper.writeValueAsString(result);