From 2a6e5504cdfb36d542516ad4313099abf9e05b7b Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 9 Oct 2026 10:52:41 -0400 Subject: [PATCH] fix(mcp): report zero packing demand accurately --- OpenNest.FrontEnd.Tests/McpMixReportingTests.cs | 12 ++++++++++++ OpenNest.Mcp/Tools/NestingTools.cs | 4 +++- docs/automatic-nesting.md | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs b/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs index 9a0c51d7..2f371e65 100644 --- a/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs +++ b/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs @@ -77,6 +77,18 @@ public class McpMixReportingTests Assert.Empty(session.GetPlate(0).Parts); } + [Fact] + public void ZeroDemandHasZeroRemainingAndNegativeDemandIsRejected() + { + var session = Session(); + var tools = new NestingTools(session); + var text = tools.PackPlate(0, "A", "0"); + Assert.Contains("A: requested=0, placed=0, remaining=0", text); + Assert.Contains("zero progress", text); + Assert.Contains("negative", tools.PackPlate(0, "A", "-1"), StringComparison.OrdinalIgnoreCase); + Assert.Empty(session.GetPlate(0).Parts); + } + private sealed class Stub : INestingEngine { public NestJobResult Solve(NestJob job, IProgress? progress = null, diff --git a/OpenNest.Mcp/Tools/NestingTools.cs b/OpenNest.Mcp/Tools/NestingTools.cs index 558f299f..018f6a71 100644 --- a/OpenNest.Mcp/Tools/NestingTools.cs +++ b/OpenNest.Mcp/Tools/NestingTools.cs @@ -346,7 +346,7 @@ namespace OpenNest.Mcp.Tools { var item = items[i]; placed[i] = counts.GetValueOrDefault(item.Drawing); - var remaining = item.Quantity > 0 ? (item.Quantity - placed[i]).ToString() : "unlimited"; + var remaining = item.Quantity - placed[i]; sb.AppendLine($" [{i}] {item.Drawing.Name}: requested={item.Quantity}, placed={placed[i]}, remaining={remaining}"); } @@ -405,6 +405,8 @@ namespace OpenNest.Mcp.Tools { if (!int.TryParse(qtyStrings[i], out qtys[i])) return (null, $"Error: '{qtyStrings[i]}' is not a valid quantity"); + if (qtys[i] < 0) + return (null, $"Error: negative quantity '{qtys[i]}' is not supported"); } if (names.Length != qtys.Length) diff --git a/docs/automatic-nesting.md b/docs/automatic-nesting.md index 9b975d14..e5eb0aaa 100644 --- a/docs/automatic-nesting.md +++ b/docs/automatic-nesting.md @@ -48,7 +48,7 @@ Invalid output is printed and rejected with exit code 2 without saving or postin MCP `autonest_plate` requires an empty target. The stdio server serializes all tool calls sharing its mutable session, so another request cannot change drawings or occupy a target during a solve. `allow_invalid` defaults to false. It reports violations and makes no changes on rejection, including with an override when the output is unrepresentable or contains multiple sheets. `engine` defaults to Default, and its description lists the built-in engines with what each suits. Fill tools (`fill_plate`, `fill_area`, `fill_remnants`, `pack_plate`) remain separate and default to the Fill strategy. Console and MCP load jobs plug-ins from `Engines/` beside their executable. -MCP `pack_plate` and `autonest_plate` report each requested drawing's newly placed count and remaining ceiling, indexed by request; a skewed positive-demand mix gets an explicit `ratio not met` warning. This is reporting, not proportional packing: both tools still choose the same density-first layout. Duplicate request tokens and ambiguous drawing names are refused before placement. A zero-quantity pack request remains unlimited and has no meaningful ratio. A partially placed balanced mix can still leave demand unfilled. Strict per-sheet ratio enforcement is not available yet. +MCP `pack_plate` and `autonest_plate` report each requested drawing's newly placed count and remaining ceiling, indexed by request; a skewed positive-demand mix gets an explicit `ratio not met` warning. This is reporting, not proportional packing: both tools still choose the same density-first layout. Duplicate request tokens and ambiguous drawing names are refused before placement. A zero-quantity pack request places zero parts for that drawing; negative quantities are rejected, and neither has a meaningful proportional mix. A partially placed balanced mix can still leave demand unfilled. Strict per-sheet ratio enforcement is not available yet. MCP `delete_plate(plateIndex)` removes only a sheet with no parts and no cutoff definitions, from either loaded or newly created session plates. Occupied, cutoff-bearing and invalid indices are refused without changing the session. After deletion every later index shifts down by one; inspect `get_plate_info` before further edits. This is session cleanup, not a data purge or a save-time option. `save_nest` lists all session plates in its response, but the existing `.nest` writer omits empty cutoff-free plates from the serialized file; reloading may therefore have fewer plates than the response says. There is no opt-in switch to preserve empty plates in the file.