From 44f722e102ed5a10300766bcdfb1e9d14ce52411 Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Wed, 9 Sep 2026 12:23:42 +0200 Subject: [PATCH] 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) --- .../blocks/configurations/JsonSchemaProducer.java | 4 ++++ .../configurations/MCPAgentBlockConfiguration.java | 4 +++- .../MCPAgentChatBlockConfiguration.java | 4 +++- .../annotations/SchemaAllowedValues.java | 11 +++++++++++ .../manager/controllers/BlocksControllerTest.java | 12 ++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/JsonSchemaProducer.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/JsonSchemaProducer.java index b4b4825..b39b203 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/JsonSchemaProducer.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/JsonSchemaProducer.java @@ -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); + } } } diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/MCPAgentBlockConfiguration.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/MCPAgentBlockConfiguration.java index dab66b7..ddb14a9 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/MCPAgentBlockConfiguration.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/MCPAgentBlockConfiguration.java @@ -124,7 +124,9 @@ public class MCPAgentBlockConfiguration extends BlockConfigurationHere 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 ""; } diff --git a/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java b/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java index f5eaecf..659f337 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java @@ -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()); }