Add schemas for form-based elicitation (#1020)
* Add StringSchema, BooleanSchema and NumberSchema * Add enum schemas for form-based elicitation matching SEP-1330 Closes #691 Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
99faac65f7
commit
fe82ad508a
1 changed files with 1531 additions and 31 deletions
|
|
@ -6,6 +6,7 @@ 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;
|
||||
|
|
@ -3888,6 +3889,817 @@ public final class McpSchema {
|
|||
}
|
||||
|
||||
// Elicitation
|
||||
|
||||
/**
|
||||
* An option in a titled enum schema, with a machine-readable value and a
|
||||
* human-readable display label.
|
||||
*
|
||||
* @param constValue The machine-readable value of the option
|
||||
* @param title The human-readable display label
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record EnumSchemaOption( // @formatter:off
|
||||
@JsonProperty("const") String constValue,
|
||||
@JsonProperty("title") String title) { // @formatter:on
|
||||
|
||||
public EnumSchemaOption {
|
||||
Assert.notNull(constValue, "constValue must not be null");
|
||||
Assert.notNull(title, "title must not be null");
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
static EnumSchemaOption fromJson(@JsonProperty("const") String constValue,
|
||||
@JsonProperty("title") String title) {
|
||||
if (constValue == null || title == null) {
|
||||
List<String> missing = new ArrayList<>();
|
||||
if (constValue == null) {
|
||||
missing.add("constValue -> ''");
|
||||
constValue = "";
|
||||
}
|
||||
if (title == null) {
|
||||
missing.add("title -> ''");
|
||||
title = "";
|
||||
}
|
||||
logger.warn("EnumSchemaOption: missing required fields during deserialization: {}",
|
||||
String.join(", ", missing));
|
||||
}
|
||||
return new EnumSchemaOption(constValue, title);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy enum schema with optional display names via the non-standard
|
||||
* {@code enumNames} property. Use {@link TitledSingleSelectEnumSchema} instead.
|
||||
*
|
||||
* @param title Optional title for the enum field
|
||||
* @param description Optional description for the enum field
|
||||
* @param enumValues Array of enum values to choose from
|
||||
* @param enumNames Optional display names for enum values (non-standard per JSON
|
||||
* Schema 2020-12)
|
||||
* @param defaultValue Optional default value
|
||||
* @deprecated Use {@link TitledSingleSelectEnumSchema} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record LegacyTitledEnumSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("enum") List<String> enumValues,
|
||||
@JsonProperty("enumNames") List<String> enumNames,
|
||||
@JsonProperty("default") String defaultValue) { // @formatter:on
|
||||
|
||||
public LegacyTitledEnumSchema {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private List<String> enumValues;
|
||||
|
||||
private List<String> enumNames;
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumValues(List<String> enumValues) {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
this.enumValues = new ArrayList<>(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumValues(String... enumValues) {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
this.enumValues = Arrays.asList(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumNames(List<String> enumNames) {
|
||||
Assert.notNull(enumNames, "enumNames must not be null");
|
||||
this.enumNames = new ArrayList<>(enumNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumNames(String... enumNames) {
|
||||
Assert.notNull(enumNames, "enumNames must not be null");
|
||||
this.enumNames = Arrays.asList(enumNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LegacyTitledEnumSchema build() {
|
||||
Assert.notEmpty(enumValues, "enumValues must not be empty");
|
||||
return new LegacyTitledEnumSchema(title, description, enumValues, enumNames, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for single-selection enumeration without display titles for options.
|
||||
*
|
||||
* @param title Optional title for the enum field
|
||||
* @param description Optional description for the enum field
|
||||
* @param enumValues Array of enum values to choose from
|
||||
* @param defaultValue Optional default value
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record UntitledSingleSelectEnumSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("enum") List<String> enumValues,
|
||||
@JsonProperty("default") String defaultValue) { // @formatter:on
|
||||
|
||||
public UntitledSingleSelectEnumSchema {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private List<String> enumValues;
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumValues(List<String> enumValues) {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
this.enumValues = new ArrayList<>(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumValues(String... enumValues) {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
this.enumValues = Arrays.asList(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UntitledSingleSelectEnumSchema build() {
|
||||
Assert.notEmpty(enumValues, "enumValues must not be empty");
|
||||
return new UntitledSingleSelectEnumSchema(title, description, enumValues, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for single-selection enumeration with display titles for each option.
|
||||
*
|
||||
* @param title Optional title for the enum field
|
||||
* @param description Optional description for the enum field
|
||||
* @param oneOf Array of enum options, each with a machine-readable value and a
|
||||
* human-readable display label
|
||||
* @param defaultValue Optional default value
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record TitledSingleSelectEnumSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("oneOf") List<EnumSchemaOption> oneOf,
|
||||
@JsonProperty("default") String defaultValue) { // @formatter:on
|
||||
|
||||
public TitledSingleSelectEnumSchema {
|
||||
Assert.notEmpty(oneOf, "oneOf must not be empty");
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private List<EnumSchemaOption> oneOf;
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder oneOf(List<EnumSchemaOption> oneOf) {
|
||||
Assert.notNull(oneOf, "oneOf must not be null");
|
||||
this.oneOf = new ArrayList<>(oneOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder oneOf(EnumSchemaOption... oneOf) {
|
||||
Assert.notNull(oneOf, "oneOf must not be null");
|
||||
this.oneOf = Arrays.asList(oneOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TitledSingleSelectEnumSchema build() {
|
||||
Assert.notEmpty(oneOf, "oneOf must not be empty");
|
||||
return new TitledSingleSelectEnumSchema(title, description, oneOf, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The items schema for {@link UntitledMultiSelectEnumSchema}, describing the allowed
|
||||
* enum values.
|
||||
*
|
||||
* @param enumValues Array of enum values to choose from
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record UntitledMultiSelectItems( // @formatter:off
|
||||
@JsonProperty("enum") List<String> enumValues) { // @formatter:on
|
||||
|
||||
public UntitledMultiSelectItems {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private List<String> enumValues;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder enumValues(List<String> enumValues) {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
this.enumValues = new ArrayList<>(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enumValues(String... enumValues) {
|
||||
Assert.notNull(enumValues, "enumValues must not be null");
|
||||
this.enumValues = Arrays.asList(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UntitledMultiSelectItems build() {
|
||||
Assert.notEmpty(enumValues, "enumValues must not be empty");
|
||||
return new UntitledMultiSelectItems(enumValues);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for multiple-selection enumeration without display titles for options.
|
||||
*
|
||||
* @param title Optional title for the enum field
|
||||
* @param description Optional description for the enum field
|
||||
* @param items Schema for the array items, containing the list of enum values
|
||||
* @param minItems Optional minimum number of items to select
|
||||
* @param maxItems Optional maximum number of items to select
|
||||
* @param defaultValue Optional default selected values
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record UntitledMultiSelectEnumSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("items") UntitledMultiSelectItems items,
|
||||
@JsonProperty("minItems") Integer minItems,
|
||||
@JsonProperty("maxItems") Integer maxItems,
|
||||
@JsonProperty("default") List<String> defaultValue) { // @formatter:on
|
||||
|
||||
public UntitledMultiSelectEnumSchema {
|
||||
Assert.notNull(items, "items must not be null");
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "array";
|
||||
}
|
||||
|
||||
public static Builder builder(UntitledMultiSelectItems items) {
|
||||
return new Builder(items);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private UntitledMultiSelectItems items;
|
||||
|
||||
private Integer minItems;
|
||||
|
||||
private Integer maxItems;
|
||||
|
||||
private List<String> defaultValue;
|
||||
|
||||
private Builder(UntitledMultiSelectItems items) {
|
||||
Assert.notNull(items, "items must not be null");
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder items(UntitledMultiSelectItems items) {
|
||||
Assert.notNull(items, "items must not be null");
|
||||
this.items = items;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder minItems(Integer minItems) {
|
||||
this.minItems = minItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxItems(Integer maxItems) {
|
||||
this.maxItems = maxItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaults(String... defaultValue) {
|
||||
Assert.notNull(defaultValue, "defaultValue must not be null");
|
||||
this.defaultValue = Arrays.asList(defaultValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaults(List<String> defaultValue) {
|
||||
Assert.notNull(defaultValue, "defaultValue must not be null");
|
||||
this.defaultValue = new ArrayList<>(defaultValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UntitledMultiSelectEnumSchema build() {
|
||||
return new UntitledMultiSelectEnumSchema(title, description, items, minItems, maxItems, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The items schema for {@link TitledMultiSelectEnumSchema}, describing the allowed
|
||||
* enum options with display labels.
|
||||
*
|
||||
* @param anyOf Array of enum options, each with a machine-readable value and a
|
||||
* human-readable display label
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record TitledMultiSelectItems( // @formatter:off
|
||||
@JsonProperty("anyOf") List<EnumSchemaOption> anyOf) { // @formatter:on
|
||||
|
||||
public TitledMultiSelectItems {
|
||||
Assert.notNull(anyOf, "anyOf must not be null");
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private List<EnumSchemaOption> anyOf;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder anyOf(List<EnumSchemaOption> anyOf) {
|
||||
Assert.notNull(anyOf, "anyOf must not be null");
|
||||
this.anyOf = new ArrayList<>(anyOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder anyOf(EnumSchemaOption... anyOf) {
|
||||
Assert.notNull(anyOf, "anyOf must not be null");
|
||||
this.anyOf = Arrays.asList(anyOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TitledMultiSelectItems build() {
|
||||
Assert.notEmpty(anyOf, "anyOf must not be empty");
|
||||
return new TitledMultiSelectItems(anyOf);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for multiple-selection enumeration with display titles for each option.
|
||||
*
|
||||
* @param title Optional title for the enum field
|
||||
* @param description Optional description for the enum field
|
||||
* @param items Schema for the array items, containing the list of titled enum options
|
||||
* @param minItems Optional minimum number of items to select
|
||||
* @param maxItems Optional maximum number of items to select
|
||||
* @param defaultValue Optional default selected values
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record TitledMultiSelectEnumSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("items") TitledMultiSelectItems items,
|
||||
@JsonProperty("minItems") Integer minItems,
|
||||
@JsonProperty("maxItems") Integer maxItems,
|
||||
@JsonProperty("default") List<String> defaultValue) { // @formatter:on
|
||||
|
||||
public TitledMultiSelectEnumSchema {
|
||||
Assert.notNull(items, "items must not be null");
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "array";
|
||||
}
|
||||
|
||||
public static Builder builder(TitledMultiSelectItems items) {
|
||||
return new Builder(items);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private TitledMultiSelectItems items;
|
||||
|
||||
private Integer minItems;
|
||||
|
||||
private Integer maxItems;
|
||||
|
||||
private List<String> defaultValue;
|
||||
|
||||
private Builder(TitledMultiSelectItems items) {
|
||||
Assert.notNull(items, "items must not be null");
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder items(TitledMultiSelectItems items) {
|
||||
Assert.notNull(items, "items must not be null");
|
||||
this.items = items;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder minItems(Integer minItems) {
|
||||
this.minItems = minItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxItems(Integer maxItems) {
|
||||
this.maxItems = maxItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaults(List<String> defaultValue) {
|
||||
Assert.notNull(defaultValue, "defaultValue must not be null");
|
||||
this.defaultValue = new ArrayList<>(defaultValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaults(String... defaultValue) {
|
||||
Assert.notNull(defaultValue, "defaultValue must not be null");
|
||||
this.defaultValue = Arrays.asList(defaultValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TitledMultiSelectEnumSchema build() {
|
||||
return new TitledMultiSelectEnumSchema(title, description, items, minItems, maxItems, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for a boolean field in a form-based elicitation request.
|
||||
*
|
||||
* @param title Optional title for the boolean field
|
||||
* @param description Optional description for the boolean field
|
||||
* @param defaultValue Optional default value
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record BooleanSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("default") Boolean defaultValue) { // @formatter:on
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "boolean";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private Boolean defaultValue;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultValue(Boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BooleanSchema build() {
|
||||
return new BooleanSchema(title, description, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for a numeric field in a form-based elicitation request, supporting both
|
||||
* {@code "number"} (floating-point) and {@code "integer"} types.
|
||||
*
|
||||
* @param title Optional title for the numeric field
|
||||
* @param description Optional description for the numeric field
|
||||
* @param type The JSON Schema type, either {@code "number"} or {@code "integer"};
|
||||
* defaults to {@code "number"} in the builder
|
||||
* @param minimum Optional minimum value (inclusive)
|
||||
* @param maximum Optional maximum value (inclusive)
|
||||
* @param defaultValue Optional default value
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record NumberSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("minimum") Number minimum,
|
||||
@JsonProperty("maximum") Number maximum,
|
||||
@JsonProperty("default") Number defaultValue) { // @formatter:on
|
||||
|
||||
public NumberSchema {
|
||||
Assert.notNull(type, "type must not be null");
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private String type = "number";
|
||||
|
||||
private Number minimum;
|
||||
|
||||
private Number maximum;
|
||||
|
||||
private Number defaultValue;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder integer() {
|
||||
this.type = "integer";
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder minimum(Number minimum) {
|
||||
this.minimum = minimum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maximum(Number maximum) {
|
||||
this.maximum = maximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultValue(Number defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NumberSchema build() {
|
||||
return new NumberSchema(title, description, type, minimum, maximum, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for a text input field in a form-based elicitation request.
|
||||
*
|
||||
* @param title Optional title for the text field
|
||||
* @param description Optional description for the text field
|
||||
* @param minLength Optional minimum string length
|
||||
* @param maxLength Optional maximum string length
|
||||
* @param format Optional format hint (e.g. {@code "email"}, {@code "uri"})
|
||||
* @param defaultValue Optional default value
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record StringSchema( // @formatter:off
|
||||
@JsonProperty("title") String title,
|
||||
@JsonProperty("description") String description,
|
||||
@JsonProperty("minLength") Integer minLength,
|
||||
@JsonProperty("maxLength") Integer maxLength,
|
||||
@JsonProperty("format") String format,
|
||||
@JsonProperty("default") String defaultValue) { // @formatter:on
|
||||
|
||||
@JsonProperty("type")
|
||||
public String type() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
private Integer minLength;
|
||||
|
||||
private Integer maxLength;
|
||||
|
||||
private String format;
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder minLength(Integer minLength) {
|
||||
this.minLength = minLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxLength(Integer maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder format(String format) {
|
||||
this.format = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StringSchema build() {
|
||||
Assert.isTrue(
|
||||
format == null || format.equals("uri") || format.equals("email") || format.equals("date")
|
||||
|| format.equals("date-time"),
|
||||
"format must be one of: null, \"uri\", \"email\", \"date\", \"date-time\"");
|
||||
return new StringSchema(title, description, minLength, maxLength, format, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request from the server to elicit additional information from the user, either
|
||||
* through the client or out-of-band.
|
||||
|
|
@ -3930,13 +4742,48 @@ public final class McpSchema {
|
|||
/**
|
||||
* A request from the server to elicit additional information from the user via the
|
||||
* client, using {@code form} mode.
|
||||
* <p>
|
||||
* The requested schema is flexible, but for standard schemas, consider using one the
|
||||
* following types:
|
||||
* <ul>
|
||||
* <li>{@link BooleanSchema}
|
||||
* <li>{@link NumberSchema}
|
||||
* <li>{@link StringSchema}
|
||||
* <li>{@link LegacyTitledEnumSchema}
|
||||
* <li>{@link TitledSingleSelectEnumSchema}
|
||||
* <li>{@link TitledMultiSelectEnumSchema}
|
||||
* <li>{@link UntitledSingleSelectEnumSchema}
|
||||
* <li>{@link UntitledMultiSelectEnumSchema}
|
||||
* </ul>
|
||||
*
|
||||
* These can be used with a JSON mapper:
|
||||
*
|
||||
* <pre>
|
||||
* var mapper = McpJsonDefaults.getMapper();
|
||||
* TypeRef<Map<String, Object>> mapType = new TypeRef<>() { };
|
||||
* var first = UntitledSingleSelectEnumSchema.builder()
|
||||
* .enumValues("option1", "option2", "option3")
|
||||
* .build();
|
||||
* var second = BooleanSchema
|
||||
* .builder()
|
||||
* .title("Say yes")
|
||||
* .description("By selecting this, you say yes to the thing")
|
||||
* .build();
|
||||
* Map<String, Object> requestedSchema = Map.of(
|
||||
* "type", "object",
|
||||
* "properties", Map.of(
|
||||
* "first-thing", mapper.convertValue(first, mapType),
|
||||
* "second-thing", mapper.convertValue(second, mapType)),
|
||||
* "required", List.of("first-thing", "second-thing"));
|
||||
* </pre>
|
||||
*
|
||||
* @param message The message to present to the user
|
||||
* @param requestedSchema A restricted subset of JSON Schema. Only top-level
|
||||
* properties are allowed, without nesting. Per SEP-1613, the dialect defaults to JSON
|
||||
* Schema 2020-12 ({@link #JSON_SCHEMA_DIALECT_2020_12}) when no explicit
|
||||
* {@code $schema} entry is present. To declare a different dialect, include a
|
||||
* {@code "$schema"} key in the map.
|
||||
* {@code "$schema"} key in the map. For type-safety in the schemas, use one of the
|
||||
* supported schema types.
|
||||
* @param meta See specification for notes on _meta usage
|
||||
* <p>
|
||||
* Note: {@code message} and {@code requestedSchema} are required by the MCP
|
||||
|
|
|
|||
Loading…
Reference in a new issue