Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 9 additions & 11 deletions src/Azure.DataApiBuilder.Mcp/BuiltInTools/CreateRecordTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,23 @@ public async Task<CallToolResult> ExecuteAsync(
}

JsonElement insertPayloadRoot = dataElement.Clone();

// Validate it's a table or view - stored procedures use execute_entity
if (dbObject.SourceType != EntitySourceType.Table && dbObject.SourceType != EntitySourceType.View)
{
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidEntity", $"Entity '{entityName}' is not a table or view. For stored procedures, use the execute_entity tool instead.", logger);
}
Comment on lines +121 to +125
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateRecordTool performs source-type validation after role-context + permission checks. For non-table/view entities (e.g., stored procedures) this can return PermissionDenied instead of the intended "InvalidEntity" guidance, and it’s inconsistent with Read/Update/Delete which validate source type immediately after metadata resolution. Move the table/view validation to right after TryResolveMetadata so the tool reliably returns the correct error and short-circuits earlier.

Copilot uses AI. Check for mistakes.

InsertRequestContext insertRequestContext = new(
entityName,
dbObject,
insertPayloadRoot,
EntityActionOperation.Insert);

RequestValidator requestValidator = serviceProvider.GetRequiredService<RequestValidator>();

// Only validate tables
// Only validate tables. For views, skip validation and let the database handle any errors.
if (dbObject.SourceType is EntitySourceType.Table)
{
RequestValidator requestValidator = serviceProvider.GetRequiredService<RequestValidator>();
try
{
requestValidator.ValidateInsertRequestContext(insertRequestContext);
Expand All @@ -137,14 +143,6 @@ public async Task<CallToolResult> ExecuteAsync(
return McpResponseBuilder.BuildErrorResult(toolName, "ValidationFailed", $"Request validation failed: {ex.Message}", logger);
}
}
else
{
return McpResponseBuilder.BuildErrorResult(
toolName,
"InvalidCreateTarget",
"The create_record tool is only available for tables.",
logger);
}

IMutationEngineFactory mutationEngineFactory = serviceProvider.GetRequiredService<IMutationEngineFactory>();
DatabaseType databaseType = sqlMetadataProvider.GetDatabaseType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ public async Task<CallToolResult> ExecuteAsync(
return McpResponseBuilder.BuildErrorResult(toolName, "EntityNotFound", metadataError, logger);
}

// Validate it's a table or view
if (dbObject.SourceType != EntitySourceType.Table && dbObject.SourceType != EntitySourceType.View)
{
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidEntity", $"Entity '{entityName}' is not a table or view. For stored procedures, use the execute_entity tool instead.", logger);
}

// Authorization check in the existing entity
IAuthorizationResolver authResolver = serviceProvider.GetRequiredService<IAuthorizationResolver>();
IAuthorizationService authorizationService = serviceProvider.GetRequiredService<IAuthorizationService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ public async Task<CallToolResult> ExecuteAsync(
return McpResponseBuilder.BuildErrorResult(toolName, "EntityNotFound", metadataError, logger);
}

// Validate it's a table or view
if (dbObject.SourceType != EntitySourceType.Table && dbObject.SourceType != EntitySourceType.View)
{
return McpResponseBuilder.BuildErrorResult(toolName, "InvalidEntity", $"Entity '{entityName}' is not a table or view. For stored procedures, use the execute_entity tool instead.", logger);
}

// 5) Authorization after we have a known entity
IHttpContextAccessor httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
HttpContext? httpContext = httpContextAccessor.HttpContext;
Expand Down
Loading
Loading