mcp-3d-printer-server 1.2.19 → 1.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.20 — 2026-10-30
4
+
5
+ ### Fixed
6
+
7
+ - Keep already-horizontal faces eligible when `lay_flat` chooses the largest flat face. Thin plates now rest on their broad face at Z=0 instead of standing on an edge, including after repeated calls. When up- and down-facing areas tie exactly, as for a plate with a raised block or lettering, keep the face that already points down so a seated part is not flipped. Validate with generated STL geometry in horizontal, upside-down, edge-on, and tilted orientations and with a raised block in either face order; no printer or live Blender scene is used.
8
+
9
+ ### Tests
10
+
11
+ - Verify malformed HTTP JSON returns a structured 400 response while the active MCP session remains usable. Exercise loopback HTTP only; no real printer or live Blender scene is used.
12
+
3
13
  ## 1.2.19 — 2026-10-30
4
14
 
5
15
  ### Fixed
@@ -1194,22 +1194,23 @@ export class STLManipulator extends EventEmitter {
1194
1194
  throw new Error("Operation cancelled");
1195
1195
  if (progressCallback)
1196
1196
  progressCallback(75, "Finding largest flat surface...");
1197
- // Find the group with the largest total area (excluding near-vertical faces)
1197
+ // Include already-horizontal faces: excluding Z-aligned normals can
1198
+ // stand a flat model on its edge, including on a repeated layFlat call.
1198
1199
  let largestArea = 0;
1199
1200
  let targetNormal = null;
1200
- const upVector = new THREE.Vector3(0, 1, 0); // Assuming Y is up for most models initially?
1201
1201
  const downVector = new THREE.Vector3(0, 0, -1); // Target normal (pointing down)
1202
+ // Faces are grouped by direction, so a part whose flat faces are all
1203
+ // horizontal has equal up- and down-facing areas. On a tie, keep the
1204
+ // face that already points down so a seated part is not flipped.
1202
1205
  for (const group of normalGroups.values()) {
1203
- // Check if the normal is reasonably horizontal (not too close to vertical Z)
1204
- // Angle threshold could be adjusted (e.g., > 5 degrees from Z axis)
1205
- if (Math.abs(group.representativeNormal.z) < 0.99 && group.totalArea > largestArea) {
1206
+ const tied = targetNormal !== null && Math.abs(group.totalArea - largestArea) <= largestArea * 1e-6;
1207
+ if (tied ? group.representativeNormal.z < targetNormal.z : group.totalArea > largestArea) {
1206
1208
  largestArea = group.totalArea;
1207
1209
  targetNormal = group.representativeNormal;
1208
1210
  }
1209
1211
  }
1210
1212
  if (!targetNormal) {
1211
- // If no suitable horizontal face found, maybe pick the lowest Z face?
1212
- // For now, throw an error or return unchanged.
1213
+ // Only a mesh without any face area reaches this.
1213
1214
  console.warn("Could not find a suitable large flat face to lay down. No changes made.");
1214
1215
  this.emit('operationComplete', { operationId, type: 'layFlat', success: true, output: stlFilePath, message: "No suitable flat face found." });
1215
1216
  return stlFilePath;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-3d-printer-server",
3
3
  "mcpName": "io.github.DMontgomery40/mcp-3d-printer-server",
4
- "version": "1.2.19",
4
+ "version": "1.2.20",
5
5
  "description": "MCP server for connecting Claude with 3D printer management systems",
6
6
  "main": "dist/index.js",
7
7
  "repository": "https://github.com/DMontgomery40/mcp-3D-printer-server",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/DMontgomery40/mcp-3D-printer-server",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.2.19",
10
+ "version": "1.2.20",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "mcp-3d-printer-server",
15
- "version": "1.2.19",
15
+ "version": "1.2.20",
16
16
  "runtimeHint": "npx",
17
17
  "transport": {
18
18
  "type": "stdio"
@@ -1503,24 +1503,25 @@ export class STLManipulator extends EventEmitter {
1503
1503
  if (!this.activeOperations.get(operationId)) throw new Error("Operation cancelled");
1504
1504
  if (progressCallback) progressCallback(75, "Finding largest flat surface...");
1505
1505
 
1506
- // Find the group with the largest total area (excluding near-vertical faces)
1506
+ // Include already-horizontal faces: excluding Z-aligned normals can
1507
+ // stand a flat model on its edge, including on a repeated layFlat call.
1507
1508
  let largestArea = 0;
1508
1509
  let targetNormal: THREE.Vector3 | null = null;
1509
- const upVector = new THREE.Vector3(0, 1, 0); // Assuming Y is up for most models initially?
1510
1510
  const downVector = new THREE.Vector3(0, 0, -1); // Target normal (pointing down)
1511
1511
 
1512
+ // Faces are grouped by direction, so a part whose flat faces are all
1513
+ // horizontal has equal up- and down-facing areas. On a tie, keep the
1514
+ // face that already points down so a seated part is not flipped.
1512
1515
  for (const group of normalGroups.values()) {
1513
- // Check if the normal is reasonably horizontal (not too close to vertical Z)
1514
- // Angle threshold could be adjusted (e.g., > 5 degrees from Z axis)
1515
- if (Math.abs(group.representativeNormal.z) < 0.99 && group.totalArea > largestArea) {
1516
+ const tied = targetNormal !== null && Math.abs(group.totalArea - largestArea) <= largestArea * 1e-6;
1517
+ if (tied ? group.representativeNormal.z < targetNormal!.z : group.totalArea > largestArea) {
1516
1518
  largestArea = group.totalArea;
1517
1519
  targetNormal = group.representativeNormal;
1518
1520
  }
1519
1521
  }
1520
1522
 
1521
1523
  if (!targetNormal) {
1522
- // If no suitable horizontal face found, maybe pick the lowest Z face?
1523
- // For now, throw an error or return unchanged.
1524
+ // Only a mesh without any face area reaches this.
1524
1525
  console.warn("Could not find a suitable large flat face to lay down. No changes made.");
1525
1526
  this.emit('operationComplete', { operationId, type: 'layFlat', success: true, output: stlFilePath, message: "No suitable flat face found." });
1526
1527
  return stlFilePath;