mcp-3d-printer-server 1.2.1 → 1.2.4
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 +257 -75
- package/dist/fulu-orca.d.ts +20 -0
- package/dist/fulu-orca.js +453 -0
- package/dist/index.js +572 -72
- package/dist/printers/prusa.d.ts +3 -0
- package/dist/printers/prusa.js +33 -1
- package/dist/stl/stl-manipulator.d.ts +11 -2
- package/dist/stl/stl-manipulator.js +162 -26
- package/package.json +23 -3
- package/src/fulu-orca.ts +602 -0
- package/src/index.ts +642 -72
- package/src/printers/prusa.ts +38 -1
- package/src/stl/stl-manipulator.ts +217 -29
- package/dist/http-server.d.ts +0 -16
- package/dist/http-server.js +0 -268
- package/dist/sse-server.d.ts +0 -20
- package/dist/sse-server.js +0 -318
- package/src/http-server.ts +0 -328
- package/src/sse-server.ts +0 -409
- package/src/test/Tape+Card+55mm+Ring.stl +0 -0
package/src/index.ts
CHANGED
|
@@ -22,6 +22,11 @@ import { PrinterFactory } from "./printers/printer-factory.js";
|
|
|
22
22
|
import { STLManipulator } from "./stl/stl-manipulator.js";
|
|
23
23
|
import { parse3MF, ThreeMFData } from './3mf_parser.js';
|
|
24
24
|
import { BambuImplementation } from "./printers/bambu.js";
|
|
25
|
+
import {
|
|
26
|
+
inspectFuluOrcaSetup,
|
|
27
|
+
invokeFuluBridgeRpc,
|
|
28
|
+
isFuluPrintRpcMethod,
|
|
29
|
+
} from "./fulu-orca.js";
|
|
25
30
|
|
|
26
31
|
// Load environment variables from .env file
|
|
27
32
|
dotenv.config();
|
|
@@ -34,9 +39,107 @@ const DEFAULT_TYPE = process.env.PRINTER_TYPE || "octoprint"; // Default to Octo
|
|
|
34
39
|
const TEMP_DIR = process.env.TEMP_DIR || path.join(process.cwd(), "temp");
|
|
35
40
|
|
|
36
41
|
// Slicer configuration
|
|
37
|
-
const
|
|
38
|
-
|
|
42
|
+
const CANONICAL_SLICER_TYPES = [
|
|
43
|
+
"prusaslicer",
|
|
44
|
+
"cura",
|
|
45
|
+
"slic3r",
|
|
46
|
+
"orcaslicer",
|
|
47
|
+
"orcaslicer-bambulab",
|
|
48
|
+
"bambustudio",
|
|
49
|
+
] as const;
|
|
50
|
+
type SlicerType = typeof CANONICAL_SLICER_TYPES[number];
|
|
51
|
+
|
|
52
|
+
const SLICER_TYPE_ALIASES: Record<string, SlicerType> = {
|
|
53
|
+
prusa: "prusaslicer",
|
|
54
|
+
prusaslicer: "prusaslicer",
|
|
55
|
+
cura: "cura",
|
|
56
|
+
curaengine: "cura",
|
|
57
|
+
slic3r: "slic3r",
|
|
58
|
+
orca: "orcaslicer",
|
|
59
|
+
orcaslicer: "orcaslicer",
|
|
60
|
+
"orca-slicer": "orcaslicer",
|
|
61
|
+
"orcaslicer-bambulab": "orcaslicer-bambulab",
|
|
62
|
+
"orcaslicer_bambulab": "orcaslicer-bambulab",
|
|
63
|
+
"orca-bambulab": "orcaslicer-bambulab",
|
|
64
|
+
"orca_bambulab": "orcaslicer-bambulab",
|
|
65
|
+
"fulu-orca": "orcaslicer-bambulab",
|
|
66
|
+
"fulu_orca": "orcaslicer-bambulab",
|
|
67
|
+
"orca-studio": "orcaslicer-bambulab",
|
|
68
|
+
"orca_studio": "orcaslicer-bambulab",
|
|
69
|
+
orcastudio: "orcaslicer-bambulab",
|
|
70
|
+
bambustudio: "bambustudio",
|
|
71
|
+
"bambu-studio": "bambustudio",
|
|
72
|
+
"bambu_studio": "bambustudio",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const SLICER_TYPE_ENUM = [
|
|
76
|
+
...CANONICAL_SLICER_TYPES,
|
|
77
|
+
"fulu_orca",
|
|
78
|
+
"fulu-orca",
|
|
79
|
+
"orca-studio",
|
|
80
|
+
"orca_bambulab",
|
|
81
|
+
] as const;
|
|
82
|
+
|
|
83
|
+
function normalizeSlicerType(rawValue: string | undefined): SlicerType {
|
|
84
|
+
const normalized = (rawValue || "").trim().toLowerCase();
|
|
85
|
+
const resolved = SLICER_TYPE_ALIASES[normalized];
|
|
86
|
+
if (!resolved) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Unsupported slicer type "${rawValue}". Valid types: ${SLICER_TYPE_ENUM.join(", ")}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return resolved;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isBambuProjectSlicer(slicerType: SlicerType): boolean {
|
|
95
|
+
return slicerType === "orcaslicer-bambulab" || slicerType === "bambustudio";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function firstExistingPath(candidates: string[]): string | undefined {
|
|
99
|
+
return candidates.find((candidate) => fs.existsSync(candidate));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function defaultFuluOrcaPath(): string {
|
|
103
|
+
return (
|
|
104
|
+
process.env.FULU_ORCA_PATH ||
|
|
105
|
+
process.env.ORCASLICER_BAMBULAB_PATH ||
|
|
106
|
+
process.env.ORCA_SLICER_BAMBULAB_PATH ||
|
|
107
|
+
firstExistingPath([
|
|
108
|
+
"/Applications/OrcaSlicer.app/Contents/MacOS/OrcaSlicer",
|
|
109
|
+
"/Applications/Orca Studio.app/Contents/MacOS/OrcaSlicer",
|
|
110
|
+
"/Applications/OrcaStudio.app/Contents/MacOS/OrcaStudio",
|
|
111
|
+
"/usr/local/bin/orcaslicer",
|
|
112
|
+
"/usr/bin/orcaslicer",
|
|
113
|
+
]) ||
|
|
114
|
+
"orcaslicer"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function defaultBambuStudioPath(): string {
|
|
119
|
+
return (
|
|
120
|
+
firstExistingPath([
|
|
121
|
+
"/Applications/BambuStudio.app/Contents/MacOS/BambuStudio",
|
|
122
|
+
"/Applications/Bambu Studio.app/Contents/MacOS/BambuStudio",
|
|
123
|
+
"/usr/local/bin/bambustudio",
|
|
124
|
+
"/usr/bin/bambustudio",
|
|
125
|
+
]) ||
|
|
126
|
+
"bambustudio"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const DEFAULT_SLICER_TYPE = normalizeSlicerType(
|
|
131
|
+
process.env.SLICER_TYPE || (DEFAULT_TYPE.toLowerCase() === "bambu" ? "orcaslicer-bambulab" : "prusaslicer")
|
|
132
|
+
);
|
|
133
|
+
const DEFAULT_SLICER_PATH =
|
|
134
|
+
process.env.SLICER_PATH ||
|
|
135
|
+
(DEFAULT_SLICER_TYPE === "orcaslicer-bambulab"
|
|
136
|
+
? defaultFuluOrcaPath()
|
|
137
|
+
: DEFAULT_SLICER_TYPE === "bambustudio"
|
|
138
|
+
? defaultBambuStudioPath()
|
|
139
|
+
: "");
|
|
39
140
|
const DEFAULT_SLICER_PROFILE = process.env.SLICER_PROFILE || "";
|
|
141
|
+
const DEFAULT_FILAMENT_PROFILE =
|
|
142
|
+
process.env.FILAMENT_PROFILE || process.env.SLICER_FILAMENT_PROFILE || "";
|
|
40
143
|
|
|
41
144
|
// Bambu-specific default values
|
|
42
145
|
const DEFAULT_BAMBU_SERIAL = process.env.BAMBU_SERIAL || "";
|
|
@@ -270,6 +373,58 @@ class ThreeDPrinterMCPServer {
|
|
|
270
373
|
this.setupToolHandlers();
|
|
271
374
|
}
|
|
272
375
|
|
|
376
|
+
private extractParsedAmsMapping(parsed3MFData: ThreeMFData | undefined): number[] | undefined {
|
|
377
|
+
if (!parsed3MFData?.slicerConfig?.ams_mapping) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const slots = Object.values(parsed3MFData.slicerConfig.ams_mapping).filter(
|
|
382
|
+
(value): value is number => typeof value === "number"
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
return slots.length > 0 ? slots.sort((a, b) => a - b) : undefined;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private resolveAmsPrintOptions(
|
|
389
|
+
parsed3MFData: ThreeMFData | undefined,
|
|
390
|
+
args: Record<string, unknown> | undefined
|
|
391
|
+
): { useAMS: boolean; amsMapping?: number[] } {
|
|
392
|
+
let finalAmsMapping = this.extractParsedAmsMapping(parsed3MFData);
|
|
393
|
+
let useAMS =
|
|
394
|
+
args?.use_ams !== undefined
|
|
395
|
+
? Boolean(args.use_ams)
|
|
396
|
+
: !!finalAmsMapping && finalAmsMapping.length > 0;
|
|
397
|
+
|
|
398
|
+
const override = args?.ams_mapping;
|
|
399
|
+
if (override !== undefined) {
|
|
400
|
+
let userMappingOverride: number[] | undefined;
|
|
401
|
+
|
|
402
|
+
if (Array.isArray(override)) {
|
|
403
|
+
userMappingOverride = override.filter((value): value is number => typeof value === "number");
|
|
404
|
+
} else if (typeof override === "object" && override !== null) {
|
|
405
|
+
userMappingOverride = Object.values(override as Record<string, unknown>)
|
|
406
|
+
.filter((value): value is number => typeof value === "number")
|
|
407
|
+
.sort((a, b) => a - b);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (userMappingOverride && userMappingOverride.length > 0) {
|
|
411
|
+
finalAmsMapping = userMappingOverride;
|
|
412
|
+
useAMS = true;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (args?.use_ams === false) {
|
|
417
|
+
finalAmsMapping = undefined;
|
|
418
|
+
useAMS = false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (!finalAmsMapping || finalAmsMapping.length === 0) {
|
|
422
|
+
useAMS = false;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return { useAMS, amsMapping: finalAmsMapping };
|
|
426
|
+
}
|
|
427
|
+
|
|
273
428
|
setupResourceHandlers() {
|
|
274
429
|
// List available resources
|
|
275
430
|
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
@@ -391,6 +546,205 @@ class ThreeDPrinterMCPServer {
|
|
|
391
546
|
}
|
|
392
547
|
}
|
|
393
548
|
},
|
|
549
|
+
{
|
|
550
|
+
name: "list_printer_files",
|
|
551
|
+
description: "List files available on the 3D printer",
|
|
552
|
+
inputSchema: {
|
|
553
|
+
type: "object",
|
|
554
|
+
properties: {
|
|
555
|
+
host: {
|
|
556
|
+
type: "string",
|
|
557
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
558
|
+
},
|
|
559
|
+
port: {
|
|
560
|
+
type: "string",
|
|
561
|
+
description: "Port of the printer API (default: value from env)"
|
|
562
|
+
},
|
|
563
|
+
type: {
|
|
564
|
+
type: "string",
|
|
565
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
566
|
+
},
|
|
567
|
+
api_key: {
|
|
568
|
+
type: "string",
|
|
569
|
+
description: "API key for authentication (default: value from env)"
|
|
570
|
+
},
|
|
571
|
+
bambu_serial: {
|
|
572
|
+
type: "string",
|
|
573
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
574
|
+
},
|
|
575
|
+
bambu_token: {
|
|
576
|
+
type: "string",
|
|
577
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
name: "upload_gcode",
|
|
584
|
+
description: "Upload G-code content or a local G-code file path to the printer",
|
|
585
|
+
inputSchema: {
|
|
586
|
+
type: "object",
|
|
587
|
+
properties: {
|
|
588
|
+
host: {
|
|
589
|
+
type: "string",
|
|
590
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
591
|
+
},
|
|
592
|
+
port: {
|
|
593
|
+
type: "string",
|
|
594
|
+
description: "Port of the printer API (default: value from env)"
|
|
595
|
+
},
|
|
596
|
+
type: {
|
|
597
|
+
type: "string",
|
|
598
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
599
|
+
},
|
|
600
|
+
api_key: {
|
|
601
|
+
type: "string",
|
|
602
|
+
description: "API key for authentication (default: value from env)"
|
|
603
|
+
},
|
|
604
|
+
bambu_serial: {
|
|
605
|
+
type: "string",
|
|
606
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
607
|
+
},
|
|
608
|
+
bambu_token: {
|
|
609
|
+
type: "string",
|
|
610
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
611
|
+
},
|
|
612
|
+
filename: {
|
|
613
|
+
type: "string",
|
|
614
|
+
description: "Filename to use on the printer. Defaults to the basename of gcode_path when omitted."
|
|
615
|
+
},
|
|
616
|
+
gcode: {
|
|
617
|
+
type: "string",
|
|
618
|
+
description: "G-code content, or a local path to a G-code file."
|
|
619
|
+
},
|
|
620
|
+
gcode_path: {
|
|
621
|
+
type: "string",
|
|
622
|
+
description: "Local path to a G-code file to upload."
|
|
623
|
+
},
|
|
624
|
+
print: {
|
|
625
|
+
type: "boolean",
|
|
626
|
+
description: "Start printing after upload when the printer backend supports it."
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
anyOf: [
|
|
630
|
+
{ required: ["filename", "gcode"] },
|
|
631
|
+
{ required: ["gcode_path"] }
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
name: "start_print",
|
|
637
|
+
description: "Start printing a file that is already available on the printer",
|
|
638
|
+
inputSchema: {
|
|
639
|
+
type: "object",
|
|
640
|
+
properties: {
|
|
641
|
+
host: {
|
|
642
|
+
type: "string",
|
|
643
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
644
|
+
},
|
|
645
|
+
port: {
|
|
646
|
+
type: "string",
|
|
647
|
+
description: "Port of the printer API (default: value from env)"
|
|
648
|
+
},
|
|
649
|
+
type: {
|
|
650
|
+
type: "string",
|
|
651
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
652
|
+
},
|
|
653
|
+
api_key: {
|
|
654
|
+
type: "string",
|
|
655
|
+
description: "API key for authentication (default: value from env)"
|
|
656
|
+
},
|
|
657
|
+
bambu_serial: {
|
|
658
|
+
type: "string",
|
|
659
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
660
|
+
},
|
|
661
|
+
bambu_token: {
|
|
662
|
+
type: "string",
|
|
663
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
664
|
+
},
|
|
665
|
+
filename: {
|
|
666
|
+
type: "string",
|
|
667
|
+
description: "Name/path of the printer-side G-code file to start."
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
required: ["filename"]
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: "cancel_print",
|
|
675
|
+
description: "Cancel the current print job",
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: "object",
|
|
678
|
+
properties: {
|
|
679
|
+
host: {
|
|
680
|
+
type: "string",
|
|
681
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
682
|
+
},
|
|
683
|
+
port: {
|
|
684
|
+
type: "string",
|
|
685
|
+
description: "Port of the printer API (default: value from env)"
|
|
686
|
+
},
|
|
687
|
+
type: {
|
|
688
|
+
type: "string",
|
|
689
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
690
|
+
},
|
|
691
|
+
api_key: {
|
|
692
|
+
type: "string",
|
|
693
|
+
description: "API key for authentication (default: value from env)"
|
|
694
|
+
},
|
|
695
|
+
bambu_serial: {
|
|
696
|
+
type: "string",
|
|
697
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
698
|
+
},
|
|
699
|
+
bambu_token: {
|
|
700
|
+
type: "string",
|
|
701
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
name: "set_printer_temperature",
|
|
708
|
+
description: "Set the temperature of a printer component",
|
|
709
|
+
inputSchema: {
|
|
710
|
+
type: "object",
|
|
711
|
+
properties: {
|
|
712
|
+
host: {
|
|
713
|
+
type: "string",
|
|
714
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
715
|
+
},
|
|
716
|
+
port: {
|
|
717
|
+
type: "string",
|
|
718
|
+
description: "Port of the printer API (default: value from env)"
|
|
719
|
+
},
|
|
720
|
+
type: {
|
|
721
|
+
type: "string",
|
|
722
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
723
|
+
},
|
|
724
|
+
api_key: {
|
|
725
|
+
type: "string",
|
|
726
|
+
description: "API key for authentication (default: value from env)"
|
|
727
|
+
},
|
|
728
|
+
bambu_serial: {
|
|
729
|
+
type: "string",
|
|
730
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
731
|
+
},
|
|
732
|
+
bambu_token: {
|
|
733
|
+
type: "string",
|
|
734
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
735
|
+
},
|
|
736
|
+
component: {
|
|
737
|
+
type: "string",
|
|
738
|
+
description: "Printer component to heat, such as extruder or bed."
|
|
739
|
+
},
|
|
740
|
+
temperature: {
|
|
741
|
+
type: "number",
|
|
742
|
+
description: "Target temperature in Celsius."
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
required: ["component", "temperature"]
|
|
746
|
+
}
|
|
747
|
+
},
|
|
394
748
|
// New STL manipulation tools
|
|
395
749
|
{
|
|
396
750
|
name: "extend_stl_base",
|
|
@@ -423,7 +777,7 @@ class ThreeDPrinterMCPServer {
|
|
|
423
777
|
bambu_model: {
|
|
424
778
|
type: "string",
|
|
425
779
|
enum: ["p1s", "p1p", "x1c", "x1e", "a1", "a1mini", "h2d"],
|
|
426
|
-
description: "Bambu Lab printer model
|
|
780
|
+
description: "Bambu Lab printer model - required when using Bambu project slicers to ensure correct G-code."
|
|
427
781
|
},
|
|
428
782
|
nozzle_diameter: {
|
|
429
783
|
type: "string",
|
|
@@ -431,7 +785,8 @@ class ThreeDPrinterMCPServer {
|
|
|
431
785
|
},
|
|
432
786
|
slicer_type: {
|
|
433
787
|
type: "string",
|
|
434
|
-
|
|
788
|
+
enum: [...SLICER_TYPE_ENUM],
|
|
789
|
+
description: "Type of slicer to use (prusaslicer, cura, slic3r, orcaslicer, orcaslicer-bambulab, bambustudio). Use orcaslicer-bambulab for the FULU fork."
|
|
435
790
|
},
|
|
436
791
|
slicer_path: {
|
|
437
792
|
type: "string",
|
|
@@ -439,7 +794,11 @@ class ThreeDPrinterMCPServer {
|
|
|
439
794
|
},
|
|
440
795
|
slicer_profile: {
|
|
441
796
|
type: "string",
|
|
442
|
-
description: "Profile to use for slicing (default: value from env)"
|
|
797
|
+
description: "Profile to use for slicing (default: value from env). OrcaSlicer also accepts machine/process profiles separated with ';', optionally followed by '|filament.json'."
|
|
798
|
+
},
|
|
799
|
+
filament_profile: {
|
|
800
|
+
type: "string",
|
|
801
|
+
description: "OrcaSlicer filament profile path loaded with --load-filaments (default: FILAMENT_PROFILE/SLICER_FILAMENT_PROFILE env)."
|
|
443
802
|
}
|
|
444
803
|
},
|
|
445
804
|
required: ["stl_path"]
|
|
@@ -504,6 +863,37 @@ class ThreeDPrinterMCPServer {
|
|
|
504
863
|
api_key: {
|
|
505
864
|
type: "string",
|
|
506
865
|
description: "API key for authentication (default: value from env)"
|
|
866
|
+
},
|
|
867
|
+
bambu_model: {
|
|
868
|
+
type: "string",
|
|
869
|
+
enum: ["p1s", "p1p", "x1c", "x1e", "a1", "a1mini", "h2d"],
|
|
870
|
+
description: "Bambu Lab printer model. Required for Bambu print operations."
|
|
871
|
+
},
|
|
872
|
+
bed_type: {
|
|
873
|
+
type: "string",
|
|
874
|
+
enum: ["textured_plate", "cool_plate", "engineering_plate", "hot_plate"],
|
|
875
|
+
description: "Bed/plate type installed on the printer (default: textured_plate)."
|
|
876
|
+
},
|
|
877
|
+
nozzle_diameter: {
|
|
878
|
+
type: "string",
|
|
879
|
+
description: "Nozzle diameter in mm (default: 0.4)."
|
|
880
|
+
},
|
|
881
|
+
slicer_type: {
|
|
882
|
+
type: "string",
|
|
883
|
+
enum: [...SLICER_TYPE_ENUM],
|
|
884
|
+
description: "Type of slicer to use. Use orcaslicer-bambulab for FULU OrcaSlicer-bambulab."
|
|
885
|
+
},
|
|
886
|
+
slicer_path: {
|
|
887
|
+
type: "string",
|
|
888
|
+
description: "Path to the slicer executable (default: value from env)."
|
|
889
|
+
},
|
|
890
|
+
slicer_profile: {
|
|
891
|
+
type: "string",
|
|
892
|
+
description: "Profile to use for slicing (default: value from env). OrcaSlicer also accepts machine/process profiles separated with ';', optionally followed by '|filament.json'."
|
|
893
|
+
},
|
|
894
|
+
filament_profile: {
|
|
895
|
+
type: "string",
|
|
896
|
+
description: "OrcaSlicer filament profile path loaded with --load-filaments (default: FILAMENT_PROFILE/SLICER_FILAMENT_PROFILE env)."
|
|
507
897
|
}
|
|
508
898
|
},
|
|
509
899
|
required: ["stl_path", "extension_inches"]
|
|
@@ -732,11 +1122,128 @@ class ThreeDPrinterMCPServer {
|
|
|
732
1122
|
type: "object",
|
|
733
1123
|
description: "Override AMS filament mapping (e.g., {\"Generic PLA\": 0, \"Generic PETG\": 1}).",
|
|
734
1124
|
additionalProperties: { type: "number" }
|
|
1125
|
+
},
|
|
1126
|
+
slicer_type: {
|
|
1127
|
+
type: "string",
|
|
1128
|
+
enum: [...SLICER_TYPE_ENUM],
|
|
1129
|
+
description: "Slicer to use if the 3MF needs auto-slicing. Use orcaslicer-bambulab for FULU OrcaSlicer-bambulab."
|
|
1130
|
+
},
|
|
1131
|
+
slicer_path: {
|
|
1132
|
+
type: "string",
|
|
1133
|
+
description: "Path to the slicer executable if auto-slicing is needed."
|
|
1134
|
+
},
|
|
1135
|
+
slicer_profile: {
|
|
1136
|
+
type: "string",
|
|
1137
|
+
description: "Optional slicer settings/profile path for auto-slicing."
|
|
1138
|
+
},
|
|
1139
|
+
filament_profile: {
|
|
1140
|
+
type: "string",
|
|
1141
|
+
description: "Optional filament profile path for auto-slicing with OrcaSlicer/Bambu Studio."
|
|
1142
|
+
},
|
|
1143
|
+
use_ams: {
|
|
1144
|
+
type: "boolean",
|
|
1145
|
+
description: "Whether to use AMS for the print. Defaults from parsed 3MF mapping when present."
|
|
1146
|
+
},
|
|
1147
|
+
bed_leveling: {
|
|
1148
|
+
type: "boolean",
|
|
1149
|
+
description: "Override bed leveling flag for the Bambu print command."
|
|
1150
|
+
},
|
|
1151
|
+
flow_calibration: {
|
|
1152
|
+
type: "boolean",
|
|
1153
|
+
description: "Override flow calibration flag for the Bambu print command."
|
|
1154
|
+
},
|
|
1155
|
+
vibration_calibration: {
|
|
1156
|
+
type: "boolean",
|
|
1157
|
+
description: "Override vibration calibration flag for the Bambu print command."
|
|
1158
|
+
},
|
|
1159
|
+
layer_inspect: {
|
|
1160
|
+
type: "boolean",
|
|
1161
|
+
description: "Override layer inspection flag for the Bambu print command."
|
|
1162
|
+
},
|
|
1163
|
+
timelapse: {
|
|
1164
|
+
type: "boolean",
|
|
1165
|
+
description: "Override timelapse flag for the Bambu print command."
|
|
735
1166
|
}
|
|
736
1167
|
},
|
|
737
1168
|
required: ["three_mf_path", "bambu_model"]
|
|
738
1169
|
}
|
|
739
1170
|
},
|
|
1171
|
+
{
|
|
1172
|
+
name: "check_fulu_orca_setup",
|
|
1173
|
+
description:
|
|
1174
|
+
"Inspect a FULU OrcaSlicer-bambulab install, platform runtime payload, setup commands, and optionally probe the BambuNetwork bridge.",
|
|
1175
|
+
inputSchema: {
|
|
1176
|
+
type: "object",
|
|
1177
|
+
properties: {
|
|
1178
|
+
slicer_path: {
|
|
1179
|
+
type: "string",
|
|
1180
|
+
description: "Path to the FULU OrcaSlicer executable. Defaults from SLICER_PATH/FULU_ORCA_PATH."
|
|
1181
|
+
},
|
|
1182
|
+
plugin_dir: {
|
|
1183
|
+
type: "string",
|
|
1184
|
+
description: "Directory containing the FULU Bambu runtime payload; on macOS this is usually OrcaSlicer.app/Contents/MacOS."
|
|
1185
|
+
},
|
|
1186
|
+
runtime_dir: {
|
|
1187
|
+
type: "string",
|
|
1188
|
+
description: "Installed runtime directory. On macOS this defaults to ~/Library/Application Support/OrcaSlicer/macos-bridge/runtime."
|
|
1189
|
+
},
|
|
1190
|
+
platform: {
|
|
1191
|
+
type: "string",
|
|
1192
|
+
enum: ["darwin", "macos", "win32", "windows", "linux"],
|
|
1193
|
+
description: "Platform to inspect. Defaults to the current Node.js platform."
|
|
1194
|
+
},
|
|
1195
|
+
bridge_command: {
|
|
1196
|
+
type: "string",
|
|
1197
|
+
description: "Command that starts the FULU BambuNetwork bridge host for probing."
|
|
1198
|
+
},
|
|
1199
|
+
run_bridge_probe: {
|
|
1200
|
+
type: "boolean",
|
|
1201
|
+
description: "When true, sends bridge.handshake, bridge.capabilities, and bridge.runtime_info to the bridge host."
|
|
1202
|
+
},
|
|
1203
|
+
probe_timeout_ms: {
|
|
1204
|
+
type: "number",
|
|
1205
|
+
description: "Bridge probe timeout in milliseconds (default: 5000)."
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
name: "fulu_bambu_network_rpc",
|
|
1212
|
+
description:
|
|
1213
|
+
"Advanced FULU bridge RPC for BambuNetwork diagnostics and development. Read-only methods are allowed by default; mutating methods require allow_mutating_method=true, and print methods require bambu_model.",
|
|
1214
|
+
inputSchema: {
|
|
1215
|
+
type: "object",
|
|
1216
|
+
properties: {
|
|
1217
|
+
bridge_command: {
|
|
1218
|
+
type: "string",
|
|
1219
|
+
description: "Command that starts the FULU BambuNetwork bridge host. Defaults to FULU_BAMBU_BRIDGE_COMMAND."
|
|
1220
|
+
},
|
|
1221
|
+
method: {
|
|
1222
|
+
type: "string",
|
|
1223
|
+
description: "FULU bridge method, e.g. bridge.handshake, bridge.runtime_info, net.get_user_print_info, net.start_print."
|
|
1224
|
+
},
|
|
1225
|
+
payload: {
|
|
1226
|
+
type: "object",
|
|
1227
|
+
description: "JSON payload sent to the FULU bridge method.",
|
|
1228
|
+
additionalProperties: true
|
|
1229
|
+
},
|
|
1230
|
+
timeout_ms: {
|
|
1231
|
+
type: "number",
|
|
1232
|
+
description: "Bridge request timeout in milliseconds (default: 5000)."
|
|
1233
|
+
},
|
|
1234
|
+
allow_mutating_method: {
|
|
1235
|
+
type: "boolean",
|
|
1236
|
+
description: "Required for methods outside the read-only bridge/net allowlist."
|
|
1237
|
+
},
|
|
1238
|
+
bambu_model: {
|
|
1239
|
+
type: "string",
|
|
1240
|
+
enum: ["p1s", "p1p", "x1c", "x1e", "a1", "a1mini", "h2d"],
|
|
1241
|
+
description: "Required when calling FULU print RPC methods; preserves the Bambu model safety gate."
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
required: ["method"]
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
740
1247
|
{
|
|
741
1248
|
name: "merge_vertices",
|
|
742
1249
|
description: "Merge vertices in an STL file that are closer than the specified tolerance.",
|
|
@@ -825,9 +1332,10 @@ class ThreeDPrinterMCPServer {
|
|
|
825
1332
|
const apiKey = String(args?.api_key || DEFAULT_API_KEY);
|
|
826
1333
|
const bambuSerial = String(args?.bambu_serial || DEFAULT_BAMBU_SERIAL);
|
|
827
1334
|
const bambuToken = String(args?.bambu_token || DEFAULT_BAMBU_TOKEN);
|
|
828
|
-
const slicerType = String(args?.slicer_type || DEFAULT_SLICER_TYPE)
|
|
1335
|
+
const slicerType = normalizeSlicerType(String(args?.slicer_type || DEFAULT_SLICER_TYPE));
|
|
829
1336
|
const slicerPath = String(args?.slicer_path || DEFAULT_SLICER_PATH);
|
|
830
1337
|
const slicerProfile = String(args?.slicer_profile || DEFAULT_SLICER_PROFILE);
|
|
1338
|
+
const filamentProfile = String(args?.filament_profile || DEFAULT_FILAMENT_PROFILE);
|
|
831
1339
|
|
|
832
1340
|
try {
|
|
833
1341
|
let result;
|
|
@@ -842,13 +1350,21 @@ class ThreeDPrinterMCPServer {
|
|
|
842
1350
|
break;
|
|
843
1351
|
|
|
844
1352
|
case "upload_gcode":
|
|
845
|
-
if (!args?.
|
|
846
|
-
throw new Error("Missing required
|
|
1353
|
+
if (!args?.gcode && !args?.gcode_path) {
|
|
1354
|
+
throw new Error("Missing required parameter: gcode or gcode_path");
|
|
1355
|
+
}
|
|
1356
|
+
if (args?.gcode_path && !fs.existsSync(String(args.gcode_path))) {
|
|
1357
|
+
throw new Error(`G-code file not found: ${String(args.gcode_path)}`);
|
|
1358
|
+
}
|
|
1359
|
+
if (!args?.filename && !args?.gcode_path) {
|
|
1360
|
+
throw new Error("Missing required parameter: filename");
|
|
847
1361
|
}
|
|
1362
|
+
const uploadFilename = String(args?.filename || path.basename(String(args.gcode_path)));
|
|
1363
|
+
const uploadGcodeSource = String(args?.gcode_path || args.gcode);
|
|
848
1364
|
result = await this.uploadGcode(
|
|
849
1365
|
host, port, type, apiKey, bambuSerial, bambuToken,
|
|
850
|
-
|
|
851
|
-
|
|
1366
|
+
uploadFilename,
|
|
1367
|
+
uploadGcodeSource,
|
|
852
1368
|
Boolean(args.print || false)
|
|
853
1369
|
);
|
|
854
1370
|
break;
|
|
@@ -892,7 +1408,7 @@ class ThreeDPrinterMCPServer {
|
|
|
892
1408
|
}
|
|
893
1409
|
// Resolve printer model for BambuStudio slicer
|
|
894
1410
|
let slicePreset: string | undefined;
|
|
895
|
-
if (slicerType
|
|
1411
|
+
if (isBambuProjectSlicer(slicerType)) {
|
|
896
1412
|
const sliceModel = await this.resolveBambuModel(args?.bambu_model as string | undefined);
|
|
897
1413
|
const nozzleDiam = String(args?.nozzle_diameter || DEFAULT_NOZZLE_DIAMETER);
|
|
898
1414
|
slicePreset = BAMBU_MODEL_PRESETS[sliceModel]?.(nozzleDiam);
|
|
@@ -903,7 +1419,8 @@ class ThreeDPrinterMCPServer {
|
|
|
903
1419
|
slicerPath,
|
|
904
1420
|
slicerProfile || undefined,
|
|
905
1421
|
undefined, // progressCallback
|
|
906
|
-
slicePreset
|
|
1422
|
+
slicePreset,
|
|
1423
|
+
filamentProfile || undefined
|
|
907
1424
|
);
|
|
908
1425
|
break;
|
|
909
1426
|
}
|
|
@@ -940,10 +1457,12 @@ class ThreeDPrinterMCPServer {
|
|
|
940
1457
|
|
|
941
1458
|
// 2. Slice the extended STL file
|
|
942
1459
|
let processPreset: string | undefined;
|
|
943
|
-
if (
|
|
1460
|
+
if (type.toLowerCase() === 'bambu' || isBambuProjectSlicer(slicerType)) {
|
|
944
1461
|
const processModel = await this.resolveBambuModel(args?.bambu_model as string | undefined);
|
|
945
1462
|
const processNozzle = String(args?.nozzle_diameter || DEFAULT_NOZZLE_DIAMETER);
|
|
946
|
-
|
|
1463
|
+
if (isBambuProjectSlicer(slicerType)) {
|
|
1464
|
+
processPreset = BAMBU_MODEL_PRESETS[processModel]?.(processNozzle);
|
|
1465
|
+
}
|
|
947
1466
|
}
|
|
948
1467
|
const gcodePath = await this.stlManipulator.sliceSTL(
|
|
949
1468
|
extendedStlPath,
|
|
@@ -951,8 +1470,59 @@ class ThreeDPrinterMCPServer {
|
|
|
951
1470
|
slicerPath,
|
|
952
1471
|
slicerProfile || undefined,
|
|
953
1472
|
processProgressCallback,
|
|
954
|
-
processPreset
|
|
1473
|
+
processPreset,
|
|
1474
|
+
filamentProfile || undefined
|
|
955
1475
|
);
|
|
1476
|
+
|
|
1477
|
+
if (type.toLowerCase() === 'bambu' && gcodePath.toLowerCase().endsWith(".3mf")) {
|
|
1478
|
+
if (!bambuSerial || !bambuToken) {
|
|
1479
|
+
throw new Error("Bambu serial number and access token are required for Bambu 3MF printing.");
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const factoryImplementation = this.printerFactory.getImplementation('bambu');
|
|
1483
|
+
if (!(factoryImplementation instanceof BambuImplementation)) {
|
|
1484
|
+
throw new Error("Internal error: Could not get Bambu printer implementation.");
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
const printBedType = resolveBedType(args?.bed_type as string | undefined);
|
|
1488
|
+
let parsedProcess3MF: ThreeMFData | undefined;
|
|
1489
|
+
try {
|
|
1490
|
+
parsedProcess3MF = await parse3MF(gcodePath);
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
console.warn(
|
|
1493
|
+
`Could not parse AMS metadata from sliced 3MF ${gcodePath}:`,
|
|
1494
|
+
(error as Error).message
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
const amsOptions = this.resolveAmsPrintOptions(
|
|
1498
|
+
parsedProcess3MF,
|
|
1499
|
+
args as Record<string, unknown> | undefined
|
|
1500
|
+
);
|
|
1501
|
+
|
|
1502
|
+
result = await factoryImplementation.print3mf(host, bambuSerial, bambuToken, {
|
|
1503
|
+
projectName: path.basename(gcodePath).replace(/\.3mf$/i, ''),
|
|
1504
|
+
filePath: gcodePath,
|
|
1505
|
+
plateIndex: 0,
|
|
1506
|
+
...amsOptions,
|
|
1507
|
+
bedType: printBedType,
|
|
1508
|
+
bedLeveling: args?.bed_leveling !== undefined ? Boolean(args.bed_leveling) : undefined,
|
|
1509
|
+
flowCalibration:
|
|
1510
|
+
args?.flow_calibration !== undefined ? Boolean(args.flow_calibration) : undefined,
|
|
1511
|
+
vibrationCalibration:
|
|
1512
|
+
args?.vibration_calibration !== undefined
|
|
1513
|
+
? Boolean(args.vibration_calibration)
|
|
1514
|
+
: undefined,
|
|
1515
|
+
layerInspect: args?.layer_inspect !== undefined ? Boolean(args.layer_inspect) : undefined,
|
|
1516
|
+
timelapse: args?.timelapse !== undefined ? Boolean(args.timelapse) : undefined,
|
|
1517
|
+
});
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
if (gcodePath.toLowerCase().endsWith(".3mf")) {
|
|
1522
|
+
throw new Error(
|
|
1523
|
+
"process_and_print_stl produced a sliced 3MF. Direct process-and-print for sliced 3MF is only supported for Bambu printers via print_3mf."
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
956
1526
|
|
|
957
1527
|
// 3. Confirm temperatures if specified
|
|
958
1528
|
if (args.extruder_temp !== undefined || args.bed_temp !== undefined) {
|
|
@@ -1211,7 +1781,8 @@ class ThreeDPrinterMCPServer {
|
|
|
1211
1781
|
slicerPath,
|
|
1212
1782
|
slicerProfile || undefined,
|
|
1213
1783
|
undefined, // progressCallback
|
|
1214
|
-
printPreset
|
|
1784
|
+
printPreset,
|
|
1785
|
+
filamentProfile || undefined
|
|
1215
1786
|
);
|
|
1216
1787
|
console.log("Auto-sliced to: " + threeMFPath);
|
|
1217
1788
|
}
|
|
@@ -1223,60 +1794,16 @@ class ThreeDPrinterMCPServer {
|
|
|
1223
1794
|
let implementation: BambuImplementation;
|
|
1224
1795
|
let threeMfFilename: string;
|
|
1225
1796
|
let projectName: string;
|
|
1226
|
-
let finalAmsMapping: number[] | undefined;
|
|
1227
|
-
let useAMS: boolean;
|
|
1228
1797
|
let printOptions: any; // Use a more specific type later if possible
|
|
1229
1798
|
|
|
1230
1799
|
try {
|
|
1231
1800
|
// --- Parse 3MF ---
|
|
1232
1801
|
const parsed3MFData = await parse3MF(threeMFPath);
|
|
1233
1802
|
console.log(`Successfully parsed 3MF file: ${threeMFPath}`);
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
.filter(v => typeof v === 'number') as number[];
|
|
1239
|
-
if (slots.length > 0) {
|
|
1240
|
-
parsedAmsMapping = slots.sort((a, b) => a - b);
|
|
1241
|
-
console.log("Extracted default AMS mapping from 3MF:", parsedAmsMapping);
|
|
1242
|
-
} else {
|
|
1243
|
-
console.log("AMS mapping found in 3MF, but no valid slots extracted.");
|
|
1244
|
-
}
|
|
1245
|
-
} else {
|
|
1246
|
-
console.log("No default AMS mapping found in 3MF slicer config.");
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
// --- Gather Overrides and Determine Final Options ---
|
|
1250
|
-
finalAmsMapping = parsedAmsMapping; // Start with parsed
|
|
1251
|
-
useAMS = args?.use_ams !== undefined ? Boolean(args.use_ams) : (!!finalAmsMapping && finalAmsMapping.length > 0);
|
|
1252
|
-
// ... (Process user AMS mapping override logic) ...
|
|
1253
|
-
if (args?.ams_mapping) {
|
|
1254
|
-
let userMappingOverride: number[] | undefined = undefined;
|
|
1255
|
-
if (Array.isArray(args.ams_mapping)) {
|
|
1256
|
-
userMappingOverride = args.ams_mapping.filter(v => typeof v === 'number');
|
|
1257
|
-
} else if (typeof args.ams_mapping === 'object') {
|
|
1258
|
-
userMappingOverride = Object.values(args.ams_mapping)
|
|
1259
|
-
.filter(v => typeof v === 'number')
|
|
1260
|
-
.sort((a, b) => a - b) as number[];
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
if (userMappingOverride && userMappingOverride.length > 0) {
|
|
1264
|
-
console.log("Applying user AMS mapping override:", userMappingOverride);
|
|
1265
|
-
finalAmsMapping = userMappingOverride;
|
|
1266
|
-
useAMS = true; // Force useAMS if override provided
|
|
1267
|
-
} else {
|
|
1268
|
-
console.warn("Received ams_mapping override, but it was empty or invalid.");
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
// ... (Handle explicit use_ams=false) ...
|
|
1272
|
-
if (args?.use_ams === false) {
|
|
1273
|
-
console.log("User explicitly disabled AMS.");
|
|
1274
|
-
finalAmsMapping = undefined;
|
|
1275
|
-
useAMS = false;
|
|
1276
|
-
}
|
|
1277
|
-
if (!finalAmsMapping || finalAmsMapping.length === 0) {
|
|
1278
|
-
useAMS = false;
|
|
1279
|
-
}
|
|
1803
|
+
const amsOptions = this.resolveAmsPrintOptions(
|
|
1804
|
+
parsed3MFData,
|
|
1805
|
+
args as Record<string, unknown> | undefined
|
|
1806
|
+
);
|
|
1280
1807
|
|
|
1281
1808
|
// --- Prepare Implementation and Print Options ---
|
|
1282
1809
|
const factoryImplementation = this.printerFactory.getImplementation('bambu');
|
|
@@ -1289,8 +1816,7 @@ class ThreeDPrinterMCPServer {
|
|
|
1289
1816
|
projectName = threeMfFilename.replace(/\.3mf$/i, ''); // Assign to outer scope variable
|
|
1290
1817
|
|
|
1291
1818
|
printOptions = { // Assign to outer scope variable
|
|
1292
|
-
|
|
1293
|
-
amsMapping: finalAmsMapping,
|
|
1819
|
+
...amsOptions,
|
|
1294
1820
|
bedType: printBedType,
|
|
1295
1821
|
bedLeveling: args?.bed_leveling !== undefined ? Boolean(args.bed_leveling) : undefined,
|
|
1296
1822
|
flowCalibration: args?.flow_calibration !== undefined ? Boolean(args.flow_calibration) : undefined,
|
|
@@ -1322,6 +1848,48 @@ class ThreeDPrinterMCPServer {
|
|
|
1322
1848
|
break;
|
|
1323
1849
|
}
|
|
1324
1850
|
|
|
1851
|
+
case "check_fulu_orca_setup": {
|
|
1852
|
+
result = await inspectFuluOrcaSetup({
|
|
1853
|
+
slicerPath: args?.slicer_path !== undefined ? String(args.slicer_path) : undefined,
|
|
1854
|
+
pluginDir: args?.plugin_dir !== undefined ? String(args.plugin_dir) : undefined,
|
|
1855
|
+
runtimeDir: args?.runtime_dir !== undefined ? String(args.runtime_dir) : undefined,
|
|
1856
|
+
platform: args?.platform !== undefined ? String(args.platform) : undefined,
|
|
1857
|
+
bridgeCommand: args?.bridge_command !== undefined ? String(args.bridge_command) : undefined,
|
|
1858
|
+
runBridgeProbe: Boolean(args?.run_bridge_probe ?? false),
|
|
1859
|
+
probeTimeoutMs:
|
|
1860
|
+
args?.probe_timeout_ms !== undefined ? Number(args.probe_timeout_ms) : undefined,
|
|
1861
|
+
});
|
|
1862
|
+
break;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
case "fulu_bambu_network_rpc": {
|
|
1866
|
+
if (!args?.method) {
|
|
1867
|
+
throw new Error("Missing required parameter: method");
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
const method = String(args.method);
|
|
1871
|
+
let rpcBambuModel: string | undefined;
|
|
1872
|
+
if (isFuluPrintRpcMethod(method)) {
|
|
1873
|
+
rpcBambuModel = await this.resolveBambuModel(args?.bambu_model as string | undefined);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
const payload =
|
|
1877
|
+
typeof args.payload === "object" && args.payload !== null && !Array.isArray(args.payload)
|
|
1878
|
+
? (args.payload as Record<string, unknown>)
|
|
1879
|
+
: undefined;
|
|
1880
|
+
|
|
1881
|
+
result = await invokeFuluBridgeRpc({
|
|
1882
|
+
bridgeCommand:
|
|
1883
|
+
args?.bridge_command !== undefined ? String(args.bridge_command) : undefined,
|
|
1884
|
+
method,
|
|
1885
|
+
payload,
|
|
1886
|
+
timeoutMs: args?.timeout_ms !== undefined ? Number(args.timeout_ms) : undefined,
|
|
1887
|
+
allowMutatingMethod: Boolean(args?.allow_mutating_method ?? false),
|
|
1888
|
+
bambuModel: rpcBambuModel,
|
|
1889
|
+
});
|
|
1890
|
+
break;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1325
1893
|
case "merge_vertices":
|
|
1326
1894
|
if (!args?.stl_path) {
|
|
1327
1895
|
throw new Error("Missing required parameter: stl_path");
|
|
@@ -1459,10 +2027,13 @@ class ThreeDPrinterMCPServer {
|
|
|
1459
2027
|
gcode: string,
|
|
1460
2028
|
print: boolean
|
|
1461
2029
|
) {
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
2030
|
+
const sourceIsFilePath = fs.existsSync(gcode) && fs.statSync(gcode).isFile();
|
|
2031
|
+
const tempFilePath = sourceIsFilePath ? gcode : path.join(TEMP_DIR, filename);
|
|
2032
|
+
|
|
2033
|
+
if (!sourceIsFilePath) {
|
|
2034
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
2035
|
+
fs.writeFileSync(tempFilePath, gcode);
|
|
2036
|
+
}
|
|
1466
2037
|
|
|
1467
2038
|
try {
|
|
1468
2039
|
const implementation = this.printerFactory.getImplementation(type);
|
|
@@ -1474,8 +2045,7 @@ class ThreeDPrinterMCPServer {
|
|
|
1474
2045
|
|
|
1475
2046
|
return await implementation.uploadFile(host, port, apiKey, tempFilePath, filename, print);
|
|
1476
2047
|
} finally {
|
|
1477
|
-
|
|
1478
|
-
if (fs.existsSync(tempFilePath)) {
|
|
2048
|
+
if (!sourceIsFilePath && fs.existsSync(tempFilePath)) {
|
|
1479
2049
|
fs.unlinkSync(tempFilePath);
|
|
1480
2050
|
}
|
|
1481
2051
|
}
|