mcp-3d-printer-server 1.2.27 → 1.2.28

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.28] – 2026-11-04
4
+
5
+ ### Added
6
+
7
+ - `slice_stl` takes `enable_support`, `support_type` (`normal(auto)`, `tree(auto)`, `normal(manual)`, `tree(manual)`) and `support_threshold_angle`, written over the selected process profile like `layer_height` and `wall_loops`. Bambu Studio and OrcaSlicer only; PrusaSlicer and Cura refuse them. Tested with the fake Bambu Studio CLI install; bambu-printer-mcp 1.1.54 checks the same keys against the installed Bambu Studio 02.08.02.61. No printer was involved.
8
+
3
9
  ## [1.2.27] – 2026-11-04
4
10
 
5
11
  ### Added
package/README.md CHANGED
@@ -690,6 +690,8 @@ Options for the Bambu-compatible path:
690
690
 
691
691
  `layer_height` (mm), `wall_loops` (a whole number), and `sparse_infill_density` (such as `"15%"`) are written over the selected profile on every slicer: as the process keys `layer_height`, `wall_loops`, and `sparse_infill_density` for Bambu Studio and OrcaSlicer, and as `--layer-height`, `--perimeters`, and `--fill-density` for PrusaSlicer and Slic3r. They win over a template. Cura refuses them; set them in its definition file. Your profile files are never modified.
692
692
 
693
+ `enable_support` (true or false), `support_type` (`normal(auto)`, `tree(auto)`, `normal(manual)` or `tree(manual)`) and `support_threshold_angle` (0 to 90 degrees; 0 lets the slicer decide) are written over the selected process profile as the same-named Bambu Studio and OrcaSlicer keys. PrusaSlicer, Slic3r and Cura refuse them; set supports in their own profile.
694
+
693
695
  Generic slicers keep their own profile formats: PrusaSlicer and Slic3r load one exported config, plus an optional filament preset `.ini` from `load_filaments`, and generic OrcaSlicer takes `machine.json;process.json`, optionally followed by `|filament.json`. `slicer_type`, `slicer_profile`, and `filament_profile` fall back to `SLICER_TYPE`, `SLICER_PROFILE`, and `FILAMENT_PROFILE`. The slicer executable comes from `SLICER_PATH`; a per-call `slicer_path` requires `MCP_ALLOW_EXECUTABLE_ARG=1`. See the [slicing guide](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/SLICING.md#bambu-compatible-slicing).
694
696
 
695
697
  #### slice_with_template
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import { createServer as createHttpServer } from "node:http";
17
17
  import { randomUUID } from "node:crypto";
18
18
  import * as THREE from 'three';
19
19
  import { PrinterFactory } from "./printers/printer-factory.js";
20
- import { STLManipulator } from "./stl/stl-manipulator.js";
20
+ import { STLManipulator, SUPPORT_TYPES } from "./stl/stl-manipulator.js";
21
21
  import { parsePauseLayers, readPlateGcode, pausedLayers, machinePauseGcode } from "./slicer/layer-pauses.js";
22
22
  import { CounterpartService } from "./counterpart/service.js";
23
23
  import { CAMERA_STREAM_TOOLS } from "./counterpart/camera-tools.js";
@@ -318,6 +318,12 @@ const SLICE_TOOL_PROPERTIES = {
318
318
  type: "string",
319
319
  description: "Sparse infill density such as \"15%\" (0–100%), written over the selected process profile (Orca/Bambu Studio sparse_infill_density; PrusaSlicer --fill-density). Not mapped for Cura.",
320
320
  },
321
+ enable_support: { type: "boolean", description: "Turn support generation on or off (Orca/Bambu Studio enable_support), written over the selected process profile. Bambu Studio and OrcaSlicer only." },
322
+ support_type: { type: "string", enum: [...SUPPORT_TYPES], description: "Support style (Orca/Bambu Studio support_type), written over the selected process profile. Bambu Studio and OrcaSlicer only." },
323
+ support_threshold_angle: {
324
+ type: "integer", minimum: 0, maximum: 90,
325
+ description: "Overhang threshold in degrees for automatic supports (Orca/Bambu Studio support_threshold_angle; 0 lets the slicer decide). Bambu Studio and OrcaSlicer only.",
326
+ },
321
327
  };
