Publish which of an enumerated field's values is the default

A field with a fixed set of values had no way to say which one it opens on, so the MCP server
dialog started with no source type chosen: nothing was selected, nothing said it had to be, and the
server could be saved that way. `@SchemaAllowedValues` now takes a `defaultValue`, emitted as the
schema's `default`, and both MCP configurations declare CATALOG - which is also the choice whose
own required fields the dialog can then enforce.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lucio Lelii 2026-09-09 12:23:42 +02:00
parent 8d6ddfecfb
commit 44f722e102
5 changed files with 33 additions and 2 deletions

View File

@ -1203,6 +1203,10 @@ public class JsonSchemaProducer {
for (String value : entry.getValue().value()) {
enumValues.add(value);
}
String defaultValue = entry.getValue().defaultValue();
if (defaultValue != null && !defaultValue.isBlank()) {
propertySchema.put("default", defaultValue);
}
}
}

View File

@ -124,7 +124,9 @@ public class MCPAgentBlockConfiguration extends BlockConfiguration<MCPAgentBlock
@JsonIgnoreProperties(ignoreUnknown = true)
public record MCPServerBinding(
@JsonProperty(required = false)
@SchemaAllowedValues({ "CATALOG", "CUSTOM" })
// The default lives in effectiveSourceType() below; declared here so the editor can say
// which one an empty field is using instead of only that it is using one.
@SchemaAllowedValues(value = { "CATALOG", "CUSTOM" }, defaultValue = "CATALOG")
String sourceType,
@JsonProperty(required = false)
@UiEnabledWhen(field = "sourceType", equalsAny = { "CATALOG", "" })

View File

@ -137,7 +137,9 @@ public class MCPAgentChatBlockConfiguration extends BlockConfiguration<MCPAgentC
@JsonIgnoreProperties(ignoreUnknown = true)
public record MCPServerBinding(
@JsonProperty(required = false)
@SchemaAllowedValues({ "CATALOG", "CUSTOM" })
// Same default as effectiveSourceType() below, declared for the editor - and the same
// one the agent block declares, so the two dialogs read alike.
@SchemaAllowedValues(value = { "CATALOG", "CUSTOM" }, defaultValue = "CATALOG")
String sourceType,
@JsonProperty(required = false)
@UiEnabledWhen(field = "sourceType", equalsAny = { "CATALOG", "" })

View File

@ -9,4 +9,15 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface SchemaAllowedValues {
String[] value();
/**
* Which of the values applies when the field is left unset, published as the schema's
* {@code default}.
*
* <p>Here rather than in an annotation of its own because it is the same statement: these are
* the values, and this is the one that holds when nobody chose. The editor needs it to say what
* an empty field is actually doing - without it, it can only say "using the default" and leave
* the reader to guess which.
*/
String defaultValue() default "";
}

View File

@ -573,6 +573,18 @@ public class BlocksControllerTest {
sharedSessionRef.path("x-retriever-url").asText());
assertEquals(UiContextKeys.FLOW_ID,
sharedSessionRef.path("x-retriever-depends-on").get(0).asText());
// The binding's sourceType has a default in Java (effectiveSourceType(): blank means
// CATALOG) and now declares it, so the editor can say which default an empty field is
// using instead of only that it is using one.
// The binding is a nested record, so it lives in the definitions and mcpServers.items is a
// $ref to it - the same shape the other nested-record assertions in this class walk.
JsonNode definitions = schema.has("definitions") ? schema.path("definitions") : schema.path("$defs");
JsonNode binding = definitions.path("MCPServerBinding");
JsonNode sourceType = binding.path("properties").path("sourceType");
assertEquals("CATALOG", sourceType.path("default").asText());
assertEquals("CATALOG", sourceType.path("enum").get(0).asText());
assertEquals("CUSTOM", sourceType.path("enum").get(1).asText());
assertTrue(sharedSessionRef.path("x-retriever-requires-auth").asBoolean());
}