Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ public Task<CallToolResult> ExecuteAsync(

List<Dictionary<string, object?>> entityList = new();

// Track how many entities were filtered out because DML tools are disabled (dml-tools: false).
// This helps provide a more specific error message when all entities are filtered.
int filteredDmlDisabledCount = 0;

if (runtimeConfig.Entities != null)
{
foreach (KeyValuePair<string, Entity> entityEntry in runtimeConfig.Entities)
Expand All @@ -155,6 +159,16 @@ public Task<CallToolResult> ExecuteAsync(
string entityName = entityEntry.Key;
Entity entity = entityEntry.Value;

// Filter out entities when dml-tools is explicitly disabled (false).
// This applies to all entity types (tables, views, stored procedures).
// When dml-tools is false, the entity is not exposed via DML tools
// (read_records, create_record, etc.) and should not appear in describe_entities.
if (entity.Mcp?.DmlToolEnabled == false)
{
filteredDmlDisabledCount++;
continue;
}
Comment on lines +162 to +170
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The new filtering block excludes any entity with entity.Mcp?.DmlToolEnabled == false, regardless of entity type or CustomToolEnabled. This is broader than the PR description (stored-procedure, custom-tool-only de-duplication). If the intent is only to avoid duplication with custom tools, restrict the condition to stored procedures where CustomToolEnabled == true and DmlToolEnabled == false; otherwise, please update the PR description/tests to reflect the expanded behavior.

Copilot uses AI. Check for mistakes.

if (!ShouldIncludeEntity(entityName, entityFilter))
{
Comment on lines +166 to 173
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

filteredDmlDisabledCount is incremented before applying ShouldIncludeEntity(entityName, entityFilter). When callers pass the entities filter argument, this can count (and later log) entities that were never requested, making the filtered/returned counts misleading. Consider moving the entityFilter check before this block, or only increment/log for entities that pass the filter.

Suggested change
if (entity.Mcp?.DmlToolEnabled == false)
{
filteredDmlDisabledCount++;
continue;
}
if (!ShouldIncludeEntity(entityName, entityFilter))
{
if (!ShouldIncludeEntity(entityName, entityFilter))
{
continue;
}
if (entity.Mcp?.DmlToolEnabled == false)
{
filteredDmlDisabledCount++;

Copilot uses AI. Check for mistakes.
continue;
Expand All @@ -177,6 +191,7 @@ public Task<CallToolResult> ExecuteAsync(

if (entityList.Count == 0)
{
// No entities matched the filter criteria
if (entityFilter != null && entityFilter.Count > 0)
{
return Task.FromResult(McpResponseBuilder.BuildErrorResult(
Expand All @@ -185,6 +200,20 @@ public Task<CallToolResult> ExecuteAsync(
$"No entities found matching the filter: {string.Join(", ", entityFilter)}",
logger));
}
// Return a specific error when ALL configured entities have dml-tools: false.
// Only show this error when every entity was intentionally filtered by the dml-tools check above,
// not when some entities failed to build due to exceptions in BuildBasicEntityInfo() or BuildFullEntityInfo() functions.
else if (filteredDmlDisabledCount > 0 &&
runtimeConfig.Entities != null &&
filteredDmlDisabledCount == runtimeConfig.Entities.Entities.Count)
{
return Task.FromResult(McpResponseBuilder.BuildErrorResult(
toolName,
"AllEntitiesFilteredDmlDisabled",
$"All {filteredDmlDisabledCount} configured entities have DML tools disabled (dml-tools: false). Entities with dml-tools disabled do not appear in describe_entities. For stored procedures, check tools/list if custom-tool is enabled.",
logger));
}
// Truly no entities configured in the runtime config, or entities failed to build for other reasons
else
{
return Task.FromResult(McpResponseBuilder.BuildErrorResult(
Expand All @@ -207,6 +236,18 @@ public Task<CallToolResult> ExecuteAsync(
["count"] = finalEntityList.Count
};

// Log when entities were filtered due to DML tools disabled for visibility
if (filteredDmlDisabledCount > 0)
{
logger?.LogInformation(
"DescribeEntitiesTool: {FilteredCount} entity(ies) filtered with DML tools disabled (dml-tools: false). " +
"These entities are not exposed via DML tools and do not appear in describe_entities response. " +
"Returned {ReturnedCount} entities, filtered {FilteredCount}.",
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The log template repeats the {FilteredCount} placeholder name twice and requires passing the same argument twice. Consider removing the duplicate placeholder (or renaming one of them) to avoid duplicate structured-log keys and to simplify the call site.

Suggested change
"Returned {ReturnedCount} entities, filtered {FilteredCount}.",
"Returned {ReturnedCount} entities, filtered {FilteredCountTotal}.",

Copilot uses AI. Check for mistakes.
filteredDmlDisabledCount,
finalEntityList.Count,
filteredDmlDisabledCount);
}

logger?.LogInformation(
"DescribeEntitiesTool returned {EntityCount} entities. Response type: {ResponseType} (nameOnly={NameOnly}).",
finalEntityList.Count,
Expand Down
Loading
Loading