322
328
  /**
323
329
  * upload_gcode / start_print results name the remote file that was stored or
@@ -348,7 +354,7 @@ const OUTPUT_SCHEMA_TOOLS = new Set([
348
354
  "camera_stream_start", "camera_frame", "camera_stream_stop",
349
355
  "fusion_mcp_status", "fusion_mcp_call", "fusion_mcp_new_design", "fusion_mcp_export",
350
356
  ]);
351
- /** layer_height / wall_loops / sparse_infill_density (PRD §8.4), validated before slicing. */
357
+ /** layer_height / wall_loops / sparse_infill_density / support settings (PRD §8.4), validated before slicing. */
352
358
  function parseProcessOverrides(args) {
353
359
  const overrides = {};
354
360
  if (args?.layer_height !== undefined) {
@@ -375,6 +381,25 @@ function parseProcessOverrides(args) {
375
381
  }
376
382
  overrides.sparseInfillDensity = `${value}%`;
377
383
  }
384
+ if (args?.enable_support !== undefined) {
385
+ if (typeof args.enable_support !== "boolean")
386
+ throw new Error("Invalid enable_support: expected true or false.");
387
+ overrides.enableSupport = args.enable_support;
388
+ }
389
+ if (args?.support_type !== undefined) {
390
+ const value = args.support_type;
391
+ if (typeof value !== "string" || !SUPPORT_TYPES.includes(value)) {
392
+ throw new Error(`Invalid support_type: expected one of ${SUPPORT_TYPES.join(", ")}.`);
393
+ }
394
+ overrides.supportType = value;
395
+ }
396
+ if (args?.support_threshold_angle !== undefined) {
397
+ const value = args.support_threshold_angle;
398
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 90) {
399
+ throw new Error("Invalid support_threshold_angle: expected an integer number of degrees from 0 to 90.");
400
+ }
401
+ overrides.supportThresholdAngle = value;
402
+ }
378
403
  return overrides;
379
404
  }
