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