bambu-printer-mcp 1.0.3 → 1.0.5
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/README.md +47 -0
- package/dist/index.js +64 -3
- package/dist/stl/stl-manipulator.d.ts +22 -1
- package/dist/stl/stl-manipulator.js +38 -2
- package/package.json +6 -3
- package/src/index.ts +51 -4
- package/src/stl/stl-manipulator.ts +46 -2
package/README.md
CHANGED
|
@@ -716,6 +716,53 @@ Slice an STL or 3MF file using an external slicer and return the path to the out
|
|
|
716
716
|
|
|
717
717
|
For printing on a Bambu printer, the recommended workflow is: slice with `bambustudio` to get a sliced 3MF, then pass that output path to `print_3mf`.
|
|
718
718
|
|
|
719
|
+
#### BambuStudio Slicer Options
|
|
720
|
+
|
|
721
|
+
When `slicer_type` is `bambustudio` (the default), these additional parameters are available on `slice_stl`:
|
|
722
|
+
|
|
723
|
+
| Parameter | Type | Description |
|
|
724
|
+
|-----------|------|-------------|
|
|
725
|
+
| `uptodate` | boolean | Update 3MF configs to latest BambuStudio presets |
|
|
726
|
+
| `repetitions` | number | Number of copies to print |
|
|
727
|
+
| `orient` | boolean | Auto-orient model for optimal printability |
|
|
728
|
+
| `arrange` | boolean | Auto-arrange objects on the build plate |
|
|
729
|
+
| `ensure_on_bed` | boolean | Lift floating models onto the bed |
|
|
730
|
+
| `clone_objects` | string | Clone counts per object, comma-separated (e.g. `"1,3,1,10"`) |
|
|
731
|
+
| `skip_objects` | string | Object indices to skip, comma-separated (e.g. `"3,5,10"`) |
|
|
732
|
+
| `load_filaments` | string | Filament profile paths, semicolon-separated |
|
|
733
|
+
| `load_filament_ids` | string | Filament-to-object mapping, comma-separated |
|
|
734
|
+
| `enable_timelapse` | boolean | Enable timelapse-aware slicing |
|
|
735
|
+
| `allow_mix_temp` | boolean | Allow mixed-temperature filaments on one plate |
|
|
736
|
+
| `scale` | number | Uniform scale factor |
|
|
737
|
+
| `rotate` | number | Z-axis rotation in degrees |
|
|
738
|
+
| `rotate_x` | number | X-axis rotation in degrees |
|
|
739
|
+
| `rotate_y` | number | Y-axis rotation in degrees |
|
|
740
|
+
| `min_save` | boolean | Produce smaller output 3MF (faster uploads) |
|
|
741
|
+
| `skip_modified_gcodes` | boolean | Ignore stale custom gcodes in the 3MF |
|
|
742
|
+
| `slice_plate` | number | Which plate to slice (0 = all plates, default: 0) |
|
|
743
|
+
|
|
744
|
+
**Example: Slice with auto-orient and 3 copies**
|
|
745
|
+
```json
|
|
746
|
+
{
|
|
747
|
+
"stl_path": "/path/to/model.stl",
|
|
748
|
+
"bambu_model": "p1s",
|
|
749
|
+
"orient": true,
|
|
750
|
+
"arrange": true,
|
|
751
|
+
"repetitions": 3
|
|
752
|
+
}
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
#### Smart Defaults (print_3mf auto-slice)
|
|
756
|
+
|
|
757
|
+
When `print_3mf` detects an unsliced 3MF and auto-slices it, these defaults are applied automatically:
|
|
758
|
+
|
|
759
|
+
- `uptodate: true` -- prevents stale config bugs from downloaded 3MFs
|
|
760
|
+
- `ensure_on_bed: true` -- safety net, lifts floating models onto the bed
|
|
761
|
+
- `min_save: true` -- smaller output for faster FTP uploads to the printer
|
|
762
|
+
- `skip_modified_gcodes: true` -- strips custom gcodes from other users' profiles
|
|
763
|
+
|
|
764
|
+
These defaults keep you safe when printing downloaded models. When calling `slice_stl` directly, you have full control over every flag.
|
|
765
|
+
|
|
719
766
|
</details>
|
|
720
767
|
|
|
721
768
|
<details>
|
package/dist/index.js
CHANGED
|
@@ -333,7 +333,25 @@ class BambuPrinterMCPServer {
|
|
|
333
333
|
},
|
|
334
334
|
slicer_path: { type: "string", description: "Path to the slicer executable (default: value from env)" },
|
|
335
335
|
slicer_profile: { type: "string", description: "Path to the slicer profile/config file (optional, overrides bambu_model preset)" },
|
|
336
|
-
nozzle_diameter: { type: "string", description: "Nozzle diameter in mm (default: 0.4)" }
|
|
336
|
+
nozzle_diameter: { type: "string", description: "Nozzle diameter in mm (default: 0.4)" },
|
|
337
|
+
uptodate: { type: "boolean", description: "Refresh 3MF preset configs to match the latest BambuStudio version. Use when slicing downloaded or older 3MF files to prevent stale-config failures." },
|
|
338
|
+
repetitions: { type: "number", description: "Print N identical copies of the model. Each copy gets its own plate placement. Example: 3 prints three copies." },
|
|
339
|
+
orient: { type: "boolean", description: "Auto-orient the model for optimal printability (minimize supports, maximize bed adhesion). Recommended for raw STL imports that lack a pre-set orientation." },
|
|
340
|
+
arrange: { type: "boolean", description: "Auto-arrange all objects on the build plate with optimal spacing. Recommended when importing STLs or adding multiple objects. Set false to preserve existing plate layout." },
|
|
341
|
+
ensure_on_bed: { type: "boolean", description: "Detect models floating above the bed and lower them onto the build surface. Safety net for imported models with incorrect Z origins." },
|
|
342
|
+
clone_objects: { type: "string", description: "Duplicate specific objects on the plate. Comma-separated clone counts per object index, e.g. '1,3,1,10' clones object 0 once, object 1 three times, etc." },
|
|
343
|
+
skip_objects: { type: "string", description: "Skip specific objects during slicing by index. Comma-separated, e.g. '3,5,10'. Useful for multi-object 3MFs where you only want to print some parts." },
|
|
344
|
+
load_filaments: { type: "string", description: "Override filament profiles. Semicolon-separated paths to filament JSON configs, e.g. 'pla_basic.json;petg_cf.json'." },
|
|
345
|
+
load_filament_ids: { type: "string", description: "Map filaments to objects/parts. Comma-separated IDs matching load_filaments order, e.g. '1,2,3,1' assigns filament 1 to objects 0 and 3." },
|
|
346
|
+
enable_timelapse: { type: "boolean", description: "Insert timelapse parking moves into gcode. The toolhead parks at a fixed position each layer for camera capture. Adds ~10% print time." },
|
|
347
|
+
allow_mix_temp: { type: "boolean", description: "Allow filaments with different temperature requirements on the same plate. Required for multi-material prints mixing e.g. PLA and PETG." },
|
|
348
|
+
scale: { type: "number", description: "Uniform scale factor applied to all axes. 1.0 = original size, 2.0 = double, 0.5 = half. Applied before slicing." },
|
|
349
|
+
rotate: { type: "number", description: "Rotate the model around the Z-axis (vertical) by this many degrees before slicing. Positive = counterclockwise when viewed from above." },
|
|
350
|
+
rotate_x: { type: "number", description: "Rotate the model around the X-axis by this many degrees before slicing. Useful for reorienting prints for better layer adhesion." },
|
|
351
|
+
rotate_y: { type: "number", description: "Rotate the model around the Y-axis by this many degrees before slicing. Useful for reorienting prints for better layer adhesion." },
|
|
352
|
+
min_save: { type: "boolean", description: "Write a smaller output 3MF by omitting non-essential metadata. Reduces file size for faster FTP upload to the printer." },
|
|
353
|
+
skip_modified_gcodes: { type: "boolean", description: "Strip custom start/end gcodes embedded in the 3MF. Recommended for downloaded 3MFs since custom gcodes from other users' profiles may be unsafe for your printer." },
|
|
354
|
+
slice_plate: { type: "number", description: "Which plate index to slice. 0 = all plates (default). Use 1, 2, etc. to slice only a specific plate in multi-plate 3MF projects." }
|
|
337
355
|
},
|
|
338
356
|
required: ["stl_path", "bambu_model"]
|
|
339
357
|
}
|
|
@@ -599,8 +617,45 @@ class BambuPrinterMCPServer {
|
|
|
599
617
|
const nozzleDiam = String(args?.nozzle_diameter || DEFAULT_NOZZLE_DIAMETER);
|
|
600
618
|
// Resolve printer preset for BambuStudio slicer
|
|
601
619
|
const printerPreset = BAMBU_MODEL_PRESETS[sliceModel]?.(nozzleDiam);
|
|
620
|
+
const sliceBambuOptions = {};
|
|
621
|
+
if (args?.uptodate !== undefined)
|
|
622
|
+
sliceBambuOptions.uptodate = Boolean(args.uptodate);
|
|
623
|
+
if (args?.repetitions !== undefined)
|
|
624
|
+
sliceBambuOptions.repetitions = Number(args.repetitions);
|
|
625
|
+
if (args?.orient !== undefined)
|
|
626
|
+
sliceBambuOptions.orient = Boolean(args.orient);
|
|
627
|
+
if (args?.arrange !== undefined)
|
|
628
|
+
sliceBambuOptions.arrange = Boolean(args.arrange);
|
|
629
|
+
if (args?.ensure_on_bed !== undefined)
|
|
630
|
+
sliceBambuOptions.ensureOnBed = Boolean(args.ensure_on_bed);
|
|
631
|
+
if (args?.clone_objects !== undefined)
|
|
632
|
+
sliceBambuOptions.cloneObjects = String(args.clone_objects);
|
|
633
|
+
if (args?.skip_objects !== undefined)
|
|
634
|
+
sliceBambuOptions.skipObjects = String(args.skip_objects);
|
|
635
|
+
if (args?.load_filaments !== undefined)
|
|
636
|
+
sliceBambuOptions.loadFilaments = String(args.load_filaments);
|
|
637
|
+
if (args?.load_filament_ids !== undefined)
|
|
638
|
+
sliceBambuOptions.loadFilamentIds = String(args.load_filament_ids);
|
|
639
|
+
if (args?.enable_timelapse !== undefined)
|
|
640
|
+
sliceBambuOptions.enableTimelapse = Boolean(args.enable_timelapse);
|
|
641
|
+
if (args?.allow_mix_temp !== undefined)
|
|
642
|
+
sliceBambuOptions.allowMixTemp = Boolean(args.allow_mix_temp);
|
|
643
|
+
if (args?.scale !== undefined)
|
|
644
|
+
sliceBambuOptions.scale = Number(args.scale);
|
|
645
|
+
if (args?.rotate !== undefined)
|
|
646
|
+
sliceBambuOptions.rotate = Number(args.rotate);
|
|
647
|
+
if (args?.rotate_x !== undefined)
|
|
648
|
+
sliceBambuOptions.rotateX = Number(args.rotate_x);
|
|
649
|
+
if (args?.rotate_y !== undefined)
|
|
650
|
+
sliceBambuOptions.rotateY = Number(args.rotate_y);
|
|
651
|
+
if (args?.min_save !== undefined)
|
|
652
|
+
sliceBambuOptions.minSave = Boolean(args.min_save);
|
|
653
|
+
if (args?.skip_modified_gcodes !== undefined)
|
|
654
|
+
sliceBambuOptions.skipModifiedGcodes = Boolean(args.skip_modified_gcodes);
|
|
655
|
+
if (args?.slice_plate !== undefined)
|
|
656
|
+
sliceBambuOptions.slicePlate = Number(args.slice_plate);
|
|
602
657
|
result = await this.stlManipulator.sliceSTL(String(args.stl_path), slicerType, slicerPath, slicerProfile || undefined, undefined, // progressCallback
|
|
603
|
-
printerPreset);
|
|
658
|
+
printerPreset, sliceBambuOptions);
|
|
604
659
|
break;
|
|
605
660
|
}
|
|
606
661
|
case "print_3mf": {
|
|
@@ -623,8 +678,14 @@ class BambuPrinterMCPServer {
|
|
|
623
678
|
const hasGcode = Object.keys(zip.files).some(f => f.match(/Metadata\/plate_\d+\.gcode/i) || f.endsWith('.gcode'));
|
|
624
679
|
if (!hasGcode) {
|
|
625
680
|
console.log(`3MF has no gcode — auto-slicing with ${slicerType} for ${printModel}`);
|
|
681
|
+
const autoSliceOptions = {
|
|
682
|
+
uptodate: true,
|
|
683
|
+
ensureOnBed: true,
|
|
684
|
+
minSave: true,
|
|
685
|
+
skipModifiedGcodes: true,
|
|
686
|
+
};
|
|
626
687
|
threeMFPath = await this.stlManipulator.sliceSTL(threeMFPath, slicerType, slicerPath, slicerProfile || undefined, undefined, // progressCallback
|
|
627
|
-
printPreset);
|
|
688
|
+
printPreset, autoSliceOptions);
|
|
628
689
|
console.log("Auto-sliced to: " + threeMFPath);
|
|
629
690
|
}
|
|
630
691
|
}
|
|
@@ -22,6 +22,26 @@ export type TransformationParams = {
|
|
|
22
22
|
relative?: boolean;
|
|
23
23
|
selectionBounds?: THREE.Box3;
|
|
24
24
|
};
|
|
25
|
+
export interface BambuSliceOptions {
|
|
26
|
+
uptodate?: boolean;
|
|
27
|
+
repetitions?: number;
|
|
28
|
+
orient?: boolean;
|
|
29
|
+
arrange?: boolean;
|
|
30
|
+
ensureOnBed?: boolean;
|
|
31
|
+
cloneObjects?: string;
|
|
32
|
+
skipObjects?: string;
|
|
33
|
+
loadFilaments?: string;
|
|
34
|
+
loadFilamentIds?: string;
|
|
35
|
+
enableTimelapse?: boolean;
|
|
36
|
+
allowMixTemp?: boolean;
|
|
37
|
+
scale?: number;
|
|
38
|
+
rotate?: number;
|
|
39
|
+
rotateX?: number;
|
|
40
|
+
rotateY?: number;
|
|
41
|
+
minSave?: boolean;
|
|
42
|
+
skipModifiedGcodes?: boolean;
|
|
43
|
+
slicePlate?: number;
|
|
44
|
+
}
|
|
25
45
|
export declare class STLManipulator extends EventEmitter {
|
|
26
46
|
private tempDir;
|
|
27
47
|
private activeOperations;
|
|
@@ -107,9 +127,10 @@ export declare class STLManipulator extends EventEmitter {
|
|
|
107
127
|
* @param slicerProfile Optional path to the slicer profile/config file
|
|
108
128
|
* @param progressCallback Optional callback for progress updates
|
|
109
129
|
* @param printerPreset Optional BambuStudio printer preset name (e.g., "Bambu Lab P1S 0.4 nozzle")
|
|
130
|
+
* @param bambuOptions Optional BambuStudio-specific CLI flags
|
|
110
131
|
* @returns Path to the generated G-code or sliced 3MF file
|
|
111
132
|
*/
|
|
112
|
-
sliceSTL(stlFilePath: string, slicerType: 'prusaslicer' | 'cura' | 'slic3r' | 'orcaslicer' | 'bambustudio', slicerPath: string, slicerProfile?: string, progressCallback?: ProgressCallback, printerPreset?: string): Promise<string>;
|
|
133
|
+
sliceSTL(stlFilePath: string, slicerType: 'prusaslicer' | 'cura' | 'slic3r' | 'orcaslicer' | 'bambustudio', slicerPath: string, slicerProfile?: string, progressCallback?: ProgressCallback, printerPreset?: string, bambuOptions?: BambuSliceOptions): Promise<string>;
|
|
113
134
|
/**
|
|
114
135
|
* Enhanced version of confirmTemperatures with better error handling
|
|
115
136
|
* @param gcodePath Path to the G-code file
|
|
@@ -738,9 +738,10 @@ export class STLManipulator extends EventEmitter {
|
|
|
738
738
|
* @param slicerProfile Optional path to the slicer profile/config file
|
|
739
739
|
* @param progressCallback Optional callback for progress updates
|
|
740
740
|
* @param printerPreset Optional BambuStudio printer preset name (e.g., "Bambu Lab P1S 0.4 nozzle")
|
|
741
|
+
* @param bambuOptions Optional BambuStudio-specific CLI flags
|
|
741
742
|
* @returns Path to the generated G-code or sliced 3MF file
|
|
742
743
|
*/
|
|
743
|
-
async sliceSTL(stlFilePath, slicerType, slicerPath, slicerProfile, progressCallback, printerPreset) {
|
|
744
|
+
async sliceSTL(stlFilePath, slicerType, slicerPath, slicerProfile, progressCallback, printerPreset, bambuOptions) {
|
|
744
745
|
const operationId = this.generateOperationId();
|
|
745
746
|
this.activeOperations.set(operationId, true);
|
|
746
747
|
if (!fs.existsSync(slicerPath)) {
|
|
@@ -809,7 +810,7 @@ export class STLManipulator extends EventEmitter {
|
|
|
809
810
|
const outputBase = path.basename(stlFilePath, is3mf ? '.3mf' : '.stl');
|
|
810
811
|
const bambuOutputPath = path.join(this.tempDir, outputBase + '_sliced.3mf');
|
|
811
812
|
args = [
|
|
812
|
-
'--slice',
|
|
813
|
+
'--slice', String(bambuOptions?.slicePlate ?? 0),
|
|
813
814
|
'--export-3mf', bambuOutputPath,
|
|
814
815
|
];
|
|
815
816
|
if (slicerProfile) {
|
|
@@ -825,6 +826,41 @@ export class STLManipulator extends EventEmitter {
|
|
|
825
826
|
fs.writeFileSync(presetJson, JSON.stringify({ printer_settings_id: printerPreset }));
|
|
826
827
|
args.push('--load-settings', presetJson);
|
|
827
828
|
}
|
|
829
|
+
// BambuSliceOptions flags
|
|
830
|
+
if (bambuOptions?.uptodate)
|
|
831
|
+
args.push('--uptodate');
|
|
832
|
+
if (bambuOptions?.ensureOnBed)
|
|
833
|
+
args.push('--ensure-on-bed');
|
|
834
|
+
if (bambuOptions?.enableTimelapse)
|
|
835
|
+
args.push('--enable-timelapse');
|
|
836
|
+
if (bambuOptions?.allowMixTemp)
|
|
837
|
+
args.push('--allow-mix-temp');
|
|
838
|
+
if (bambuOptions?.minSave)
|
|
839
|
+
args.push('--min-save');
|
|
840
|
+
if (bambuOptions?.skipModifiedGcodes)
|
|
841
|
+
args.push('--skip-modified-gcodes');
|
|
842
|
+
if (bambuOptions?.orient !== undefined)
|
|
843
|
+
args.push('--orient', bambuOptions.orient ? '1' : '0');
|
|
844
|
+
if (bambuOptions?.arrange !== undefined)
|
|
845
|
+
args.push('--arrange', bambuOptions.arrange ? '1' : '0');
|
|
846
|
+
if (bambuOptions?.repetitions !== undefined)
|
|
847
|
+
args.push('--repetitions', String(bambuOptions.repetitions));
|
|
848
|
+
if (bambuOptions?.scale !== undefined)
|
|
849
|
+
args.push('--scale', String(bambuOptions.scale));
|
|
850
|
+
if (bambuOptions?.rotate !== undefined)
|
|
851
|
+
args.push('--rotate', String(bambuOptions.rotate));
|
|
852
|
+
if (bambuOptions?.rotateX !== undefined)
|
|
853
|
+
args.push('--rotate-x', String(bambuOptions.rotateX));
|
|
854
|
+
if (bambuOptions?.rotateY !== undefined)
|
|
855
|
+
args.push('--rotate-y', String(bambuOptions.rotateY));
|
|
856
|
+
if (bambuOptions?.cloneObjects)
|
|
857
|
+
args.push('--clone-objects', bambuOptions.cloneObjects);
|
|
858
|
+
if (bambuOptions?.skipObjects)
|
|
859
|
+
args.push('--skip-objects', bambuOptions.skipObjects);
|
|
860
|
+
if (bambuOptions?.loadFilaments)
|
|
861
|
+
args.push('--load-filaments', bambuOptions.loadFilaments);
|
|
862
|
+
if (bambuOptions?.loadFilamentIds)
|
|
863
|
+
args.push('--load-filament-ids', bambuOptions.loadFilamentIds);
|
|
828
864
|
args.push(stlFilePath);
|
|
829
865
|
// Override outputFilePath for bambustudio since it produces 3MF, not gcode
|
|
830
866
|
outputFilePath = bambuOutputPath;
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bambu-printer-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "MCP server for Bambu Lab 3D printers with STL manipulation, slicing, and print management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"repository":
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/DMontgomery40/bambu-printer-mcp.git"
|
|
9
|
+
},
|
|
7
10
|
"type": "module",
|
|
8
11
|
"scripts": {
|
|
9
12
|
"start": "node dist/index.js",
|
|
@@ -13,7 +16,7 @@
|
|
|
13
16
|
"test": "npm run build && node --test tests/**/*.test.mjs"
|
|
14
17
|
},
|
|
15
18
|
"bin": {
|
|
16
|
-
"bambu-printer-mcp": "
|
|
19
|
+
"bambu-printer-mcp": "dist/index.js"
|
|
17
20
|
},
|
|
18
21
|
"files": [
|
|
19
22
|
"dist",
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ import fs from "fs";
|
|
|
16
16
|
import path from "path";
|
|
17
17
|
import { createServer as createHttpServer, type IncomingMessage, type Server as HttpServer, type ServerResponse } from "node:http";
|
|
18
18
|
import { randomUUID } from "node:crypto";
|
|
19
|
-
import { STLManipulator } from "./stl/stl-manipulator.js";
|
|
19
|
+
import { STLManipulator, type BambuSliceOptions } from "./stl/stl-manipulator.js";
|
|
20
20
|
import { parse3MF } from './3mf_parser.js';
|
|
21
21
|
import { BambuImplementation } from "./printers/bambu.js";
|
|
22
22
|
|
|
@@ -405,7 +405,25 @@ class BambuPrinterMCPServer {
|
|
|
405
405
|
},
|
|
406
406
|
slicer_path: { type: "string", description: "Path to the slicer executable (default: value from env)" },
|
|
407
407
|
slicer_profile: { type: "string", description: "Path to the slicer profile/config file (optional, overrides bambu_model preset)" },
|
|
408
|
-
nozzle_diameter: { type: "string", description: "Nozzle diameter in mm (default: 0.4)" }
|
|
408
|
+
nozzle_diameter: { type: "string", description: "Nozzle diameter in mm (default: 0.4)" },
|
|
409
|
+
uptodate: { type: "boolean", description: "Refresh 3MF preset configs to match the latest BambuStudio version. Use when slicing downloaded or older 3MF files to prevent stale-config failures." },
|
|
410
|
+
repetitions: { type: "number", description: "Print N identical copies of the model. Each copy gets its own plate placement. Example: 3 prints three copies." },
|
|
411
|
+
orient: { type: "boolean", description: "Auto-orient the model for optimal printability (minimize supports, maximize bed adhesion). Recommended for raw STL imports that lack a pre-set orientation." },
|
|
412
|
+
arrange: { type: "boolean", description: "Auto-arrange all objects on the build plate with optimal spacing. Recommended when importing STLs or adding multiple objects. Set false to preserve existing plate layout." },
|
|
413
|
+
ensure_on_bed: { type: "boolean", description: "Detect models floating above the bed and lower them onto the build surface. Safety net for imported models with incorrect Z origins." },
|
|
414
|
+
clone_objects: { type: "string", description: "Duplicate specific objects on the plate. Comma-separated clone counts per object index, e.g. '1,3,1,10' clones object 0 once, object 1 three times, etc." },
|
|
415
|
+
skip_objects: { type: "string", description: "Skip specific objects during slicing by index. Comma-separated, e.g. '3,5,10'. Useful for multi-object 3MFs where you only want to print some parts." },
|
|
416
|
+
load_filaments: { type: "string", description: "Override filament profiles. Semicolon-separated paths to filament JSON configs, e.g. 'pla_basic.json;petg_cf.json'." },
|
|
417
|
+
load_filament_ids: { type: "string", description: "Map filaments to objects/parts. Comma-separated IDs matching load_filaments order, e.g. '1,2,3,1' assigns filament 1 to objects 0 and 3." },
|
|
418
|
+
enable_timelapse: { type: "boolean", description: "Insert timelapse parking moves into gcode. The toolhead parks at a fixed position each layer for camera capture. Adds ~10% print time." },
|
|
419
|
+
allow_mix_temp: { type: "boolean", description: "Allow filaments with different temperature requirements on the same plate. Required for multi-material prints mixing e.g. PLA and PETG." },
|
|
420
|
+
scale: { type: "number", description: "Uniform scale factor applied to all axes. 1.0 = original size, 2.0 = double, 0.5 = half. Applied before slicing." },
|
|
421
|
+
rotate: { type: "number", description: "Rotate the model around the Z-axis (vertical) by this many degrees before slicing. Positive = counterclockwise when viewed from above." },
|
|
422
|
+
rotate_x: { type: "number", description: "Rotate the model around the X-axis by this many degrees before slicing. Useful for reorienting prints for better layer adhesion." },
|
|
423
|
+
rotate_y: { type: "number", description: "Rotate the model around the Y-axis by this many degrees before slicing. Useful for reorienting prints for better layer adhesion." },
|
|
424
|
+
min_save: { type: "boolean", description: "Write a smaller output 3MF by omitting non-essential metadata. Reduces file size for faster FTP upload to the printer." },
|
|
425
|
+
skip_modified_gcodes: { type: "boolean", description: "Strip custom start/end gcodes embedded in the 3MF. Recommended for downloaded 3MFs since custom gcodes from other users' profiles may be unsafe for your printer." },
|
|
426
|
+
slice_plate: { type: "number", description: "Which plate index to slice. 0 = all plates (default). Use 1, 2, etc. to slice only a specific plate in multi-plate 3MF projects." }
|
|
409
427
|
},
|
|
410
428
|
required: ["stl_path", "bambu_model"]
|
|
411
429
|
}
|
|
@@ -703,11 +721,33 @@ class BambuPrinterMCPServer {
|
|
|
703
721
|
const nozzleDiam = String(args?.nozzle_diameter || DEFAULT_NOZZLE_DIAMETER);
|
|
704
722
|
// Resolve printer preset for BambuStudio slicer
|
|
705
723
|
const printerPreset = BAMBU_MODEL_PRESETS[sliceModel]?.(nozzleDiam);
|
|
724
|
+
|
|
725
|
+
const sliceBambuOptions: BambuSliceOptions = {};
|
|
726
|
+
if (args?.uptodate !== undefined) sliceBambuOptions.uptodate = Boolean(args.uptodate);
|
|
727
|
+
if (args?.repetitions !== undefined) sliceBambuOptions.repetitions = Number(args.repetitions);
|
|
728
|
+
if (args?.orient !== undefined) sliceBambuOptions.orient = Boolean(args.orient);
|
|
729
|
+
if (args?.arrange !== undefined) sliceBambuOptions.arrange = Boolean(args.arrange);
|
|
730
|
+
if (args?.ensure_on_bed !== undefined) sliceBambuOptions.ensureOnBed = Boolean(args.ensure_on_bed);
|
|
731
|
+
if (args?.clone_objects !== undefined) sliceBambuOptions.cloneObjects = String(args.clone_objects);
|
|
732
|
+
if (args?.skip_objects !== undefined) sliceBambuOptions.skipObjects = String(args.skip_objects);
|
|
733
|
+
if (args?.load_filaments !== undefined) sliceBambuOptions.loadFilaments = String(args.load_filaments);
|
|
734
|
+
if (args?.load_filament_ids !== undefined) sliceBambuOptions.loadFilamentIds = String(args.load_filament_ids);
|
|
735
|
+
if (args?.enable_timelapse !== undefined) sliceBambuOptions.enableTimelapse = Boolean(args.enable_timelapse);
|
|
736
|
+
if (args?.allow_mix_temp !== undefined) sliceBambuOptions.allowMixTemp = Boolean(args.allow_mix_temp);
|
|
737
|
+
if (args?.scale !== undefined) sliceBambuOptions.scale = Number(args.scale);
|
|
738
|
+
if (args?.rotate !== undefined) sliceBambuOptions.rotate = Number(args.rotate);
|
|
739
|
+
if (args?.rotate_x !== undefined) sliceBambuOptions.rotateX = Number(args.rotate_x);
|
|
740
|
+
if (args?.rotate_y !== undefined) sliceBambuOptions.rotateY = Number(args.rotate_y);
|
|
741
|
+
if (args?.min_save !== undefined) sliceBambuOptions.minSave = Boolean(args.min_save);
|
|
742
|
+
if (args?.skip_modified_gcodes !== undefined) sliceBambuOptions.skipModifiedGcodes = Boolean(args.skip_modified_gcodes);
|
|
743
|
+
if (args?.slice_plate !== undefined) sliceBambuOptions.slicePlate = Number(args.slice_plate);
|
|
744
|
+
|
|
706
745
|
result = await this.stlManipulator.sliceSTL(
|
|
707
746
|
String(args.stl_path), slicerType, slicerPath,
|
|
708
747
|
slicerProfile || undefined,
|
|
709
748
|
undefined, // progressCallback
|
|
710
|
-
printerPreset
|
|
749
|
+
printerPreset,
|
|
750
|
+
sliceBambuOptions
|
|
711
751
|
);
|
|
712
752
|
break;
|
|
713
753
|
}
|
|
@@ -737,10 +777,17 @@ class BambuPrinterMCPServer {
|
|
|
737
777
|
);
|
|
738
778
|
if (!hasGcode) {
|
|
739
779
|
console.log(`3MF has no gcode — auto-slicing with ${slicerType} for ${printModel}`);
|
|
780
|
+
const autoSliceOptions: BambuSliceOptions = {
|
|
781
|
+
uptodate: true,
|
|
782
|
+
ensureOnBed: true,
|
|
783
|
+
minSave: true,
|
|
784
|
+
skipModifiedGcodes: true,
|
|
785
|
+
};
|
|
740
786
|
threeMFPath = await this.stlManipulator.sliceSTL(
|
|
741
787
|
threeMFPath, slicerType, slicerPath, slicerProfile || undefined,
|
|
742
788
|
undefined, // progressCallback
|
|
743
|
-
printPreset
|
|
789
|
+
printPreset,
|
|
790
|
+
autoSliceOptions
|
|
744
791
|
);
|
|
745
792
|
console.log("Auto-sliced to: " + threeMFPath);
|
|
746
793
|
}
|
|
@@ -40,6 +40,28 @@ export type TransformationParams = {
|
|
|
40
40
|
selectionBounds?: THREE.Box3;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
// BambuStudio CLI flags exposed via the slice_stl tool
|
|
44
|
+
export interface BambuSliceOptions {
|
|
45
|
+
uptodate?: boolean; // --uptodate: update 3MF configs to latest presets
|
|
46
|
+
repetitions?: number; // --repetitions N: print N copies
|
|
47
|
+
orient?: boolean; // --orient 1: auto-orient for printability
|
|
48
|
+
arrange?: boolean; // --arrange 1: auto-arrange objects on plate
|
|
49
|
+
ensureOnBed?: boolean; // --ensure-on-bed: lift floating models onto bed
|
|
50
|
+
cloneObjects?: string; // --clone-objects "1,3,1,10": clone counts per object
|
|
51
|
+
skipObjects?: string; // --skip-objects "3,5,10": skip specific objects
|
|
52
|
+
loadFilaments?: string; // --load-filaments "f1.json;f2.json": filament profiles
|
|
53
|
+
loadFilamentIds?: string; // --load-filament-ids "1,2,3,1": filament-to-object mapping
|
|
54
|
+
enableTimelapse?: boolean; // --enable-timelapse: timelapse-aware slicing
|
|
55
|
+
allowMixTemp?: boolean; // --allow-mix-temp: allow mixed-temp filaments
|
|
56
|
+
scale?: number; // --scale factor: uniform scale
|
|
57
|
+
rotate?: number; // --rotate degrees: Z-axis rotation
|
|
58
|
+
rotateX?: number; // --rotate-x degrees
|
|
59
|
+
rotateY?: number; // --rotate-y degrees
|
|
60
|
+
minSave?: boolean; // --min-save: smaller output 3MF
|
|
61
|
+
skipModifiedGcodes?: boolean;// --skip-modified-gcodes: ignore stale custom gcodes
|
|
62
|
+
slicePlate?: number; // --slice N: which plate (default: 0 = all)
|
|
63
|
+
}
|
|
64
|
+
|
|
43
65
|
export class STLManipulator extends EventEmitter {
|
|
44
66
|
private tempDir: string;
|
|
45
67
|
private activeOperations: Map<string, boolean> = new Map();
|
|
@@ -966,6 +988,7 @@ export class STLManipulator extends EventEmitter {
|
|
|
966
988
|
* @param slicerProfile Optional path to the slicer profile/config file
|
|
967
989
|
* @param progressCallback Optional callback for progress updates
|
|
968
990
|
* @param printerPreset Optional BambuStudio printer preset name (e.g., "Bambu Lab P1S 0.4 nozzle")
|
|
991
|
+
* @param bambuOptions Optional BambuStudio-specific CLI flags
|
|
969
992
|
* @returns Path to the generated G-code or sliced 3MF file
|
|
970
993
|
*/
|
|
971
994
|
async sliceSTL(
|
|
@@ -974,7 +997,8 @@ export class STLManipulator extends EventEmitter {
|
|
|
974
997
|
slicerPath: string,
|
|
975
998
|
slicerProfile?: string,
|
|
976
999
|
progressCallback?: ProgressCallback,
|
|
977
|
-
printerPreset?: string
|
|
1000
|
+
printerPreset?: string,
|
|
1001
|
+
bambuOptions?: BambuSliceOptions
|
|
978
1002
|
): Promise<string> {
|
|
979
1003
|
const operationId = this.generateOperationId();
|
|
980
1004
|
this.activeOperations.set(operationId, true);
|
|
@@ -1051,7 +1075,7 @@ export class STLManipulator extends EventEmitter {
|
|
|
1051
1075
|
const outputBase = path.basename(stlFilePath, is3mf ? '.3mf' : '.stl');
|
|
1052
1076
|
const bambuOutputPath = path.join(this.tempDir, outputBase + '_sliced.3mf');
|
|
1053
1077
|
args = [
|
|
1054
|
-
'--slice',
|
|
1078
|
+
'--slice', String(bambuOptions?.slicePlate ?? 0),
|
|
1055
1079
|
'--export-3mf', bambuOutputPath,
|
|
1056
1080
|
];
|
|
1057
1081
|
if (slicerProfile) {
|
|
@@ -1068,6 +1092,26 @@ export class STLManipulator extends EventEmitter {
|
|
|
1068
1092
|
fs.writeFileSync(presetJson, JSON.stringify({ printer_settings_id: printerPreset }));
|
|
1069
1093
|
args.push('--load-settings', presetJson);
|
|
1070
1094
|
}
|
|
1095
|
+
|
|
1096
|
+
// BambuSliceOptions flags
|
|
1097
|
+
if (bambuOptions?.uptodate) args.push('--uptodate');
|
|
1098
|
+
if (bambuOptions?.ensureOnBed) args.push('--ensure-on-bed');
|
|
1099
|
+
if (bambuOptions?.enableTimelapse) args.push('--enable-timelapse');
|
|
1100
|
+
if (bambuOptions?.allowMixTemp) args.push('--allow-mix-temp');
|
|
1101
|
+
if (bambuOptions?.minSave) args.push('--min-save');
|
|
1102
|
+
if (bambuOptions?.skipModifiedGcodes) args.push('--skip-modified-gcodes');
|
|
1103
|
+
if (bambuOptions?.orient !== undefined) args.push('--orient', bambuOptions.orient ? '1' : '0');
|
|
1104
|
+
if (bambuOptions?.arrange !== undefined) args.push('--arrange', bambuOptions.arrange ? '1' : '0');
|
|
1105
|
+
if (bambuOptions?.repetitions !== undefined) args.push('--repetitions', String(bambuOptions.repetitions));
|
|
1106
|
+
if (bambuOptions?.scale !== undefined) args.push('--scale', String(bambuOptions.scale));
|
|
1107
|
+
if (bambuOptions?.rotate !== undefined) args.push('--rotate', String(bambuOptions.rotate));
|
|
1108
|
+
if (bambuOptions?.rotateX !== undefined) args.push('--rotate-x', String(bambuOptions.rotateX));
|
|
1109
|
+
if (bambuOptions?.rotateY !== undefined) args.push('--rotate-y', String(bambuOptions.rotateY));
|
|
1110
|
+
if (bambuOptions?.cloneObjects) args.push('--clone-objects', bambuOptions.cloneObjects);
|
|
1111
|
+
if (bambuOptions?.skipObjects) args.push('--skip-objects', bambuOptions.skipObjects);
|
|
1112
|
+
if (bambuOptions?.loadFilaments) args.push('--load-filaments', bambuOptions.loadFilaments);
|
|
1113
|
+
if (bambuOptions?.loadFilamentIds) args.push('--load-filament-ids', bambuOptions.loadFilamentIds);
|
|
1114
|
+
|
|
1071
1115
|
args.push(stlFilePath);
|
|
1072
1116
|
// Override outputFilePath for bambustudio since it produces 3MF, not gcode
|
|
1073
1117
|
outputFilePath = bambuOutputPath;
|