380
405
  function validateSliceBambuModel(model) {
@@ -15,7 +15,14 @@ export interface ProcessOverrides {
15
15
  wallLoops?: number;
16
16
  /** e.g. "15%" */
17
17
  sparseInfillDensity?: string;
18
+ enableSupport?: boolean;
19
+ /** normal(auto), tree(auto), normal(manual) or tree(manual) */
20
+ supportType?: string;
21
+ /** degrees, 0-90 */
22
+ supportThresholdAngle?: number;
18
23
  }
24
+ /** Bambu Studio's and OrcaSlicer's support_type values. */
25
+ export declare const SUPPORT_TYPES: readonly ["normal(auto)", "tree(auto)", "normal(manual)", "tree(manual)"];
19
26
  /** Orca/Bambu Studio process keys; profile JSON values are strings. */
20
27
  export declare function orcaProcessOverrides(overrides: ProcessOverrides | undefined): Record<string, string>;
21
28
  /** PrusaSlicer/Slic3r command-line options, which override the loaded config. */
@@ -13,6 +13,16 @@ import { BambuCliProfilePreparer, assertSlicedProjectOutput, bambuCliFailureReas
13
13
  import { runSlicerProcess } from '../slicer/run-slicer.js';
14
14
  import { SlicerError, asPreparationError } from '../slicer/slicer-error.js';
15
15
  import { customGcodesWithPauses, layerTops, machinePauseGcode, pausedLayers, readPlateGcode, readProjectCustomGcodes } from '../slicer/layer-pauses.js';
16
+ /** Bambu Studio's and OrcaSlicer's support_type values. */
17
+ export const SUPPORT_TYPES = ["normal(auto)", "tree(auto)", "normal(manual)", "tree(manual)"];
18
+ /** Support overrides only Bambu Studio and OrcaSlicer take; PrusaSlicer and Cura refuse them. */
19
+ function supportOverrideNames(overrides) {
20
+ return [
21
+ overrides?.enableSupport !== undefined ? "enable_support" : "",
22
+ overrides?.supportType !== undefined ? "support_type" : "",
23
+ overrides?.supportThresholdAngle !== undefined ? "support_threshold_angle" : "",
24
+ ].filter(Boolean);
25
+ }
16
26
  /** Orca/Bambu Studio process keys; profile JSON values are strings. */
17
27
  export function orcaProcessOverrides(overrides) {
18
28
  const keys = {};
@@ -22,10 +32,19 @@ export function orcaProcessOverrides(overrides) {
22
32
  keys.wall_loops = String(overrides.wallLoops);
23
33
  if (overrides?.sparseInfillDensity !== undefined)
24
34
  keys.sparse_infill_density = overrides.sparseInfillDensity;
35
+ if (overrides?.enableSupport !== undefined)
36
+ keys.enable_support = overrides.enableSupport ? "1" : "0";
37
+ if (overrides?.supportType !== undefined)
38
+ keys.support_type = overrides.supportType;
39
+ if (overrides?.supportThresholdAngle !== undefined)
40
+ keys.support_threshold_angle = String(overrides.supportThresholdAngle);
25
41
  return keys;
26
42
  }
27
43
  /** PrusaSlicer/Slic3r command-line options, which override the loaded config. */
28
44
  export function prusaSlicerOverrideArgs(overrides) {
45
+ const unsupported = supportOverrideNames(overrides);
46
+ if (unsupported.length)
47
+ throw new Error(`${unsupported.join(", ")} apply to Bambu Studio and OrcaSlicer only; set them in the PrusaSlicer config passed as slicer_profile.`);
29
48
  const args = [];
30
49
  if (overrides?.layerHeight !== undefined)
31
50
  args.push('--layer-height', String(overrides.layerHeight));
@@ -1000,7 +1019,7 @@ export class STLManipulator extends EventEmitter {
1000
1019
  break;
1001
1020
  case 'cura':
1002
1021
  if (Object.keys(orcaProcessOverrides(processOverrides)).length) {
1003
- throw new SlicerError('preparation', 'layer_height, wall_loops and sparse_infill_density are not mapped for Cura; set them in the Cura definition passed as slicer_profile.', { slicerType, slicerPath });
1022
+ throw new SlicerError('preparation', 'layer_height, wall_loops, sparse_infill_density and the support settings are not mapped for Cura; set them in the Cura definition passed as slicer_profile.', { slicerType, slicerPath });
1004
1023
  }
1005
1024
  // CuraEngine CLI args are different, often requiring -s for settings
1006
1025
  // Example: curaengine slice -v -j cura_settings.json -s layer_height=0.2 -o output.gcode -l input.stl
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.27",
4
+ "version": "1.2.28",
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.27",
10
+ "version": "1.2.28",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "mcp-3d-printer-server",
15
- "version": "1.2.27",
15
+ "version": "1.2.28",
16
16
  "runtimeHint": "npx",
17
17
  "transport": {
18
18
  "type": "stdio"
package/src/index.ts CHANGED
@@ -34,7 +34,7 @@ import { createServer as createHttpServer, type IncomingMessage, type Server as
34
34
  import { randomUUID } from "node:crypto";
35
35
  import * as THREE from 'three';
36
36
  import { PrinterFactory } from "./printers/printer-factory.js";
37
- import { STLManipulator, type BambuSliceOptions, type ProcessOverrides, type SliceOutput } from "./stl/stl-manipulator.js";
37
+ import { STLManipulator, SUPPORT_TYPES, type BambuSliceOptions, type ProcessOverrides, type SliceOutput } from "./stl/stl-manipulator.js";
38
38
  import { parsePauseLayers, readPlateGcode, pausedLayers, machinePauseGcode } from "./slicer/layer-pauses.js";
39
39
  import { CounterpartService, type BambuControlRoute, type PrinterTarget } from "./counterpart/service.js";
40
40
  import {CAMERA_STREAM_TOOLS} from "./counterpart/camera-tools.js";
@@ -396,6 +396,12 @@ const SLICE_TOOL_PROPERTIES = {
396
396
  type: "string",
397
397
  description: "Sparse infill density such as \"15%\" (0–100%), written over the selected process profile (Orca/Bambu Studio sparse_infill_density; PrusaSlicer --fill-density). Not mapped for Cura.",
398
398
  },
399
+ enable_support: { type: "boolean", description: "Turn support generation on or off (Orca/Bambu Studio enable_support), written over the selected process profile. Bambu Studio and OrcaSlicer only." },
400
+ support_type: { type: "string", enum: [...SUPPORT_TYPES], description: "Support style (Orca/Bambu Studio support_type), written over the selected process profile. Bambu Studio and OrcaSlicer only." },
401
+ support_threshold_angle: {
402
+ type: "integer", minimum: 0, maximum: 90,
403
+ description: "Overhang threshold in degrees for automatic supports (Orca/Bambu Studio support_threshold_angle; 0 lets the slicer decide). Bambu Studio and OrcaSlicer only.",
404
+ },
399
405
  };
400
406
 
401
407
  /**
@@ -428,7 +434,7 @@ const OUTPUT_SCHEMA_TOOLS = new Set([
428
434
  "fusion_mcp_status", "fusion_mcp_call", "fusion_mcp_new_design", "fusion_mcp_export",
429
435
  ]);
430
436
 
431
- /** layer_height / wall_loops / sparse_infill_density (PRD §8.4), validated before slicing. */
437
+ /** layer_height / wall_loops / sparse_infill_density / support settings (PRD §8.4), validated before slicing. */
432
438
  function parseProcessOverrides(args: Record<string, unknown> | undefined): ProcessOverrides {
433
439
  const overrides: ProcessOverrides = {};
434
440
  if (args?.layer_height !== undefined) {
@@ -455,6 +461,24 @@ function parseProcessOverrides(args: Record<string, unknown> | undefined): Proce
455
461
  }
456
462
  overrides.sparseInfillDensity = `${value}%`;
457
463
  }
464
+ if (args?.enable_support !== undefined) {
465
+ if (typeof args.enable_support !== "boolean") throw new Error("Invalid enable_support: expected true or false.");
466
+ overrides.enableSupport = args.enable_support;
467
+ }
468
+ if (args?.support_type !== undefined) {
469
+ const value = args.support_type;
470
+ if (typeof value !== "string" || !(SUPPORT_TYPES as readonly string[]).includes(value)) {
471
+ throw new Error(`Invalid support_type: expected one of ${SUPPORT_TYPES.join(", ")}.`);
472
+ }
473
+ overrides.supportType = value;
474
+ }
475
+ if (args?.support_threshold_angle !== undefined) {
476
+ const value = args.support_threshold_angle;
477
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 90) {
478
+ throw new Error("Invalid support_threshold_angle: expected an integer number of degrees from 0 to 90.");
479
+ }
480
+ overrides.supportThresholdAngle = value;
481
+ }
458
482
  return overrides;
459
483
  }
460
484
 
@@ -36,6 +36,23 @@ export interface ProcessOverrides {
36
36
  wallLoops?: number;
37
37
  /** e.g. "15%" */
38
38
  sparseInfillDensity?: string;
39
+ enableSupport?: boolean;
40
+ /** normal(auto), tree(auto), normal(manual) or tree(manual) */
41
+ supportType?: string;
42
+ /** degrees, 0-90 */
43
+ supportThresholdAngle?: number;
44
+ }
45
+
46
+ /** Bambu Studio's and OrcaSlicer's support_type values. */
47
+ export const SUPPORT_TYPES = ["normal(auto)", "tree(auto)", "normal(manual)", "tree(manual)"] as const;
48
+
49
+ /** Support overrides only Bambu Studio and OrcaSlicer take; PrusaSlicer and Cura refuse them. */
50
+ function supportOverrideNames(overrides: ProcessOverrides | undefined): string[] {
51
+ return [
52
+ overrides?.enableSupport !== undefined ? "enable_support" : "",
53
+ overrides?.supportType !== undefined ? "support_type" : "",
54
+ overrides?.supportThresholdAngle !== undefined ? "support_threshold_angle" : "",
55
+ ].filter(Boolean);
39
56
  }
40
57
 
41
58
  /** Orca/Bambu Studio process keys; profile JSON values are strings. */
@@ -44,11 +61,16 @@ export function orcaProcessOverrides(overrides: ProcessOverrides | undefined): R
44
61
  if (overrides?.layerHeight !== undefined) keys.layer_height = String(overrides.layerHeight);
45
62
  if (overrides?.wallLoops !== undefined) keys.wall_loops = String(overrides.wallLoops);
46
63
  if (overrides?.sparseInfillDensity !== undefined) keys.sparse_infill_density = overrides.sparseInfillDensity;
64
+ if (overrides?.enableSupport !== undefined) keys.enable_support = overrides.enableSupport ? "1" : "0";
65
+ if (overrides?.supportType !== undefined) keys.support_type = overrides.supportType;
66
+ if (overrides?.supportThresholdAngle !== undefined) keys.support_threshold_angle = String(overrides.supportThresholdAngle);
47
67
  return keys;
48
68
  }
49
69
 
50
70
  /** PrusaSlicer/Slic3r command-line options, which override the loaded config. */
51
71
  export function prusaSlicerOverrideArgs(overrides: ProcessOverrides | undefined): string[] {
72
+ const unsupported = supportOverrideNames(overrides);
73
+ if (unsupported.length) throw new Error(`${unsupported.join(", ")} apply to Bambu Studio and OrcaSlicer only; set them in the PrusaSlicer config passed as slicer_profile.`);
52
74
  const args: string[] = [];
53
75
  if (overrides?.layerHeight !== undefined) args.push('--layer-height', String(overrides.layerHeight));
54
76
  if (overrides?.wallLoops !== undefined) args.push('--perimeters', String(overrides.wallLoops));
@@ -1295,7 +1317,7 @@ export class STLManipulator extends EventEmitter {
1295
1317
  case 'cura':
1296
1318
  if (Object.keys(orcaProcessOverrides(processOverrides)).length) {
1297
1319
  throw new SlicerError('preparation',
1298
- 'layer_height, wall_loops and sparse_infill_density are not mapped for Cura; set them in the Cura definition passed as slicer_profile.',
1320
+ 'layer_height, wall_loops, sparse_infill_density and the support settings are not mapped for Cura; set them in the Cura definition passed as slicer_profile.',
1299
1321
  { slicerType, slicerPath });
1300
1322
  }
1301
1323
  // CuraEngine CLI args are different, often requiring -s for settings