Skip to content

Commit f4ef858

Browse files
committed
Add enum schemas for form-based elicitation matching SEP-1330
Closes #691 Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
1 parent df75857 commit f4ef858

3 files changed

Lines changed: 952 additions & 31 deletions

File tree

conformance-tests/server-servlet/src/main/java/io/modelcontextprotocol/conformance/server/ConformanceServlet.java

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
import java.util.List;
66
import java.util.Map;
77

8+
import io.modelcontextprotocol.json.McpJsonDefaults;
9+
import io.modelcontextprotocol.json.TypeRef;
810
import io.modelcontextprotocol.server.McpServer;
911
import io.modelcontextprotocol.server.McpServerFeatures;
1012
import io.modelcontextprotocol.server.transport.DefaultServerTransportSecurityValidator;
1113
import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider;
12-
import io.modelcontextprotocol.spec.McpSchema;
1314
import io.modelcontextprotocol.spec.McpSchema.AudioContent;
1415
import io.modelcontextprotocol.spec.McpSchema.BlobResourceContents;
1516
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
@@ -43,6 +44,16 @@
4344
import org.slf4j.Logger;
4445
import org.slf4j.LoggerFactory;
4546

47+
import static io.modelcontextprotocol.spec.McpSchema.EnumSchemaOption;
48+
import static io.modelcontextprotocol.spec.McpSchema.JSON_SCHEMA_DIALECT_2020_12;
49+
import static io.modelcontextprotocol.spec.McpSchema.LegacyTitledEnumSchema;
50+
import static io.modelcontextprotocol.spec.McpSchema.TitledMultiSelectEnumSchema;
51+
import static io.modelcontextprotocol.spec.McpSchema.TitledMultiSelectItems;
52+
import static io.modelcontextprotocol.spec.McpSchema.TitledSingleSelectEnumSchema;
53+
import static io.modelcontextprotocol.spec.McpSchema.UntitledMultiSelectEnumSchema;
54+
import static io.modelcontextprotocol.spec.McpSchema.UntitledMultiSelectItems;
55+
import static io.modelcontextprotocol.spec.McpSchema.UntitledSingleSelectEnumSchema;
56+
4657
public class ConformanceServlet {
4758

4859
private static final Logger logger = LoggerFactory.getLogger(ConformanceServlet.class);
@@ -141,6 +152,7 @@ private static Tomcat createEmbeddedTomcat(HttpServletStreamableServerTransportP
141152
return tomcat;
142153
}
143154

155+
@SuppressWarnings("deprecation")
144156
private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
145157
return List.of(
146158
// test_simple_text - Returns simple text content
@@ -406,8 +418,8 @@ private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
406418
// json_schema_2020_12_tool - SEP-1613 dialect/keyword preservation
407419
McpServerFeatures.SyncToolSpecification.builder()
408420
.tool(Tool
409-
.builder("json_schema_2020_12_tool", Map.of("$schema", McpSchema.JSON_SCHEMA_DIALECT_2020_12,
410-
"type", "object", "$defs",
421+
.builder("json_schema_2020_12_tool", Map.of("$schema", JSON_SCHEMA_DIALECT_2020_12, "type",
422+
"object", "$defs",
411423
Map.of("address",
412424
Map.of("type", "object", "properties",
413425
Map.of("street", Map.of("type", "string"), "city",
@@ -434,33 +446,44 @@ private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
434446
.callHandler((exchange, request) -> {
435447
logger.info("Tool 'test_elicitation_sep1330_enums' called");
436448

437-
// Create schema with all 5 enum variants
438-
Map<String, Object> requestedSchema = Map.of("type", "object", "properties", Map.of(
439-
// 1. Untitled single-select
440-
"untitledSingle",
441-
Map.of("type", "string", "enum", List.of("option1", "option2", "option3")),
442-
// 2. Titled single-select using oneOf with const/title
443-
"titledSingle",
444-
Map.of("type", "string", "oneOf",
445-
List.of(Map.of("const", "value1", "title", "First Option"),
446-
Map.of("const", "value2", "title", "Second Option"),
447-
Map.of("const", "value3", "title", "Third Option"))),
448-
// 3. Legacy titled using enumNames (deprecated)
449-
"legacyEnum",
450-
Map.of("type", "string", "enum", List.of("opt1", "opt2", "opt3"), "enumNames",
451-
List.of("Option One", "Option Two", "Option Three")),
452-
// 4. Untitled multi-select
453-
"untitledMulti",
454-
Map.of("type", "array", "items",
455-
Map.of("type", "string", "enum", List.of("option1", "option2", "option3"))),
456-
// 5. Titled multi-select using items.anyOf with
457-
// const/title
458-
"titledMulti",
459-
Map.of("type", "array", "items",
460-
Map.of("anyOf",
461-
List.of(Map.of("const", "value1", "title", "First Choice"),
462-
Map.of("const", "value2", "title", "Second Choice"),
463-
Map.of("const", "value3", "title", "Third Choice"))))),
449+
TypeRef<Map<String, Object>> mapType = new TypeRef<>() {
450+
};
451+
var mapper = McpJsonDefaults.getMapper();
452+
453+
// 1. Untitled single-select
454+
var untitledSingle = UntitledSingleSelectEnumSchema.builder()
455+
.enumValues("option1", "option2", "option3")
456+
.build();
457+
// 2. Titled single-select using oneOf with const/title
458+
var titledSingle = TitledSingleSelectEnumSchema.builder()
459+
.oneOf(EnumSchemaOption.builder("value1", "First Option").build(),
460+
EnumSchemaOption.builder("value2", "Second Option").build(),
461+
EnumSchemaOption.builder("value3", "Third Option").build())
462+
.build();
463+
// 3. Legacy titled using enumNames (deprecated)
464+
var legacyEnum = LegacyTitledEnumSchema.builder()
465+
.enumValues("opt1", "opt2", "opt3")
466+
.enumNames("Option One", "Option Two", "Option Three")
467+
.build();
468+
// 4. Untitled multi-select
469+
var untitledMulti = UntitledMultiSelectEnumSchema.builder(
470+
UntitledMultiSelectItems.builder().enumValues("option1", "option2", "option3").build())
471+
.build();
472+
// 5. Titled multi-select using items.anyOf with const/title
473+
var titledMulti = TitledMultiSelectEnumSchema
474+
.builder(TitledMultiSelectItems.builder()
475+
.anyOf(EnumSchemaOption.builder("value1", "First Choice").build(),
476+
EnumSchemaOption.builder("value2", "Second Choice").build(),
477+
EnumSchemaOption.builder("value3", "Third Choice").build())
478+
.build())
479+
.build();
480+
481+
Map<String, Object> requestedSchema = Map.of("type", "object", "properties",
482+
Map.of("untitledSingle", mapper.convertValue(untitledSingle, mapType), "titledSingle",
483+
mapper.convertValue(titledSingle, mapType), "legacyEnum",
484+
mapper.convertValue(legacyEnum, mapType), "untitledMulti",
485+
mapper.convertValue(untitledMulti, mapType), "titledMulti",
486+
mapper.convertValue(titledMulti, mapType)),
464487
"required", List.of("untitledSingle", "titledSingle", "legacyEnum", "untitledMulti",
465488
"titledMulti"));
466489

0 commit comments

Comments
 (0)