From 044aeb5165c083cc9f92fb473cc012458a85e7a8 Mon Sep 17 00:00:00 2001 From: aaronburtle <93220300+aaronburtle@users.noreply.github.com> Date: Thu, 3 Sep 2026 00:31:54 +0000 Subject: [PATCH] Fix whitespace handling in MCP read_records select lists (#3786) ## Why make this change? Closes https://github.com/Azure/data-api-builder/issues/3771 ## What is this change? - Trim whitespace from each field after splitting the comma-separated `select` value. - Add a regression integration test covering a space after the comma. ## How was this tested? - [x] Integration Tests - Added `ReadRecords_WithWhitespaceAfterSelectComma_ReturnsSelectedFields`. - [x] Unit Tests - All 309 non-database MCP tests passed. - The service test project builds successfully. ## Sample Request(s) Example MCP `read_records` arguments: { "entity": "Book", "select": "id, title" } The request now succeeds and returns the selected `id` and `title` fields. CLI, REST, and GraphQL samples are not applicable because this change affects the MCP `read_records` tool only. (cherry picked from commit dfd3be4c8ffac6ba227d52793f58a04f1878e922) --- .../BuiltInTools/ReadRecordsTool.cs | 7 +++- .../ReadRecordsToolMsSqlIntegrationTests.cs | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ReadRecordsTool.cs b/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ReadRecordsTool.cs index 8da3c4856a..bd33dd8433 100644 --- a/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ReadRecordsTool.cs +++ b/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ReadRecordsTool.cs @@ -201,7 +201,12 @@ public async Task ExecuteAsync( if (!string.IsNullOrWhiteSpace(select)) { // Update the context to specify which fields will be returned from the entity. - IEnumerable fieldsReturnedForFind = select.Split(",").ToList(); + List fieldsReturnedForFind = select.Split(',').Select(field => field.Trim()).ToList(); + if (fieldsReturnedForFind.Any(string.IsNullOrEmpty)) + { + return McpResponseBuilder.BuildErrorResult(toolName, "InvalidArguments", "The 'select' argument cannot contain empty field names.", logger); + } + context.UpdateReturnFields(fieldsReturnedForFind); } diff --git a/src/Service.Tests/Mcp/ReadRecordsToolMsSqlIntegrationTests.cs b/src/Service.Tests/Mcp/ReadRecordsToolMsSqlIntegrationTests.cs index 26e07b359c..2c009e772e 100644 --- a/src/Service.Tests/Mcp/ReadRecordsToolMsSqlIntegrationTests.cs +++ b/src/Service.Tests/Mcp/ReadRecordsToolMsSqlIntegrationTests.cs @@ -61,6 +61,39 @@ public async Task ReadRecords_WithSelect_ReturnsSelectedFields() Assert.IsTrue(firstRecord.TryGetProperty("title", out _), "Expected 'title' field in result."); } + /// + /// Reads records with whitespace after a comma in the select clause. + /// + [TestMethod] + public async Task ReadRecords_WithWhitespaceAfterSelectComma_ReturnsSelectedFields() + { + CallToolResult result = await ExecuteReadAsync("Book", select: "id, title"); + + AssertSuccess(result, "ReadRecords with whitespace after a select comma should succeed."); + + JsonElement root = ParseResultRoot(result); + JsonElement records = GetRecordsArray(root); + JsonElement firstRecord = records[0]; + Assert.IsTrue(firstRecord.TryGetProperty("id", out _), "Expected 'id' field in result."); + Assert.IsTrue(firstRecord.TryGetProperty("title", out _), "Expected 'title' field in result."); + } + + /// + /// Rejects empty field names in the select clause with a clear error. + /// + [DataTestMethod] + [DataRow("id,title,")] + [DataRow("id,,title")] + public async Task ReadRecords_WithEmptySelectField_ReturnsInvalidArguments(string select) + { + CallToolResult result = await ExecuteReadAsync("Book", select: select); + + AssertError(result); + JsonElement error = ParseResultRoot(result).GetProperty("error"); + Assert.AreEqual("InvalidArguments", error.GetProperty("type").GetString()); + Assert.AreEqual("The 'select' argument cannot contain empty field names.", error.GetProperty("message").GetString()); + } + /// /// Reads records with an OData filter expression and verifies filtered results are returned. ///