Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0e6b9db
Phase 3 Stage 1: Add embed flag to stored procedure parameters
prshri-msft Apr 28, 2026
ccf5f42
Phase 3 Stage 2: Wire EmbeddingService into stored procedure execution
prshri-msft Apr 29, 2026
9e693b8
Phase 3 Stage 3.5: Address review findings
prshri-msft Apr 29, 2026
2a6fccd
Phase 3 Stage 3.6: Reject non-VECTOR types for embed:true at startup
prshri-msft Apr 29, 2026
c2d9f48
Phase 3 Stage 3.7: Round-2 reviewer polish fixes
prshri-msft Apr 30, 2026
0213bd6
Phase 3 Stage 3.8: Revert two over-corrections from Stage 3.7
prshri-msft Apr 30, 2026
3709a66
Phase 3 Stage 3.9: Multi-embed batching + clarify embed/default ratio…
prshri-msft May 1, 2026
6950e8b
Phase 3 Stage 4.1: Refactors enabling Phase 3 unit-testability
prshri-msft May 4, 2026
fb43d63
Phase 3 Stage 4.2: Add ParameterEmbeddingHelper unit tests (28 tests)
prshri-msft May 4, 2026
0e8ab5f
Phase 3 Stage 4.3: Add validator + metadata override unit tests (16 t…
prshri-msft May 4, 2026
7226ffb
Phase 3 Stage 3.10: Address PR review feedback from ajtiwari07
prshri-msft May 15, 2026
0d10998
Phase 3 Stage 3.11: Rename `embed` to `auto-embed` and switch schema …
prshri-msft May 19, 2026
87b17d1
Phase 3 Stage 3.12: Bundle 3 quick wins (CB1, B2, N3, N2)
prshri-msft May 19, 2026
d53c773
Phase 3 Stage 3.13: Non-optional IEmbeddingService + NullEmbeddingSer…
prshri-msft May 19, 2026
1d45220
Phase 3 Stage 3.14: Add telemetry to auto-embed substitution
prshri-msft May 20, 2026
32bb966
Phase 3 Stage 3.15: Bundle 2 data-flow redesign per spec #3331
prshri-msft May 20, 2026
1bbec61
Phase 3 Stage 3.16: Add --parameters.auto-embed to dab add and dab up…
prshri-msft May 20, 2026
46226ff
Phase 3 Stage 3.17: Surface auto-embed in OpenAPI, GraphQL, and MCP m…
prshri-msft May 21, 2026
508470b
Phase 3 Stage 3.18: Map embedding errors to spec-compliant HTTP statuses
prshri-msft May 21, 2026
97fa4ca
Phase 3 Stage 3.19: Fix IDE1006 + IDE0005 violations (unblock CI form…
prshri-msft May 21, 2026
e0b0976
Phase 3 Stage 3.20: Register auto-embed meter in Startup
prshri-msft May 21, 2026
9354f08
Phase 3 Stage 3.21: Fix dab update --parameters.auto-embed stickiness
prshri-msft May 22, 2026
9e940fe
Phase 3 Stage 3.22: Surface auto-embed in REST GET + MCP custom tool …
prshri-msft May 22, 2026
89fd8b3
Phase 3 Stage 3.23: Refresh stale auto-embed docs/comments
prshri-msft May 22, 2026
c6e2e7e
Phase 3 Stage 3.24: Code hygiene — strip Phase 3/spec refs + restore …
prshri-msft May 26, 2026
ff6bb4e
Phase 3 Stage 3.25: Use BadRequest for auto-embed config validation e…
prshri-msft May 26, 2026
f59c377
Phase 3 Stage 3.26: Dedupe auto-embed description literal
prshri-msft May 26, 2026
35b91dc
Phase 3 Stage 3.27: Remove BOM from one snapshot to match original en…
prshri-msft May 26, 2026
d8acb51
Phase 3 Stage 3.28: Flatten embedding service registration in Startup
prshri-msft May 27, 2026
c80e332
Phase 3 Stage 3.29: Rename Suffix const to satisfy IDE1006 naming rule
prshri-msft May 27, 2026
391b92a
Phase 3 Stage 3.30: Reject auto-embed on non-MSSQL providers at startup
prshri-msft May 27, 2026
11928df
Phase 3 Stage 3.31: Fix whitespace in DataRow attributes to satisfy d…
prshri-msft May 27, 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 @@ -1280,7 +1280,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
24 changes: 20 additions & 4 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,18 @@ 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 baseDescription = param.Description ?? $"Parameter {param.Name}";
string description = AutoEmbedDescription.Append(baseDescription, param.AutoEmbed)!;

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,16 +468,19 @@ 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})";
}

return description;
return AutoEmbedDescription.Append(description, isAutoEmbed)!;
}

/// <summary>
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
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