Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d1e17ee
Phase 3 Stage 1: Add embed flag to stored procedure parameters
prshri-msft Apr 28, 2026
1fd348f
Phase 3 Stage 2: Wire EmbeddingService into stored procedure execution
prshri-msft Apr 29, 2026
119c786
Phase 3 Stage 3.5: Address review findings
prshri-msft Apr 29, 2026
34acafe
Phase 3 Stage 3.6: Reject non-VECTOR types for embed:true at startup
prshri-msft Apr 29, 2026
c8e7bf5
Phase 3 Stage 3.7: Round-2 reviewer polish fixes
prshri-msft Apr 30, 2026
3d83fa6
Phase 3 Stage 3.8: Revert two over-corrections from Stage 3.7
prshri-msft Apr 30, 2026
916d271
Phase 3 Stage 3.9: Multi-embed batching + clarify embed/default ratio…
prshri-msft May 1, 2026
9c4a75e
Phase 3 Stage 4.1: Refactors enabling Phase 3 unit-testability
prshri-msft May 4, 2026
9940f16
Phase 3 Stage 4.2: Add ParameterEmbeddingHelper unit tests (28 tests)
prshri-msft May 4, 2026
12415bd
Phase 3 Stage 4.3: Add validator + metadata override unit tests (16 t…
prshri-msft May 4, 2026
815c8fd
Phase 3 Stage 3.10: Address PR review feedback from ajtiwari07
prshri-msft May 15, 2026
4f13708
Phase 3 Stage 3.11: Rename `embed` to `auto-embed` and switch schema …
prshri-msft May 19, 2026
5e52c68
Phase 3 Stage 3.12: Bundle 3 quick wins (CB1, B2, N3, N2)
prshri-msft May 19, 2026
d48d122
Phase 3 Stage 3.13: Non-optional IEmbeddingService + NullEmbeddingSer…
prshri-msft May 19, 2026
323e146
Phase 3 Stage 3.14: Add telemetry to auto-embed substitution
prshri-msft May 20, 2026
cf00fa3
Phase 3 Stage 3.15: Bundle 2 data-flow redesign per spec #3331
prshri-msft May 20, 2026
68a834f
Phase 3 Stage 3.16: Add --parameters.auto-embed to dab add and dab up…
prshri-msft May 20, 2026
e2b2952
Phase 3 Stage 3.17: Surface auto-embed in OpenAPI, GraphQL, and MCP m…
prshri-msft May 21, 2026
9884ef3
Phase 3 Stage 3.18: Map embedding errors to spec-compliant HTTP statuses
prshri-msft May 21, 2026
3ffa6db
Phase 3 Stage 3.19: Fix IDE1006 + IDE0005 violations (unblock CI form…
prshri-msft May 21, 2026
90e6b49
Phase 3 Stage 3.20: Register auto-embed meter in Startup
prshri-msft May 21, 2026
acc0ec8
Phase 3 Stage 3.21: Fix dab update --parameters.auto-embed stickiness
prshri-msft May 22, 2026
cc2ba4f
Phase 3 Stage 3.22: Surface auto-embed in REST GET + MCP custom tool …
prshri-msft May 22, 2026
daae1fb
Phase 3 Stage 3.23: Refresh stale auto-embed docs/comments
prshri-msft May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion schemas/dab.draft.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,8 @@
"name": { "type": "string", "description": "Parameter name" },
"required": { "$ref": "#/$defs/boolean-or-string", "description": "Is parameter required" },
"default": { "type": ["string", "number", "boolean", "null"], "description": "Default value" },
"description": { "type": "string", "description": "Parameter description. Since descriptions for multiple parameters are provided as a comma-separated string, individual parameter descriptions must not contain a comma (',')." }
"description": { "type": "string", "description": "Parameter description. Since descriptions for multiple parameters are provided as a comma-separated string, individual parameter descriptions must not contain a comma (',')." },
"auto-embed": { "$ref": "#/$defs/boolean-or-string", "description": "When true, the parameter value (text) is automatically converted to an embedding vector via the configured embedding service before being passed to the stored procedure. The target stored-procedure parameter must be a string-compatible type (e.g., NVARCHAR/VARCHAR); the stored procedure is responsible for any conversion to a native vector type if needed (e.g., CAST(@param AS VECTOR(N)) on Azure SQL). Requires runtime.embeddings to be configured and enabled.", "default": false }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ private static bool ShouldIncludeEntity(string entityName, HashSet<string>? enti

if (entity.Source.Type == EntitySourceType.StoredProcedure)
{
info["parameters"] = BuildParameterMetadataInfo(databaseObject);
info["parameters"] = BuildParameterMetadataInfo(databaseObject, entity.Source.Parameters);
}

info["permissions"] = BuildPermissionsInfo(entity, currentUserRole);
Expand Down Expand Up @@ -511,7 +511,7 @@ private static List<object> BuildFieldMetadataInfo(List<FieldMetadata>? fields)
/// <returns>A list whose elements are dictionaries (one per parameter), each with the keys
/// <c>name</c>, <c>required</c>, <c>default</c>, and <c>description</c>.</returns>
/// <exception cref="InvalidOperationException">Thrown when <paramref name="databaseObject"/> is not a <see cref="DatabaseStoredProcedure"/> with a populated <see cref="StoredProcedureDefinition"/>.</exception>
private static List<object> BuildParameterMetadataInfo(DatabaseObject? databaseObject)
private static List<object> BuildParameterMetadataInfo(DatabaseObject? databaseObject, List<ParameterMetadata>? configParams)
{
IReadOnlyDictionary<string, ParameterDefinition>? dbParameters =
(databaseObject as DatabaseStoredProcedure)?.StoredProcedureDefinition?.Parameters
Expand All @@ -522,20 +522,23 @@ private static List<object> BuildParameterMetadataInfo(DatabaseObject? databaseO
List<object> result = new(dbParameters.Count);
foreach ((string parameterName, ParameterDefinition definition) in dbParameters)
{
result.Add(BuildParameterEntry(parameterName, definition));
bool autoEmbed = configParams?.FirstOrDefault(p => p.Name == parameterName)?.AutoEmbed ?? false;
result.Add(BuildParameterEntry(parameterName, definition, autoEmbed));
}

return result;
}

private static Dictionary<string, object?> BuildParameterEntry(
string name,
ParameterDefinition definition) => new()
ParameterDefinition definition,
bool autoEmbed) => new()
{
["name"] = name,
["required"] = definition.Required ?? true,
["default"] = definition.Default,
["description"] = definition.Description ?? string.Empty
["description"] = definition.Description ?? string.Empty,
["autoEmbed"] = autoEmbed
};

/// <summary>
Expand Down
30 changes: 27 additions & 3 deletions src/Azure.DataApiBuilder.Mcp/Core/DynamicCustomTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,15 @@ private JsonElement BuildInputSchema()
Dictionary<string, object> properties = new();
foreach ((string paramName, ParameterDefinition paramDef) in spDefinition.Parameters)
{
// Surface the auto-embed indicator in the description when the config marks
// this parameter as auto-embed:true. DB metadata doesn't carry this flag, so
// we cross-reference the config-side ParameterMetadata.
bool isAutoEmbed = _entity.Source.Parameters?.FirstOrDefault(p => p.Name == paramName)?.AutoEmbed ?? false;

Dictionary<string, object> paramSchema = new()
{
["type"] = MapSystemTypeToJsonSchemaType(paramDef.SystemType),
["description"] = BuildParameterDescription(paramName, paramDef)
["description"] = BuildParameterDescription(paramName, paramDef, isAutoEmbed)
};

properties[paramName] = paramSchema;
Expand Down Expand Up @@ -399,10 +404,21 @@ private JsonElement BuildInputSchemaFromConfig()

foreach (ParameterMetadata param in _entity.Source.Parameters)
{
// Note: Parameter type information is not available in ParameterMetadata,
// so we allow multiple JSON types to match the behavior of GetParameterValue
// that handles string, number, boolean, and null values. Runtime
// validation (ParameterEmbeddingHelper for auto-embed, SQL Server for the
// rest) rejects values that the sproc parameter can't accept.
string description = param.Description ?? $"Parameter {param.Name}";
if (param.AutoEmbed)
{
description += " (auto-embed: DAB converts this value to an embedding before execution)";
}

properties[param.Name] = new Dictionary<string, object>
{
["type"] = new[] { "string", "number", "boolean", "null" },
["description"] = param.Description ?? $"Parameter {param.Name}"
["description"] = description
};
}
}
Expand Down Expand Up @@ -455,15 +471,23 @@ private static object MapSystemTypeToJsonSchemaType(Type? systemType)
/// <summary>
/// Builds a description string for a parameter using DB metadata.
/// Uses ParameterDefinition.Description when available, falling back to generic text.
/// When <paramref name="isAutoEmbed"/> is true, appends the auto-embed indicator
/// so MCP clients see that DAB will convert the value to an embedding before
/// executing the stored procedure.
/// </summary>
private static string BuildParameterDescription(string paramName, ParameterDefinition paramDef)
private static string BuildParameterDescription(string paramName, ParameterDefinition paramDef, bool isAutoEmbed = false)
{
string description = paramDef.Description ?? $"Parameter {paramName}";
if (paramDef.HasConfigDefault)
{
description += $" (default: {paramDef.ConfigDefaultValue})";
}

if (isAutoEmbed)
{
description += " (auto-embed: DAB converts this value to an embedding before execution)";
}

return description;
}

Expand Down
86 changes: 86 additions & 0 deletions src/Cli.Tests/AddEntityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public Task AddNewEntityWhenEntitiesEmpty()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -91,6 +92,7 @@ public Task AddNewEntityWhenEntitiesNotEmpty()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -133,6 +135,7 @@ public void AddDuplicateEntity()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -179,6 +182,7 @@ public Task AddEntityWithAnExistingNameButWithDifferentCase()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -220,6 +224,7 @@ public Task AddEntityWithCachingEnabled()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -267,6 +272,7 @@ public Task AddEntityWithPolicyAndFieldProperties(
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -310,6 +316,7 @@ public Task AddNewEntityWhenEntitiesWithSourceAsStoredProcedure()
parametersDescriptionCollection: ["This is a test parameter description."],
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -352,6 +359,7 @@ public Task TestAddStoredProcedureWithRestMethodsAndGraphQLOperations()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -390,6 +398,7 @@ public void AddEntityWithDescriptionAndVerifyInConfig()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -455,6 +464,7 @@ public void TestAddNewEntityWithSourceObjectHavingValidFields(
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -525,6 +535,7 @@ public Task TestAddNewSpWithDifferentRestAndGraphQLOptions(
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -571,6 +582,7 @@ public void TestAddStoredProcedureWithConflictingRestGraphQLOptions(
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -620,6 +632,7 @@ public void TestAddEntityPermissionWithInvalidOperation(IEnumerable<string> perm
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -695,6 +708,7 @@ public Task AddTableEntityWithMcpDmlTools(string mcpDmlTools, string source, str
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -739,6 +753,7 @@ public Task AddStoredProcedureWithMcpCustomToolEnabled()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -780,6 +795,7 @@ public Task AddStoredProcedureWithBothMcpProperties()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -821,6 +837,7 @@ public Task AddStoredProcedureWithBothMcpPropertiesEnabled()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -862,6 +879,7 @@ public void AddTableEntityWithInvalidMcpCustomTool()
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand Down Expand Up @@ -910,6 +928,7 @@ public void AddEntityWithInvalidMcpOptions(string? mcpDmlTools, string? mcpCusto
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: null,
parametersAutoEmbedCollection: null,
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
Expand All @@ -925,5 +944,72 @@ public void AddEntityWithInvalidMcpOptions(string? mcpDmlTools, string? mcpCusto
}

#endregion MCP Entity Configuration Tests

#region Auto-Embed CLI Tests

/// <summary>
/// Verify that --parameters.auto-embed correctly sets AutoEmbed on the resulting
/// ParameterMetadata, both with and without default values.
/// Per spec #3331, defaults are allowed on auto-embed parameters.
/// </summary>
[DataTestMethod]
[DataRow("query_text,top_k", "true,false", null, 0, true, null,
DisplayName = "auto-embed true/false on two params, no defaults")]
[DataRow("query_text", "true", "electronics", 0, true, "electronics",
DisplayName = "auto-embed with default value")]
public void AddEntityWithAutoEmbedParameter(
string paramNames,
string autoEmbedFlags,
string? defaultValues,
int assertIndex,
bool expectedAutoEmbed,
string? expectedDefault)
{
AddOptions options = new(
source: "dbo.SearchProducts",
permissions: new string[] { "anonymous", "execute" },
entity: "SearchProducts",
description: null,
sourceType: "stored-procedure",
sourceParameters: null,
sourceKeyFields: null,
restRoute: null,
graphQLType: null,
fieldsToInclude: Array.Empty<string>(),
fieldsToExclude: Array.Empty<string>(),
policyRequest: null,
policyDatabase: null,
cacheEnabled: null,
cacheTtlSeconds: null,
cacheLevel: null,
healthEnabled: null,
config: TEST_RUNTIME_CONFIG_FILE,
restMethodsForStoredProcedure: null,
graphQLOperationForStoredProcedure: null,
parametersNameCollection: paramNames.Split(','),
parametersDescriptionCollection: null,
parametersRequiredCollection: null,
parametersDefaultCollection: defaultValues?.Split(','),
parametersAutoEmbedCollection: autoEmbedFlags.Split(','),
fieldsNameCollection: [],
fieldsAliasCollection: [],
fieldsDescriptionCollection: [],
fieldsPrimaryKeyCollection: []
);

Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(INITIAL_CONFIG, out RuntimeConfig? runtimeConfig));
Assert.IsTrue(TryAddNewEntity(options, runtimeConfig, out RuntimeConfig updatedConfig));

Entity entity = updatedConfig.Entities["SearchProducts"];
Assert.IsNotNull(entity.Source.Parameters);

ParameterMetadata param = entity.Source.Parameters[assertIndex];
Assert.AreEqual(expectedAutoEmbed, param.AutoEmbed,
$"Parameter '{param.Name}' AutoEmbed should be {expectedAutoEmbed}");
Assert.AreEqual(expectedDefault, param.Default,
$"Parameter '{param.Name}' Default should be '{expectedDefault}'");
}

#endregion Auto-Embed CLI Tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@
{
Name: param1,
Required: false,
Default: 123
Default: 123,
AutoEmbed: false
},
{
Name: param2,
Required: false,
Default: hello
Default: hello,
AutoEmbed: false
},
{
Name: param3,
Required: false,
Default: True
Default: True,
AutoEmbed: false
}
]
},
Expand Down
Loading