-
Notifications
You must be signed in to change notification settings - Fork 301
Entity level check if MCP Tool is enabled #3084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 6 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR wires entity-level MCP configuration into the MCP tools so that both global runtime and per-entity flags are honored when deciding if a tool is enabled. It also introduces tests to validate behavior for enabled/disabled states, missing MCP config, and custom tools under hot-reload scenarios.
Changes:
- Added
DmlToolEnabledchecks to all DML MCP tools (read_records,create_record,update_record,delete_record,execute_entity) so they return aToolDisablederror when disabled at the entity level. - Added a
CustomToolEnabledruntime check inDynamicCustomToolto ensure stored-proc tools are disabled when entity-level config turns them off, even after hot reload. - Extended MCP error helper to support custom tool-disabled messages and added a dedicated test suite validating entity-level MCP behavior across DML and custom tools.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Service.Tests/Mcp/EntityLevelDmlToolConfigurationTests.cs | New MSTest suite validating entity-level Mcp.DmlToolEnabled and Mcp.CustomToolEnabled handling across DML tools and dynamic custom tools, including disabled entities, enabled/default cases, and missing MCP config. |
| src/Azure.DataApiBuilder.Mcp/Utils/McpErrorHelpers.cs | Extends ToolDisabled helper with an optional custom message parameter to support more specific error messages while preserving existing call sites. |
| src/Azure.DataApiBuilder.Mcp/Core/DynamicCustomTool.cs | Adds a runtime check against entityConfig.Mcp.CustomToolEnabled before metadata/authorization, returning a ToolDisabled error when the custom tool is disabled in the current config. |
| src/Azure.DataApiBuilder.Mcp/BuiltInTools/UpdateRecordTool.cs | After parsing entity/keys/fields, adds an entity-level DmlToolEnabled check that returns ToolDisabled when the entity’s DML tools are disabled. |
| src/Azure.DataApiBuilder.Mcp/BuiltInTools/ReadRecordsTool.cs | After parsing the entity argument, adds an entity-level DmlToolEnabled check to gate the read_records tool per entity. |
| src/Azure.DataApiBuilder.Mcp/BuiltInTools/ExecuteEntityTool.cs | Adds an early entity-level DmlToolEnabled check and hardens entity lookup against config.Entities being null before resolving metadata and executing. |
| src/Azure.DataApiBuilder.Mcp/BuiltInTools/DeleteRecordTool.cs | Adds an entity-level DmlToolEnabled check after parsing entity/keys to prevent deletes when the entity’s DML tools are disabled. |
| src/Azure.DataApiBuilder.Mcp/BuiltInTools/CreateRecordTool.cs | Adds an entity-level DmlToolEnabled check after parsing entity/data so create operations respect per-entity MCP configuration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| JsonDocument arguments = JsonDocument.Parse("{\"entity\": \"Book\"}"); | ||
|
|
||
| // Act | ||
| CallToolResult result = await tool.ExecuteAsync(arguments, serviceProvider, CancellationToken.None); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`private static async Task RunAsync(
IMcpTool tool,
string json,
IServiceProvider sp)
{
CallToolResult result = await tool.ExecuteAsync(
JsonDocument.Parse(json),
sp,
CancellationToken.None);
return JsonDocument.Parse(((TextContentBlock)result.Content[0]).Text).RootElement;
}`
we can create a function like this and just reuse it.
anushakolan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests code can be refactored to reduce duplicate logic, apart from that rest LGTM. Will re-review once the test code is refactored.
| TextContentBlock firstContent = (TextContentBlock)result.Content[0]; | ||
| JsonElement content = JsonDocument.Parse(firstContent.Text).RootElement; | ||
|
|
||
| Assert.IsTrue(content.TryGetProperty("error", out JsonElement error)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These assertions can also be extracted into a function and reused.
| /// Verifies that CreateRecordTool respects entity-level DmlToolEnabled=false. | ||
| /// </summary> | ||
| [TestMethod] | ||
| public async Task CreateRecord_RespectsEntityLevelDmlToolDisabled() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following tests differ only by which tool is used:
ReadRecords_RespectsEntityLevelDmlToolDisabled CreateRecord_RespectsEntityLevelDmlToolDisabled UpdateRecord_RespectsEntityLevelDmlToolDisabled DeleteRecord_RespectsEntityLevelDmlToolDisabled ExecuteEntity_RespectsEntityLevelDmlToolDisabled
You can collapse these into an MSTest DataRow test:
| /// Verifies that entity-level check is skipped when entity has no MCP configuration. | ||
| /// When entity.Mcp is null, DmlToolEnabled defaults to true. | ||
| /// </summary> | ||
| [TestMethod] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two:
ReadRecords_WorksWhenEntityLevelDmlToolEnabled
ReadRecords_WorksWhenEntityHasNoMcpConfig
can also be merged
Why make this change?
The entity-level MCP configuration (
Entity.Mcp.DmlToolEnabledorEntity.Mcp.CustomToolEnabled) can override runtime-level settings.DmlToolEnableddefaults to true when unspecified, whileCustomToolEnableddefaults to false.However, this entity-level configuration was not being checked by the MCP tools when they execute. The tools only checked the runtime-level configuration (e.g.
RuntimeConfig.McpDmlTools.ReadRecords, etc.).This change addresses this gap for both built-in/DML tools and custom tools by adding entity-level validation before tool execution.
What is this change?
entity.Mcp.DmlToolEnabledand return aToolDisablederror when disabled at the entity level.entity.Mcp.CustomToolEnabledand return aToolDisablederrorHow was this tested?
Sample Request(s)
Not applicable since it's a config level change and have been tested based on config settings.