From 22f7acb630272f0abf0e0145c68f06bbc1741173 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Mon, 6 Apr 2026 10:41:31 -0700 Subject: [PATCH 1/5] Fix CLI/Schema mismatch bug --- src/Cli/Commands/AutoConfigOptions.cs | 6 +++--- src/Cli/ConfigGenerator.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Cli/Commands/AutoConfigOptions.cs b/src/Cli/Commands/AutoConfigOptions.cs index 41227cd03a..1931978205 100644 --- a/src/Cli/Commands/AutoConfigOptions.cs +++ b/src/Cli/Commands/AutoConfigOptions.cs @@ -39,7 +39,7 @@ public AutoConfigOptions( PatternsInclude = patternsInclude; PatternsExclude = patternsExclude; PatternsName = patternsName; - TemplateMcpDmlTool = templateMcpDmlTool; + TemplateMcpDmlTools = templateMcpDmlTool; TemplateRestEnabled = templateRestEnabled; TemplateGraphqlEnabled = templateGraphqlEnabled; TemplateCacheEnabled = templateCacheEnabled; @@ -61,8 +61,8 @@ public AutoConfigOptions( [Option("patterns.name", Required = false, HelpText = "Interpolation syntax for entity naming (must be unique for each generated entity). Default: '{object}'")] public string? PatternsName { get; } - [Option("template.mcp.dml-tool", Required = false, HelpText = "Enable/disable DML tools for generated entities. Allowed values: true, false. Default: true")] - public string? TemplateMcpDmlTool { get; } + [Option("template.mcp.dml-tools", Required = false, HelpText = "Enable/disable DML tools for generated entities. Allowed values: true, false. Default: true")] + public string? TemplateMcpDmlTools { get; } [Option("template.rest.enabled", Required = false, HelpText = "Enable/disable REST endpoint for generated entities. Allowed values: true, false. Default: true")] public bool? TemplateRestEnabled { get; } diff --git a/src/Cli/ConfigGenerator.cs b/src/Cli/ConfigGenerator.cs index 8e95eb6e5d..1d55309e31 100644 --- a/src/Cli/ConfigGenerator.cs +++ b/src/Cli/ConfigGenerator.cs @@ -3109,11 +3109,11 @@ private static AutoentityPatterns BuildAutoentityPatterns(AutoConfigOptions opti bool userProvidedCache = existingAutoentity?.Template.UserProvidedCacheOptions ?? false; // Update MCP options - if (!string.IsNullOrWhiteSpace(options.TemplateMcpDmlTool)) + if (!string.IsNullOrWhiteSpace(options.TemplateMcpDmlTools)) { - if (!bool.TryParse(options.TemplateMcpDmlTool, out bool mcpDmlToolValue)) + if (!bool.TryParse(options.TemplateMcpDmlTools, out bool mcpDmlToolValue)) { - _logger.LogError("Invalid value for template.mcp.dml-tool: {value}. Valid values are: true, false", options.TemplateMcpDmlTool); + _logger.LogError("Invalid value for template.mcp.dml-tools: {value}. Valid values are: true, false", options.TemplateMcpDmlTools); return null; } @@ -3122,7 +3122,7 @@ private static AutoentityPatterns BuildAutoentityPatterns(AutoConfigOptions opti bool? dmlToolValue = mcpDmlToolValue; mcp = new EntityMcpOptions(customToolEnabled: customToolEnabled, dmlToolsEnabled: dmlToolValue); userProvidedMcp = true; - _logger.LogInformation("Updated template.mcp.dml-tool for definition '{DefinitionName}'", options.DefinitionName); + _logger.LogInformation("Updated template.mcp.dml-tools for definition '{DefinitionName}'", options.DefinitionName); } // Update REST options From ff187b0af235f07873acc3b6ea94abddaad00c08 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Mon, 6 Apr 2026 11:29:59 -0700 Subject: [PATCH 2/5] Fix autoentities validation bug --- src/Cli/ConfigGenerator.cs | 68 ++++++++++++------- .../MsSqlMetadataProvider.cs | 2 +- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/Cli/ConfigGenerator.cs b/src/Cli/ConfigGenerator.cs index 1d55309e31..00e1b90a8a 100644 --- a/src/Cli/ConfigGenerator.cs +++ b/src/Cli/ConfigGenerator.cs @@ -2646,45 +2646,63 @@ public static bool IsConfigValid(ValidateOptions options, FileSystemRuntimeConfi ILogger runtimeConfigValidatorLogger = LoggerFactoryForCli.CreateLogger(); RuntimeConfigValidator runtimeConfigValidator = new(runtimeConfigProvider, fileSystem, runtimeConfigValidatorLogger, true); - bool isValid = runtimeConfigValidator.TryValidateConfig(runtimeConfigFile, LoggerFactoryForCli).Result; - - // Additional validation: warn if fields are missing and MCP is enabled - if (isValid) + bool isValid = false; + try { - if (runtimeConfigProvider.TryGetConfig(out RuntimeConfig? config) && config is not null) + isValid = runtimeConfigValidator.TryValidateConfig(runtimeConfigFile, LoggerFactoryForCli).Result; + + // Additional validation: warn if fields are missing and MCP is enabled + if (isValid) { - bool mcpEnabled = config.IsMcpEnabled; - if (mcpEnabled) + if (runtimeConfigProvider.TryGetConfig(out RuntimeConfig? config) && config is not null) { - foreach (KeyValuePair entity in config.Entities) + bool mcpEnabled = config.IsMcpEnabled; + if (mcpEnabled) { - if (entity.Value.Fields == null || !entity.Value.Fields.Any()) + foreach (KeyValuePair entity in config.Entities) { - _logger.LogWarning($"Entity '{entity.Key}' is missing 'fields' definition while MCP is enabled. " + - "It's recommended to define fields explicitly to ensure optimal performance with MCP."); + if (entity.Value.Fields == null || !entity.Value.Fields.Any()) + { + _logger.LogWarning($"Entity '{entity.Key}' is missing 'fields' definition while MCP is enabled. " + + "It's recommended to define fields explicitly to ensure optimal performance with MCP."); + } } } - } - // Warn if Unauthenticated provider is used with authenticated or custom roles - if (config.Runtime?.Host?.Authentication?.IsUnauthenticatedAuthenticationProvider() == true) - { - bool hasNonAnonymousRoles = config.Entities - .Where(e => e.Value.Permissions is not null) - .SelectMany(e => e.Value.Permissions!) - .Any(p => !p.Role.Equals("anonymous", StringComparison.OrdinalIgnoreCase)); - - if (hasNonAnonymousRoles) + // Warn if Unauthenticated provider is used with authenticated or custom roles + if (config.Runtime?.Host?.Authentication?.IsUnauthenticatedAuthenticationProvider() == true) { - _logger.LogWarning( - "Authentication provider is 'Unauthenticated' but some entities have permissions configured for non-anonymous roles. " + - "All requests will be treated as anonymous."); + bool hasNonAnonymousRoles = config.Entities + .Where(e => e.Value.Permissions is not null) + .SelectMany(e => e.Value.Permissions!) + .Any(p => !p.Role.Equals("anonymous", StringComparison.OrdinalIgnoreCase)); + + if (hasNonAnonymousRoles) + { + _logger.LogWarning( + "Authentication provider is 'Unauthenticated' but some entities have permissions configured for non-anonymous roles. " + + "All requests will be treated as anonymous."); + } } } } + + return isValid; } + catch (AggregateException ex) + { + foreach (Exception exception in ex.InnerExceptions) + { + _logger.LogError(exception, exception.Message); + } - return isValid; + return isValid; + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return isValid; + } } /// diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index f85249b69e..23d12ec31f 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -357,7 +357,7 @@ protected override async Task GenerateAutoentitiesIntoEntities(IReadOnlyDictiona if (!entities.TryAdd(entityName, generatedEntity) || !runtimeConfig.TryAddGeneratedAutoentityNameToDataSourceName(entityName, autoentityName)) { throw new DataApiBuilderException( - message: $"Entity with name '{entityName}' already exists. Cannot create new entity from autoentities definition '{autoentityName}'.", + message: $"Entity '{entityName}' conflicts with autoentity pattern '{autoentityName}'. Use --patterns.exclude to skip it.", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); } From aa08f8353447bd99e9aaa3fd68b49c07ba8e352a Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 8 Apr 2026 12:09:38 -0700 Subject: [PATCH 3/5] Revert validation changes --- src/Cli/ConfigGenerator.cs | 68 ++++++++++++++------------------------ 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/src/Cli/ConfigGenerator.cs b/src/Cli/ConfigGenerator.cs index 00e1b90a8a..1d55309e31 100644 --- a/src/Cli/ConfigGenerator.cs +++ b/src/Cli/ConfigGenerator.cs @@ -2646,63 +2646,45 @@ public static bool IsConfigValid(ValidateOptions options, FileSystemRuntimeConfi ILogger runtimeConfigValidatorLogger = LoggerFactoryForCli.CreateLogger(); RuntimeConfigValidator runtimeConfigValidator = new(runtimeConfigProvider, fileSystem, runtimeConfigValidatorLogger, true); - bool isValid = false; - try - { - isValid = runtimeConfigValidator.TryValidateConfig(runtimeConfigFile, LoggerFactoryForCli).Result; + bool isValid = runtimeConfigValidator.TryValidateConfig(runtimeConfigFile, LoggerFactoryForCli).Result; - // Additional validation: warn if fields are missing and MCP is enabled - if (isValid) + // Additional validation: warn if fields are missing and MCP is enabled + if (isValid) + { + if (runtimeConfigProvider.TryGetConfig(out RuntimeConfig? config) && config is not null) { - if (runtimeConfigProvider.TryGetConfig(out RuntimeConfig? config) && config is not null) + bool mcpEnabled = config.IsMcpEnabled; + if (mcpEnabled) { - bool mcpEnabled = config.IsMcpEnabled; - if (mcpEnabled) + foreach (KeyValuePair entity in config.Entities) { - foreach (KeyValuePair entity in config.Entities) + if (entity.Value.Fields == null || !entity.Value.Fields.Any()) { - if (entity.Value.Fields == null || !entity.Value.Fields.Any()) - { - _logger.LogWarning($"Entity '{entity.Key}' is missing 'fields' definition while MCP is enabled. " + - "It's recommended to define fields explicitly to ensure optimal performance with MCP."); - } + _logger.LogWarning($"Entity '{entity.Key}' is missing 'fields' definition while MCP is enabled. " + + "It's recommended to define fields explicitly to ensure optimal performance with MCP."); } } + } - // Warn if Unauthenticated provider is used with authenticated or custom roles - if (config.Runtime?.Host?.Authentication?.IsUnauthenticatedAuthenticationProvider() == true) - { - bool hasNonAnonymousRoles = config.Entities - .Where(e => e.Value.Permissions is not null) - .SelectMany(e => e.Value.Permissions!) - .Any(p => !p.Role.Equals("anonymous", StringComparison.OrdinalIgnoreCase)); + // Warn if Unauthenticated provider is used with authenticated or custom roles + if (config.Runtime?.Host?.Authentication?.IsUnauthenticatedAuthenticationProvider() == true) + { + bool hasNonAnonymousRoles = config.Entities + .Where(e => e.Value.Permissions is not null) + .SelectMany(e => e.Value.Permissions!) + .Any(p => !p.Role.Equals("anonymous", StringComparison.OrdinalIgnoreCase)); - if (hasNonAnonymousRoles) - { - _logger.LogWarning( - "Authentication provider is 'Unauthenticated' but some entities have permissions configured for non-anonymous roles. " + - "All requests will be treated as anonymous."); - } + if (hasNonAnonymousRoles) + { + _logger.LogWarning( + "Authentication provider is 'Unauthenticated' but some entities have permissions configured for non-anonymous roles. " + + "All requests will be treated as anonymous."); } } } - - return isValid; } - catch (AggregateException ex) - { - foreach (Exception exception in ex.InnerExceptions) - { - _logger.LogError(exception, exception.Message); - } - return isValid; - } - catch (Exception ex) - { - _logger.LogError(ex, ex.Message); - return isValid; - } + return isValid; } /// From 2ad225861e5a2725b1385bdbd47ea7088934788a Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 8 Apr 2026 14:25:08 -0700 Subject: [PATCH 4/5] Changes based on comments --- src/Cli.Tests/AutoConfigTests.cs | 4 ++-- src/Cli/Commands/AutoConfigOptions.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cli.Tests/AutoConfigTests.cs b/src/Cli.Tests/AutoConfigTests.cs index 40e3a461f7..7bd5487305 100644 --- a/src/Cli.Tests/AutoConfigTests.cs +++ b/src/Cli.Tests/AutoConfigTests.cs @@ -80,7 +80,7 @@ public void TestConfigureAutoentitiesDefinition_WithTemplateOptions() definitionName: "test-def", templateRestEnabled: true, templateGraphqlEnabled: false, - templateMcpDmlTool: "true", + templateMcpDmlTools: "true", templateCacheEnabled: true, templateCacheTtlSeconds: 30, templateCacheLevel: "L1", @@ -196,7 +196,7 @@ public void TestConfigureAutoentitiesDefinition_InvalidMcpDmlTool() AutoConfigOptions options = new( definitionName: "test-def", - templateMcpDmlTool: "invalid-value", + templateMcpDmlTools: "invalid-value", permissions: new[] { "anonymous", "read" }, config: TEST_RUNTIME_CONFIG_FILE ); diff --git a/src/Cli/Commands/AutoConfigOptions.cs b/src/Cli/Commands/AutoConfigOptions.cs index 1931978205..41be943303 100644 --- a/src/Cli/Commands/AutoConfigOptions.cs +++ b/src/Cli/Commands/AutoConfigOptions.cs @@ -24,7 +24,7 @@ public AutoConfigOptions( IEnumerable? patternsInclude = null, IEnumerable? patternsExclude = null, string? patternsName = null, - string? templateMcpDmlTool = null, + string? templateMcpDmlTools = null, bool? templateRestEnabled = null, bool? templateGraphqlEnabled = null, bool? templateCacheEnabled = null, @@ -39,7 +39,7 @@ public AutoConfigOptions( PatternsInclude = patternsInclude; PatternsExclude = patternsExclude; PatternsName = patternsName; - TemplateMcpDmlTools = templateMcpDmlTool; + TemplateMcpDmlTools = templateMcpDmlTools; TemplateRestEnabled = templateRestEnabled; TemplateGraphqlEnabled = templateGraphqlEnabled; TemplateCacheEnabled = templateCacheEnabled; From 6a34ae7490d14df71071a6537bd4158b0627ce43 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 8 Apr 2026 14:29:01 -0700 Subject: [PATCH 5/5] Fix log test --- src/Service.Tests/Configuration/ConfigurationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index a8925f3ad6..f894b841ed 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -5611,7 +5611,7 @@ public async Task TestAutoentitiesAreGeneratedIntoEntities(bool useEntities, int /// [TestCategory(TestCategory.MSSQL)] [DataTestMethod] - [DataRow("publishers", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", "Entity with name 'publishers' already exists. Cannot create new entity from autoentities definition 'PublisherAutoEntity'.", DisplayName = "Autoentities fail due to entity name")] + [DataRow("publishers", "uniqueSingularPublisher", "uniquePluralPublishers", "/unique/publisher", "Entity 'publishers' conflicts with autoentity pattern 'PublisherAutoEntity'. Use --patterns.exclude to skip it.", DisplayName = "Autoentities fail due to entity name")] [DataRow("UniquePublisher", "publishers", "uniquePluralPublishers", "/unique/publisher", "Entity publishers generates queries/mutation that already exist", DisplayName = "Autoentities fail due to graphql singular type")] [DataRow("UniquePublisher", "uniqueSingularPublisher", "publishers", "/unique/publisher", "Entity publishers generates queries/mutation that already exist", DisplayName = "Autoentities fail due to graphql plural type")] [DataRow("UniquePublisher", "uniqueSingularPublisher", "uniquePluralPublishers", "/publishers", "The rest path: publishers specified for entity: publishers is already used by another entity.", DisplayName = "Autoentities fail due to rest path")]