mcp-3d-printer-server 1.2.2 → 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 +236 -26
- 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/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { PrinterFactory } from "./printers/printer-factory.js";
|
|
|
14
14
|
import { STLManipulator } from "./stl/stl-manipulator.js";
|
|
15
15
|
import { parse3MF } from './3mf_parser.js';
|
|
16
16
|
import { BambuImplementation } from "./printers/bambu.js";
|
|
17
|
+
import { inspectFuluOrcaSetup, invokeFuluBridgeRpc, isFuluPrintRpcMethod, } from "./fulu-orca.js";
|
|
17
18
|
// Load environment variables from .env file
|
|
18
19
|
dotenv.config();
|
|
19
20
|
// Default values
|
|
@@ -23,9 +24,88 @@ const DEFAULT_API_KEY = process.env.API_KEY || "";
|
|
|
23
24
|
const DEFAULT_TYPE = process.env.PRINTER_TYPE || "octoprint"; // Default to OctoPrint
|
|
24
25
|
const TEMP_DIR = process.env.TEMP_DIR || path.join(process.cwd(), "temp");
|
|
25
26
|
// Slicer configuration
|
|
26
|
-
const
|
|
27
|
-
|
|
27
|
+
const CANONICAL_SLICER_TYPES = [
|
|
28
|
+
"prusaslicer",
|
|
29
|
+
"cura",
|
|
30
|
+
"slic3r",
|
|
31
|
+
"orcaslicer",
|
|
32
|
+
"orcaslicer-bambulab",
|
|
33
|
+
"bambustudio",
|
|
34
|
+
];
|
|
35
|
+
const SLICER_TYPE_ALIASES = {
|
|
36
|
+
prusa: "prusaslicer",
|
|
37
|
+
prusaslicer: "prusaslicer",
|
|
38
|
+
cura: "cura",
|
|
39
|
+
curaengine: "cura",
|
|
40
|
+
slic3r: "slic3r",
|
|
41
|
+
orca: "orcaslicer",
|
|
42
|
+
orcaslicer: "orcaslicer",
|
|
43
|
+
"orca-slicer": "orcaslicer",
|
|
44
|
+
"orcaslicer-bambulab": "orcaslicer-bambulab",
|
|
45
|
+
"orcaslicer_bambulab": "orcaslicer-bambulab",
|
|
46
|
+
"orca-bambulab": "orcaslicer-bambulab",
|
|
47
|
+
"orca_bambulab": "orcaslicer-bambulab",
|
|
48
|
+
"fulu-orca": "orcaslicer-bambulab",
|
|
49
|
+
"fulu_orca": "orcaslicer-bambulab",
|
|
50
|
+
"orca-studio": "orcaslicer-bambulab",
|
|
51
|
+
"orca_studio": "orcaslicer-bambulab",
|
|
52
|
+
orcastudio: "orcaslicer-bambulab",
|
|
53
|
+
bambustudio: "bambustudio",
|
|
54
|
+
"bambu-studio": "bambustudio",
|
|
55
|
+
"bambu_studio": "bambustudio",
|
|
56
|
+
};
|
|
57
|
+
const SLICER_TYPE_ENUM = [
|
|
58
|
+
...CANONICAL_SLICER_TYPES,
|
|
59
|
+
"fulu_orca",
|
|
60
|
+
"fulu-orca",
|
|
61
|
+
"orca-studio",
|
|
62
|
+
"orca_bambulab",
|
|
63
|
+
];
|
|
64
|
+
function normalizeSlicerType(rawValue) {
|
|
65
|
+
const normalized = (rawValue || "").trim().toLowerCase();
|
|
66
|
+
const resolved = SLICER_TYPE_ALIASES[normalized];
|
|
67
|
+
if (!resolved) {
|
|
68
|
+
throw new Error(`Unsupported slicer type "${rawValue}". Valid types: ${SLICER_TYPE_ENUM.join(", ")}`);
|
|
69
|
+
}
|
|
70
|
+
return resolved;
|
|
71
|
+
}
|
|
72
|
+
function isBambuProjectSlicer(slicerType) {
|
|
73
|
+
return slicerType === "orcaslicer-bambulab" || slicerType === "bambustudio";
|
|
74
|
+
}
|
|
75
|
+
function firstExistingPath(candidates) {
|
|
76
|
+
return candidates.find((candidate) => fs.existsSync(candidate));
|
|
77
|
+
}
|
|
78
|
+
function defaultFuluOrcaPath() {
|
|
79
|
+
return (process.env.FULU_ORCA_PATH ||
|
|
80
|
+
process.env.ORCASLICER_BAMBULAB_PATH ||
|
|
81
|
+
process.env.ORCA_SLICER_BAMBULAB_PATH ||
|
|
82
|
+
firstExistingPath([
|
|
83
|
+
"/Applications/OrcaSlicer.app/Contents/MacOS/OrcaSlicer",
|
|
84
|
+
"/Applications/Orca Studio.app/Contents/MacOS/OrcaSlicer",
|
|
85
|
+
"/Applications/OrcaStudio.app/Contents/MacOS/OrcaStudio",
|
|
86
|
+
"/usr/local/bin/orcaslicer",
|
|
87
|
+
"/usr/bin/orcaslicer",
|
|
88
|
+
]) ||
|
|
89
|
+
"orcaslicer");
|
|
90
|
+
}
|
|
91
|
+
function defaultBambuStudioPath() {
|
|
92
|
+
return (firstExistingPath([
|
|
93
|
+
"/Applications/BambuStudio.app/Contents/MacOS/BambuStudio",
|
|
94
|
+
"/Applications/Bambu Studio.app/Contents/MacOS/BambuStudio",
|
|
95
|
+
"/usr/local/bin/bambustudio",
|
|
96
|
+
"/usr/bin/bambustudio",
|
|
97
|
+
]) ||
|
|
98
|
+
"bambustudio");
|
|
99
|
+
}
|
|
100
|
+
const DEFAULT_SLICER_TYPE = normalizeSlicerType(process.env.SLICER_TYPE || (DEFAULT_TYPE.toLowerCase() === "bambu" ? "orcaslicer-bambulab" : "prusaslicer"));
|
|
101
|
+
const DEFAULT_SLICER_PATH = process.env.SLICER_PATH ||
|
|
102
|
+
(DEFAULT_SLICER_TYPE === "orcaslicer-bambulab"
|
|
103
|
+
? defaultFuluOrcaPath()
|
|
104
|
+
: DEFAULT_SLICER_TYPE === "bambustudio"
|
|
105
|
+
? defaultBambuStudioPath()
|
|
106
|
+
: "");
|
|
28
107
|
const DEFAULT_SLICER_PROFILE = process.env.SLICER_PROFILE || "";
|
|
108
|
+
const DEFAULT_FILAMENT_PROFILE = process.env.FILAMENT_PROFILE || process.env.SLICER_FILAMENT_PROFILE || "";
|
|
29
109
|
// Bambu-specific default values
|
|
30
110
|
const DEFAULT_BAMBU_SERIAL = process.env.BAMBU_SERIAL || "";
|
|
31
111
|
const DEFAULT_BAMBU_TOKEN = process.env.BAMBU_TOKEN || "";
|
|
@@ -193,6 +273,43 @@ class ThreeDPrinterMCPServer {
|
|
|
193
273
|
this.setupResourceHandlers();
|
|
194
274
|
this.setupToolHandlers();
|
|
195
275
|
}
|
|
276
|
+
extractParsedAmsMapping(parsed3MFData) {
|
|
277
|
+
if (!parsed3MFData?.slicerConfig?.ams_mapping) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
const slots = Object.values(parsed3MFData.slicerConfig.ams_mapping).filter((value) => typeof value === "number");
|
|
281
|
+
return slots.length > 0 ? slots.sort((a, b) => a - b) : undefined;
|
|
282
|
+
}
|
|
283
|
+
resolveAmsPrintOptions(parsed3MFData, args) {
|
|
284
|
+
let finalAmsMapping = this.extractParsedAmsMapping(parsed3MFData);
|
|
285
|
+
let useAMS = args?.use_ams !== undefined
|
|
286
|
+
? Boolean(args.use_ams)
|
|
287
|
+
: !!finalAmsMapping && finalAmsMapping.length > 0;
|
|
288
|
+
const override = args?.ams_mapping;
|
|
289
|
+
if (override !== undefined) {
|
|
290
|
+
let userMappingOverride;
|
|
291
|
+
if (Array.isArray(override)) {
|
|
292
|
+
userMappingOverride = override.filter((value) => typeof value === "number");
|
|
293
|
+
}
|
|
294
|
+
else if (typeof override === "object" && override !== null) {
|
|
295
|
+
userMappingOverride = Object.values(override)
|
|
296
|
+
.filter((value) => typeof value === "number")
|
|
297
|
+
.sort((a, b) => a - b);
|
|
298
|
+
}
|
|
299
|
+
if (userMappingOverride && userMappingOverride.length > 0) {
|
|
300
|
+
finalAmsMapping = userMappingOverride;
|
|
301
|
+
useAMS = true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (args?.use_ams === false) {
|
|
305
|
+
finalAmsMapping = undefined;
|
|
306
|
+
useAMS = false;
|
|
307
|
+
}
|
|
308
|
+
if (!finalAmsMapping || finalAmsMapping.length === 0) {
|
|
309
|
+
useAMS = false;
|
|
310
|
+
}
|
|
311
|
+
return { useAMS, amsMapping: finalAmsMapping };
|
|
312
|
+
}
|
|
196
313
|
setupResourceHandlers() {
|
|
197
314
|
// List available resources
|
|
198
315
|
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
@@ -309,6 +426,205 @@ class ThreeDPrinterMCPServer {
|
|
|
309
426
|
}
|
|
310
427
|
}
|
|
311
428
|
},
|
|
429
|
+
{
|
|
430
|
+
name: "list_printer_files",
|
|
431
|
+
description: "List files available on the 3D printer",
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: "object",
|
|
434
|
+
properties: {
|
|
435
|
+
host: {
|
|
436
|
+
type: "string",
|
|
437
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
438
|
+
},
|
|
439
|
+
port: {
|
|
440
|
+
type: "string",
|
|
441
|
+
description: "Port of the printer API (default: value from env)"
|
|
442
|
+
},
|
|
443
|
+
type: {
|
|
444
|
+
type: "string",
|
|
445
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
446
|
+
},
|
|
447
|
+
api_key: {
|
|
448
|
+
type: "string",
|
|
449
|
+
description: "API key for authentication (default: value from env)"
|
|
450
|
+
},
|
|
451
|
+
bambu_serial: {
|
|
452
|
+
type: "string",
|
|
453
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
454
|
+
},
|
|
455
|
+
bambu_token: {
|
|
456
|
+
type: "string",
|
|
457
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: "upload_gcode",
|
|
464
|
+
description: "Upload G-code content or a local G-code file path to the printer",
|
|
465
|
+
inputSchema: {
|
|
466
|
+
type: "object",
|
|
467
|
+
properties: {
|
|
468
|
+
host: {
|
|
469
|
+
type: "string",
|
|
470
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
471
|
+
},
|
|
472
|
+
port: {
|
|
473
|
+
type: "string",
|
|
474
|
+
description: "Port of the printer API (default: value from env)"
|
|
475
|
+
},
|
|
476
|
+
type: {
|
|
477
|
+
type: "string",
|
|
478
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
479
|
+
},
|
|
480
|
+
api_key: {
|
|
481
|
+
type: "string",
|
|
482
|
+
description: "API key for authentication (default: value from env)"
|
|
483
|
+
},
|
|
484
|
+
bambu_serial: {
|
|
485
|
+
type: "string",
|
|
486
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
487
|
+
},
|
|
488
|
+
bambu_token: {
|
|
489
|
+
type: "string",
|
|
490
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
491
|
+
},
|
|
492
|
+
filename: {
|
|
493
|
+
type: "string",
|
|
494
|
+
description: "Filename to use on the printer. Defaults to the basename of gcode_path when omitted."
|
|
495
|
+
},
|
|
496
|
+
gcode: {
|
|
497
|
+
type: "string",
|
|
498
|
+
description: "G-code content, or a local path to a G-code file."
|
|
499
|
+
},
|
|
500
|
+
gcode_path: {
|
|
501
|
+
type: "string",
|
|
502
|
+
description: "Local path to a G-code file to upload."
|
|
503
|
+
},
|
|
504
|
+
print: {
|
|
505
|
+
type: "boolean",
|
|
506
|
+
description: "Start printing after upload when the printer backend supports it."
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
anyOf: [
|
|
510
|
+
{ required: ["filename", "gcode"] },
|
|
511
|
+
{ required: ["gcode_path"] }
|
|
512
|
+
]
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
name: "start_print",
|
|
517
|
+
description: "Start printing a file that is already available on the printer",
|
|
518
|
+
inputSchema: {
|
|
519
|
+
type: "object",
|
|
520
|
+
properties: {
|
|
521
|
+
host: {
|
|
522
|
+
type: "string",
|
|
523
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
524
|
+
},
|
|
525
|
+
port: {
|
|
526
|
+
type: "string",
|
|
527
|
+
description: "Port of the printer API (default: value from env)"
|
|
528
|
+
},
|
|
529
|
+
type: {
|
|
530
|
+
type: "string",
|
|
531
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
532
|
+
},
|
|
533
|
+
api_key: {
|
|
534
|
+
type: "string",
|
|
535
|
+
description: "API key for authentication (default: value from env)"
|
|
536
|
+
},
|
|
537
|
+
bambu_serial: {
|
|
538
|
+
type: "string",
|
|
539
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
540
|
+
},
|
|
541
|
+
bambu_token: {
|
|
542
|
+
type: "string",
|
|
543
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
544
|
+
},
|
|
545
|
+
filename: {
|
|
546
|
+
type: "string",
|
|
547
|
+
description: "Name/path of the printer-side G-code file to start."
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
required: ["filename"]
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "cancel_print",
|
|
555
|
+
description: "Cancel the current print job",
|
|
556
|
+
inputSchema: {
|
|
557
|
+
type: "object",
|
|
558
|
+
properties: {
|
|
559
|
+
host: {
|
|
560
|
+
type: "string",
|
|
561
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
562
|
+
},
|
|
563
|
+
port: {
|
|
564
|
+
type: "string",
|
|
565
|
+
description: "Port of the printer API (default: value from env)"
|
|
566
|
+
},
|
|
567
|
+
type: {
|
|
568
|
+
type: "string",
|
|
569
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
570
|
+
},
|
|
571
|
+
api_key: {
|
|
572
|
+
type: "string",
|
|
573
|
+
description: "API key for authentication (default: value from env)"
|
|
574
|
+
},
|
|
575
|
+
bambu_serial: {
|
|
576
|
+
type: "string",
|
|
577
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
578
|
+
},
|
|
579
|
+
bambu_token: {
|
|
580
|
+
type: "string",
|
|
581
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
name: "set_printer_temperature",
|
|
588
|
+
description: "Set the temperature of a printer component",
|
|
589
|
+
inputSchema: {
|
|
590
|
+
type: "object",
|
|
591
|
+
properties: {
|
|
592
|
+
host: {
|
|
593
|
+
type: "string",
|
|
594
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
595
|
+
},
|
|
596
|
+
port: {
|
|
597
|
+
type: "string",
|
|
598
|
+
description: "Port of the printer API (default: value from env)"
|
|
599
|
+
},
|
|
600
|
+
type: {
|
|
601
|
+
type: "string",
|
|
602
|
+
description: "Type of printer management system (octoprint, klipper, duet, repetier, bambu, prusa, creality) (default: value from env)"
|
|
603
|
+
},
|
|
604
|
+
api_key: {
|
|
605
|
+
type: "string",
|
|
606
|
+
description: "API key for authentication (default: value from env)"
|
|
607
|
+
},
|
|
608
|
+
bambu_serial: {
|
|
609
|
+
type: "string",
|
|
610
|
+
description: "Serial number for Bambu Lab printers (default: value from env)"
|
|
611
|
+
},
|
|
612
|
+
bambu_token: {
|
|
613
|
+
type: "string",
|
|
614
|
+
description: "Access token for Bambu Lab printers (default: value from env)"
|
|
615
|
+
},
|
|
616
|
+
component: {
|
|
617
|
+
type: "string",
|
|
618
|
+
description: "Printer component to heat, such as extruder or bed."
|
|
619
|
+
},
|
|
620
|
+
temperature: {
|
|
621
|
+
type: "number",
|
|
622
|
+
description: "Target temperature in Celsius."
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
required: ["component", "temperature"]
|
|
626
|
+
}
|
|
627
|
+
},
|
|
312
628
|
// New STL manipulation tools
|
|
313
629
|
{
|
|
314
630
|
name: "extend_stl_base",
|
|
@@ -341,7 +657,7 @@ class ThreeDPrinterMCPServer {
|
|
|
341
657
|
bambu_model: {
|
|
342
658
|
type: "string",
|
|
343
659
|
enum: ["p1s", "p1p", "x1c", "x1e", "a1", "a1mini", "h2d"],
|
|
344
|
-
description: "Bambu Lab printer model
|
|
660
|
+
description: "Bambu Lab printer model - required when using Bambu project slicers to ensure correct G-code."
|
|
345
661
|
},
|
|
346
662
|
nozzle_diameter: {
|
|
347
663
|
type: "string",
|
|
@@ -349,7 +665,8 @@ class ThreeDPrinterMCPServer {
|
|
|
349
665
|
},
|
|
350
666
|
slicer_type: {
|
|
351
667
|
type: "string",
|
|
352
|
-
|
|
668
|
+
enum: [...SLICER_TYPE_ENUM],
|
|
669
|
+
description: "Type of slicer to use (prusaslicer, cura, slic3r, orcaslicer, orcaslicer-bambulab, bambustudio). Use orcaslicer-bambulab for the FULU fork."
|
|
353
670
|
},
|
|
354
671
|
slicer_path: {
|
|
355
672
|
type: "string",
|
|
@@ -357,7 +674,11 @@ class ThreeDPrinterMCPServer {
|
|
|
357
674
|
},
|
|
358
675
|
slicer_profile: {
|
|
359
676
|
type: "string",
|
|
360
|
-
description: "Profile to use for slicing (default: value from env)"
|
|
677
|
+
description: "Profile to use for slicing (default: value from env). OrcaSlicer also accepts machine/process profiles separated with ';', optionally followed by '|filament.json'."
|
|
678
|
+
},
|
|
679
|
+
filament_profile: {
|
|
680
|
+
type: "string",
|
|
681
|
+
description: "OrcaSlicer filament profile path loaded with --load-filaments (default: FILAMENT_PROFILE/SLICER_FILAMENT_PROFILE env)."
|
|
361
682
|
}
|
|
362
683
|
},
|
|
363
684
|
required: ["stl_path"]
|
|
@@ -422,6 +743,37 @@ class ThreeDPrinterMCPServer {
|
|
|
422
743
|
api_key: {
|
|
423
744
|
type: "string",
|
|
424
745
|
description: "API key for authentication (default: value from env)"
|
|
746
|
+
},
|
|
747
|
+
bambu_model: {
|
|
748
|
+
type: "string",
|
|
749
|
+
enum: ["p1s", "p1p", "x1c", "x1e", "a1", "a1mini", "h2d"],
|
|
750
|
+
description: "Bambu Lab printer model. Required for Bambu print operations."
|
|
751
|
+
},
|
|
752
|
+
bed_type: {
|
|
753
|
+
type: "string",
|
|
754
|
+
enum: ["textured_plate", "cool_plate", "engineering_plate", "hot_plate"],
|
|
755
|
+
description: "Bed/plate type installed on the printer (default: textured_plate)."
|
|
756
|
+
},
|
|
757
|
+
nozzle_diameter: {
|
|
758
|
+
type: "string",
|
|
759
|
+
description: "Nozzle diameter in mm (default: 0.4)."
|
|
760
|
+
},
|
|
761
|
+
slicer_type: {
|
|
762
|
+
type: "string",
|
|
763
|
+
enum: [...SLICER_TYPE_ENUM],
|
|
764
|
+
description: "Type of slicer to use. Use orcaslicer-bambulab for FULU OrcaSlicer-bambulab."
|
|
765
|
+
},
|
|
766
|
+
slicer_path: {
|
|
767
|
+
type: "string",
|
|
768
|
+
description: "Path to the slicer executable (default: value from env)."
|
|
769
|
+
},
|
|
770
|
+
slicer_profile: {
|
|
771
|
+
type: "string",
|
|
772
|
+
description: "Profile to use for slicing (default: value from env). OrcaSlicer also accepts machine/process profiles separated with ';', optionally followed by '|filament.json'."
|
|
773
|
+
},
|
|
774
|
+
filament_profile: {
|
|
775
|
+
type: "string",
|
|
776
|
+
description: "OrcaSlicer filament profile path loaded with --load-filaments (default: FILAMENT_PROFILE/SLICER_FILAMENT_PROFILE env)."
|
|
425
777
|
}
|
|
426
778
|
},
|
|
427
779
|
required: ["stl_path", "extension_inches"]
|
|
@@ -650,11 +1002,126 @@ class ThreeDPrinterMCPServer {
|
|
|
650
1002
|
type: "object",
|
|
651
1003
|
description: "Override AMS filament mapping (e.g., {\"Generic PLA\": 0, \"Generic PETG\": 1}).",
|
|
652
1004
|
additionalProperties: { type: "number" }
|
|
1005
|
+
},
|
|
1006
|
+
slicer_type: {
|
|
1007
|
+
type: "string",
|
|
1008
|
+
enum: [...SLICER_TYPE_ENUM],
|
|
1009
|
+
description: "Slicer to use if the 3MF needs auto-slicing. Use orcaslicer-bambulab for FULU OrcaSlicer-bambulab."
|
|
1010
|
+
},
|
|
1011
|
+
slicer_path: {
|
|
1012
|
+
type: "string",
|
|
1013
|
+
description: "Path to the slicer executable if auto-slicing is needed."
|
|
1014
|
+
},
|
|
1015
|
+
slicer_profile: {
|
|
1016
|
+
type: "string",
|
|
1017
|
+
description: "Optional slicer settings/profile path for auto-slicing."
|
|
1018
|
+
},
|
|
1019
|
+
filament_profile: {
|
|
1020
|
+
type: "string",
|
|
1021
|
+
description: "Optional filament profile path for auto-slicing with OrcaSlicer/Bambu Studio."
|
|
1022
|
+
},
|
|
1023
|
+
use_ams: {
|
|
1024
|
+
type: "boolean",
|
|
1025
|
+
description: "Whether to use AMS for the print. Defaults from parsed 3MF mapping when present."
|
|
1026
|
+
},
|
|
1027
|
+
bed_leveling: {
|
|
1028
|
+
type: "boolean",
|
|
1029
|
+
description: "Override bed leveling flag for the Bambu print command."
|
|
1030
|
+
},
|
|
1031
|
+
flow_calibration: {
|
|
1032
|
+
type: "boolean",
|
|
1033
|
+
description: "Override flow calibration flag for the Bambu print command."
|
|
1034
|
+
},
|
|
1035
|
+
vibration_calibration: {
|
|
1036
|
+
type: "boolean",
|
|
1037
|
+
description: "Override vibration calibration flag for the Bambu print command."
|
|
1038
|
+
},
|
|
1039
|
+
layer_inspect: {
|
|
1040
|
+
type: "boolean",
|
|
1041
|
+
description: "Override layer inspection flag for the Bambu print command."
|
|
1042
|
+
},
|
|
1043
|
+
timelapse: {
|
|
1044
|
+
type: "boolean",
|
|
1045
|
+
description: "Override timelapse flag for the Bambu print command."
|
|
653
1046
|
}
|
|
654
1047
|
},
|
|
655
1048
|
required: ["three_mf_path", "bambu_model"]
|
|
656
1049
|
}
|
|
657
1050
|
},
|
|
1051
|
+
{
|
|
1052
|
+
name: "check_fulu_orca_setup",
|
|
1053
|
+
description: "Inspect a FULU OrcaSlicer-bambulab install, platform runtime payload, setup commands, and optionally probe the BambuNetwork bridge.",
|
|
1054
|
+
inputSchema: {
|
|
1055
|
+
type: "object",
|
|
1056
|
+
properties: {
|
|
1057
|
+
slicer_path: {
|
|
1058
|
+
type: "string",
|
|
1059
|
+
description: "Path to the FULU OrcaSlicer executable. Defaults from SLICER_PATH/FULU_ORCA_PATH."
|
|
1060
|
+
},
|
|
1061
|
+
plugin_dir: {
|
|
1062
|
+
type: "string",
|
|
1063
|
+
description: "Directory containing the FULU Bambu runtime payload; on macOS this is usually OrcaSlicer.app/Contents/MacOS."
|
|
1064
|
+
},
|
|
1065
|
+
runtime_dir: {
|
|
1066
|
+
type: "string",
|
|
1067
|
+
description: "Installed runtime directory. On macOS this defaults to ~/Library/Application Support/OrcaSlicer/macos-bridge/runtime."
|
|
1068
|
+
},
|
|
1069
|
+
platform: {
|
|
1070
|
+
type: "string",
|
|
1071
|
+
enum: ["darwin", "macos", "win32", "windows", "linux"],
|
|
1072
|
+
description: "Platform to inspect. Defaults to the current Node.js platform."
|
|
1073
|
+
},
|
|
1074
|
+
bridge_command: {
|
|
1075
|
+
type: "string",
|
|
1076
|
+
description: "Command that starts the FULU BambuNetwork bridge host for probing."
|
|
1077
|
+
},
|
|
1078
|
+
run_bridge_probe: {
|
|
1079
|
+
type: "boolean",
|
|
1080
|
+
description: "When true, sends bridge.handshake, bridge.capabilities, and bridge.runtime_info to the bridge host."
|
|
1081
|
+
},
|
|
1082
|
+
probe_timeout_ms: {
|
|
1083
|
+
type: "number",
|
|
1084
|
+
description: "Bridge probe timeout in milliseconds (default: 5000)."
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
name: "fulu_bambu_network_rpc",
|
|
1091
|
+
description: "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.",
|
|
1092
|
+
inputSchema: {
|
|
1093
|
+
type: "object",
|
|
1094
|
+
properties: {
|
|
1095
|
+
bridge_command: {
|
|
1096
|
+
type: "string",
|
|
1097
|
+
description: "Command that starts the FULU BambuNetwork bridge host. Defaults to FULU_BAMBU_BRIDGE_COMMAND."
|
|
1098
|
+
},
|
|
1099
|
+
method: {
|
|
1100
|
+
type: "string",
|
|
1101
|
+
description: "FULU bridge method, e.g. bridge.handshake, bridge.runtime_info, net.get_user_print_info, net.start_print."
|
|
1102
|
+
},
|
|
1103
|
+
payload: {
|
|
1104
|
+
type: "object",
|
|
1105
|
+
description: "JSON payload sent to the FULU bridge method.",
|
|
1106
|
+
additionalProperties: true
|
|
1107
|
+
},
|
|
1108
|
+
timeout_ms: {
|
|
1109
|
+
type: "number",
|
|
1110
|
+
description: "Bridge request timeout in milliseconds (default: 5000)."
|
|
1111
|
+
},
|
|
1112
|
+
allow_mutating_method: {
|
|
1113
|
+
type: "boolean",
|
|
1114
|
+
description: "Required for methods outside the read-only bridge/net allowlist."
|
|
1115
|
+
},
|
|
1116
|
+
bambu_model: {
|
|
1117
|
+
type: "string",
|
|
1118
|
+
enum: ["p1s", "p1p", "x1c", "x1e", "a1", "a1mini", "h2d"],
|
|
1119
|
+
description: "Required when calling FULU print RPC methods; preserves the Bambu model safety gate."
|
|
1120
|
+
}
|
|
1121
|
+
},
|
|
1122
|
+
required: ["method"]
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
658
1125
|
{
|
|
659
1126
|
name: "merge_vertices",
|
|
660
1127
|
description: "Merge vertices in an STL file that are closer than the specified tolerance.",
|
|
@@ -741,9 +1208,10 @@ class ThreeDPrinterMCPServer {
|
|
|
741
1208
|
const apiKey = String(args?.api_key || DEFAULT_API_KEY);
|
|
742
1209
|
const bambuSerial = String(args?.bambu_serial || DEFAULT_BAMBU_SERIAL);
|
|
743
1210
|
const bambuToken = String(args?.bambu_token || DEFAULT_BAMBU_TOKEN);
|
|
744
|
-
const slicerType = String(args?.slicer_type || DEFAULT_SLICER_TYPE);
|
|
1211
|
+
const slicerType = normalizeSlicerType(String(args?.slicer_type || DEFAULT_SLICER_TYPE));
|
|
745
1212
|
const slicerPath = String(args?.slicer_path || DEFAULT_SLICER_PATH);
|
|
746
1213
|
const slicerProfile = String(args?.slicer_profile || DEFAULT_SLICER_PROFILE);
|
|
1214
|
+
const filamentProfile = String(args?.filament_profile || DEFAULT_FILAMENT_PROFILE);
|
|
747
1215
|
try {
|
|
748
1216
|
let result;
|
|
749
1217
|
switch (name) {
|
|
@@ -754,10 +1222,18 @@ class ThreeDPrinterMCPServer {
|
|
|
754
1222
|
result = await this.getPrinterFiles(host, port, type, apiKey, bambuSerial, bambuToken);
|
|
755
1223
|
break;
|
|
756
1224
|
case "upload_gcode":
|
|
757
|
-
if (!args?.
|
|
758
|
-
throw new Error("Missing required
|
|
1225
|
+
if (!args?.gcode && !args?.gcode_path) {
|
|
1226
|
+
throw new Error("Missing required parameter: gcode or gcode_path");
|
|
1227
|
+
}
|
|
1228
|
+
if (args?.gcode_path && !fs.existsSync(String(args.gcode_path))) {
|
|
1229
|
+
throw new Error(`G-code file not found: ${String(args.gcode_path)}`);
|
|
759
1230
|
}
|
|
760
|
-
|
|
1231
|
+
if (!args?.filename && !args?.gcode_path) {
|
|
1232
|
+
throw new Error("Missing required parameter: filename");
|
|
1233
|
+
}
|
|
1234
|
+
const uploadFilename = String(args?.filename || path.basename(String(args.gcode_path)));
|
|
1235
|
+
const uploadGcodeSource = String(args?.gcode_path || args.gcode);
|
|
1236
|
+
result = await this.uploadGcode(host, port, type, apiKey, bambuSerial, bambuToken, uploadFilename, uploadGcodeSource, Boolean(args.print || false));
|
|
761
1237
|
break;
|
|
762
1238
|
case "start_print":
|
|
763
1239
|
if (!args?.filename) {
|
|
@@ -787,13 +1263,13 @@ class ThreeDPrinterMCPServer {
|
|
|
787
1263
|
}
|
|
788
1264
|
// Resolve printer model for BambuStudio slicer
|
|
789
1265
|
let slicePreset;
|
|
790
|
-
if (slicerType
|
|
1266
|
+
if (isBambuProjectSlicer(slicerType)) {
|
|
791
1267
|
const sliceModel = await this.resolveBambuModel(args?.bambu_model);
|
|
792
1268
|
const nozzleDiam = String(args?.nozzle_diameter || DEFAULT_NOZZLE_DIAMETER);
|
|
793
1269
|
slicePreset = BAMBU_MODEL_PRESETS[sliceModel]?.(nozzleDiam);
|
|
794
1270
|
}
|
|
795
1271
|
result = await this.stlManipulator.sliceSTL(String(args.stl_path), slicerType, slicerPath, slicerProfile || undefined, undefined, // progressCallback
|
|
796
|
-
slicePreset);
|
|
1272
|
+
slicePreset, filamentProfile || undefined);
|
|
797
1273
|
break;
|
|
798
1274
|
}
|
|
799
1275
|
case "confirm_temperatures":
|
|
@@ -817,12 +1293,50 @@ class ThreeDPrinterMCPServer {
|
|
|
817
1293
|
const extendedStlPath = await this.stlManipulator.extendBase(String(args.stl_path), Number(args.extension_inches), processProgressCallback);
|
|
818
1294
|
// 2. Slice the extended STL file
|
|
819
1295
|
let processPreset;
|
|
820
|
-
if (
|
|
1296
|
+
if (type.toLowerCase() === 'bambu' || isBambuProjectSlicer(slicerType)) {
|
|
821
1297
|
const processModel = await this.resolveBambuModel(args?.bambu_model);
|
|
822
1298
|
const processNozzle = String(args?.nozzle_diameter || DEFAULT_NOZZLE_DIAMETER);
|
|
823
|
-
|
|
1299
|
+
if (isBambuProjectSlicer(slicerType)) {
|
|
1300
|
+
processPreset = BAMBU_MODEL_PRESETS[processModel]?.(processNozzle);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
const gcodePath = await this.stlManipulator.sliceSTL(extendedStlPath, slicerType, slicerPath, slicerProfile || undefined, processProgressCallback, processPreset, filamentProfile || undefined);
|
|
1304
|
+
if (type.toLowerCase() === 'bambu' && gcodePath.toLowerCase().endsWith(".3mf")) {
|
|
1305
|
+
if (!bambuSerial || !bambuToken) {
|
|
1306
|
+
throw new Error("Bambu serial number and access token are required for Bambu 3MF printing.");
|
|
1307
|
+
}
|
|
1308
|
+
const factoryImplementation = this.printerFactory.getImplementation('bambu');
|
|
1309
|
+
if (!(factoryImplementation instanceof BambuImplementation)) {
|
|
1310
|
+
throw new Error("Internal error: Could not get Bambu printer implementation.");
|
|
1311
|
+
}
|
|
1312
|
+
const printBedType = resolveBedType(args?.bed_type);
|
|
1313
|
+
let parsedProcess3MF;
|
|
1314
|
+
try {
|
|
1315
|
+
parsedProcess3MF = await parse3MF(gcodePath);
|
|
1316
|
+
}
|
|
1317
|
+
catch (error) {
|
|
1318
|
+
console.warn(`Could not parse AMS metadata from sliced 3MF ${gcodePath}:`, error.message);
|
|
1319
|
+
}
|
|
1320
|
+
const amsOptions = this.resolveAmsPrintOptions(parsedProcess3MF, args);
|
|
1321
|
+
result = await factoryImplementation.print3mf(host, bambuSerial, bambuToken, {
|
|
1322
|
+
projectName: path.basename(gcodePath).replace(/\.3mf$/i, ''),
|
|
1323
|
+
filePath: gcodePath,
|
|
1324
|
+
plateIndex: 0,
|
|
1325
|
+
...amsOptions,
|
|
1326
|
+
bedType: printBedType,
|
|
1327
|
+
bedLeveling: args?.bed_leveling !== undefined ? Boolean(args.bed_leveling) : undefined,
|
|
1328
|
+
flowCalibration: args?.flow_calibration !== undefined ? Boolean(args.flow_calibration) : undefined,
|
|
1329
|
+
vibrationCalibration: args?.vibration_calibration !== undefined
|
|
1330
|
+
? Boolean(args.vibration_calibration)
|
|
1331
|
+
: undefined,
|
|
1332
|
+
layerInspect: args?.layer_inspect !== undefined ? Boolean(args.layer_inspect) : undefined,
|
|
1333
|
+
timelapse: args?.timelapse !== undefined ? Boolean(args.timelapse) : undefined,
|
|
1334
|
+
});
|
|
1335
|
+
break;
|
|
1336
|
+
}
|
|
1337
|
+
if (gcodePath.toLowerCase().endsWith(".3mf")) {
|
|
1338
|
+
throw new Error("process_and_print_stl produced a sliced 3MF. Direct process-and-print for sliced 3MF is only supported for Bambu printers via print_3mf.");
|
|
824
1339
|
}
|
|
825
|
-
const gcodePath = await this.stlManipulator.sliceSTL(extendedStlPath, slicerType, slicerPath, slicerProfile || undefined, processProgressCallback, processPreset);
|
|
826
1340
|
// 3. Confirm temperatures if specified
|
|
827
1341
|
if (args.extruder_temp !== undefined || args.bed_temp !== undefined) {
|
|
828
1342
|
const tempConfirmation = await this.stlManipulator.confirmTemperatures(gcodePath, {
|
|
@@ -1002,7 +1516,7 @@ class ThreeDPrinterMCPServer {
|
|
|
1002
1516
|
if (!hasGcode) {
|
|
1003
1517
|
console.log(`3MF has no gcode — auto-slicing with ${slicerType} for ${printModel}`);
|
|
1004
1518
|
threeMFPath = await this.stlManipulator.sliceSTL(threeMFPath, slicerType, slicerPath, slicerProfile || undefined, undefined, // progressCallback
|
|
1005
|
-
printPreset);
|
|
1519
|
+
printPreset, filamentProfile || undefined);
|
|
1006
1520
|
console.log("Auto-sliced to: " + threeMFPath);
|
|
1007
1521
|
}
|
|
1008
1522
|
}
|
|
@@ -1013,61 +1527,12 @@ class ThreeDPrinterMCPServer {
|
|
|
1013
1527
|
let implementation;
|
|
1014
1528
|
let threeMfFilename;
|
|
1015
1529
|
let projectName;
|
|
1016
|
-
let finalAmsMapping;
|
|
1017
|
-
let useAMS;
|
|
1018
1530
|
let printOptions; // Use a more specific type later if possible
|
|
1019
1531
|
try {
|
|
1020
1532
|
// --- Parse 3MF ---
|
|
1021
1533
|
const parsed3MFData = await parse3MF(threeMFPath);
|
|
1022
1534
|
console.log(`Successfully parsed 3MF file: ${threeMFPath}`);
|
|
1023
|
-
|
|
1024
|
-
// ... (Extract default AMS mapping logic) ...
|
|
1025
|
-
if (parsed3MFData.slicerConfig?.ams_mapping) {
|
|
1026
|
-
const slots = Object.values(parsed3MFData.slicerConfig.ams_mapping)
|
|
1027
|
-
.filter(v => typeof v === 'number');
|
|
1028
|
-
if (slots.length > 0) {
|
|
1029
|
-
parsedAmsMapping = slots.sort((a, b) => a - b);
|
|
1030
|
-
console.log("Extracted default AMS mapping from 3MF:", parsedAmsMapping);
|
|
1031
|
-
}
|
|
1032
|
-
else {
|
|
1033
|
-
console.log("AMS mapping found in 3MF, but no valid slots extracted.");
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
console.log("No default AMS mapping found in 3MF slicer config.");
|
|
1038
|
-
}
|
|
1039
|
-
// --- Gather Overrides and Determine Final Options ---
|
|
1040
|
-
finalAmsMapping = parsedAmsMapping; // Start with parsed
|
|
1041
|
-
useAMS = args?.use_ams !== undefined ? Boolean(args.use_ams) : (!!finalAmsMapping && finalAmsMapping.length > 0);
|
|
1042
|
-
// ... (Process user AMS mapping override logic) ...
|
|
1043
|
-
if (args?.ams_mapping) {
|
|
1044
|
-
let userMappingOverride = undefined;
|
|
1045
|
-
if (Array.isArray(args.ams_mapping)) {
|
|
1046
|
-
userMappingOverride = args.ams_mapping.filter(v => typeof v === 'number');
|
|
1047
|
-
}
|
|
1048
|
-
else if (typeof args.ams_mapping === 'object') {
|
|
1049
|
-
userMappingOverride = Object.values(args.ams_mapping)
|
|
1050
|
-
.filter(v => typeof v === 'number')
|
|
1051
|
-
.sort((a, b) => a - b);
|
|
1052
|
-
}
|
|
1053
|
-
if (userMappingOverride && userMappingOverride.length > 0) {
|
|
1054
|
-
console.log("Applying user AMS mapping override:", userMappingOverride);
|
|
1055
|
-
finalAmsMapping = userMappingOverride;
|
|
1056
|
-
useAMS = true; // Force useAMS if override provided
|
|
1057
|
-
}
|
|
1058
|
-
else {
|
|
1059
|
-
console.warn("Received ams_mapping override, but it was empty or invalid.");
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
// ... (Handle explicit use_ams=false) ...
|
|
1063
|
-
if (args?.use_ams === false) {
|
|
1064
|
-
console.log("User explicitly disabled AMS.");
|
|
1065
|
-
finalAmsMapping = undefined;
|
|
1066
|
-
useAMS = false;
|
|
1067
|
-
}
|
|
1068
|
-
if (!finalAmsMapping || finalAmsMapping.length === 0) {
|
|
1069
|
-
useAMS = false;
|
|
1070
|
-
}
|
|
1535
|
+
const amsOptions = this.resolveAmsPrintOptions(parsed3MFData, args);
|
|
1071
1536
|
// --- Prepare Implementation and Print Options ---
|
|
1072
1537
|
const factoryImplementation = this.printerFactory.getImplementation('bambu');
|
|
1073
1538
|
if (!(factoryImplementation instanceof BambuImplementation)) {
|
|
@@ -1077,8 +1542,7 @@ class ThreeDPrinterMCPServer {
|
|
|
1077
1542
|
threeMfFilename = path.basename(threeMFPath); // Assign to outer scope variable
|
|
1078
1543
|
projectName = threeMfFilename.replace(/\.3mf$/i, ''); // Assign to outer scope variable
|
|
1079
1544
|
printOptions = {
|
|
1080
|
-
|
|
1081
|
-
amsMapping: finalAmsMapping,
|
|
1545
|
+
...amsOptions,
|
|
1082
1546
|
bedType: printBedType,
|
|
1083
1547
|
bedLeveling: args?.bed_leveling !== undefined ? Boolean(args.bed_leveling) : undefined,
|
|
1084
1548
|
flowCalibration: args?.flow_calibration !== undefined ? Boolean(args.flow_calibration) : undefined,
|
|
@@ -1108,6 +1572,40 @@ class ThreeDPrinterMCPServer {
|
|
|
1108
1572
|
}
|
|
1109
1573
|
break;
|
|
1110
1574
|
}
|
|
1575
|
+
case "check_fulu_orca_setup": {
|
|
1576
|
+
result = await inspectFuluOrcaSetup({
|
|
1577
|
+
slicerPath: args?.slicer_path !== undefined ? String(args.slicer_path) : undefined,
|
|
1578
|
+
pluginDir: args?.plugin_dir !== undefined ? String(args.plugin_dir) : undefined,
|
|
1579
|
+
runtimeDir: args?.runtime_dir !== undefined ? String(args.runtime_dir) : undefined,
|
|
1580
|
+
platform: args?.platform !== undefined ? String(args.platform) : undefined,
|
|
1581
|
+
bridgeCommand: args?.bridge_command !== undefined ? String(args.bridge_command) : undefined,
|
|
1582
|
+
runBridgeProbe: Boolean(args?.run_bridge_probe ?? false),
|
|
1583
|
+
probeTimeoutMs: args?.probe_timeout_ms !== undefined ? Number(args.probe_timeout_ms) : undefined,
|
|
1584
|
+
});
|
|
1585
|
+
break;
|
|
1586
|
+
}
|
|
1587
|
+
case "fulu_bambu_network_rpc": {
|
|
1588
|
+
if (!args?.method) {
|
|
1589
|
+
throw new Error("Missing required parameter: method");
|
|
1590
|
+
}
|
|
1591
|
+
const method = String(args.method);
|
|
1592
|
+
let rpcBambuModel;
|
|
1593
|
+
if (isFuluPrintRpcMethod(method)) {
|
|
1594
|
+
rpcBambuModel = await this.resolveBambuModel(args?.bambu_model);
|
|
1595
|
+
}
|
|
1596
|
+
const payload = typeof args.payload === "object" && args.payload !== null && !Array.isArray(args.payload)
|
|
1597
|
+
? args.payload
|
|
1598
|
+
: undefined;
|
|
1599
|
+
result = await invokeFuluBridgeRpc({
|
|
1600
|
+
bridgeCommand: args?.bridge_command !== undefined ? String(args.bridge_command) : undefined,
|
|
1601
|
+
method,
|
|
1602
|
+
payload,
|
|
1603
|
+
timeoutMs: args?.timeout_ms !== undefined ? Number(args.timeout_ms) : undefined,
|
|
1604
|
+
allowMutatingMethod: Boolean(args?.allow_mutating_method ?? false),
|
|
1605
|
+
bambuModel: rpcBambuModel,
|
|
1606
|
+
});
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1111
1609
|
case "merge_vertices":
|
|
1112
1610
|
if (!args?.stl_path) {
|
|
1113
1611
|
throw new Error("Missing required parameter: stl_path");
|
|
@@ -1193,9 +1691,12 @@ class ThreeDPrinterMCPServer {
|
|
|
1193
1691
|
return implementation.getFile(host, port, apiKey, filename);
|
|
1194
1692
|
}
|
|
1195
1693
|
async uploadGcode(host, port, type, apiKey, bambuSerial, bambuToken, filename, gcode, print) {
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1694
|
+
const sourceIsFilePath = fs.existsSync(gcode) && fs.statSync(gcode).isFile();
|
|
1695
|
+
const tempFilePath = sourceIsFilePath ? gcode : path.join(TEMP_DIR, filename);
|
|
1696
|
+
if (!sourceIsFilePath) {
|
|
1697
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
1698
|
+
fs.writeFileSync(tempFilePath, gcode);
|
|
1699
|
+
}
|
|
1199
1700
|
try {
|
|
1200
1701
|
const implementation = this.printerFactory.getImplementation(type);
|
|
1201
1702
|
if (type.toLowerCase() === "bambu") {
|
|
@@ -1205,8 +1706,7 @@ class ThreeDPrinterMCPServer {
|
|
|
1205
1706
|
return await implementation.uploadFile(host, port, apiKey, tempFilePath, filename, print);
|
|
1206
1707
|
}
|
|
1207
1708
|
finally {
|
|
1208
|
-
|
|
1209
|
-
if (fs.existsSync(tempFilePath)) {
|
|
1709
|
+
if (!sourceIsFilePath && fs.existsSync(tempFilePath)) {
|
|
1210
1710
|
fs.unlinkSync(tempFilePath);
|
|
1211
1711
|
}
|
|
1212
1712
|
}
|