mcp-3d-printer-server 0.2.3 β†’ 1.0.1

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.
Files changed (41) hide show
  1. package/README.md +267 -29
  2. package/dist/3mf_parser.d.ts +3 -0
  3. package/dist/3mf_parser.js +216 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +1018 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/printers/bambu.d.ts +42 -0
  8. package/dist/printers/bambu.js +278 -0
  9. package/dist/printers/creality.d.ts +10 -0
  10. package/dist/printers/creality.js +90 -0
  11. package/dist/printers/duet.d.ts +10 -0
  12. package/dist/printers/duet.js +66 -0
  13. package/dist/printers/klipper.d.ts +10 -0
  14. package/dist/printers/klipper.js +62 -0
  15. package/dist/printers/octoprint.d.ts +10 -0
  16. package/dist/printers/octoprint.js +97 -0
  17. package/dist/printers/printer-factory.d.ts +9 -0
  18. package/dist/printers/printer-factory.js +33 -0
  19. package/dist/printers/prusa.d.ts +10 -0
  20. package/dist/printers/prusa.js +93 -0
  21. package/dist/printers/repetier.d.ts +10 -0
  22. package/dist/printers/repetier.js +59 -0
  23. package/dist/stl/stl-manipulator.d.ts +140 -0
  24. package/dist/stl/stl-manipulator.js +1081 -0
  25. package/dist/test/stl-manipulator-test.d.ts +2 -0
  26. package/dist/test/stl-manipulator-test.js +91 -0
  27. package/dist/types.d.ts +75 -0
  28. package/dist/types.js +26 -0
  29. package/package.json +14 -4
  30. package/src/3mf_parser.ts +223 -0
  31. package/src/index.ts +239 -10
  32. package/src/printers/bambu.ts +305 -100
  33. package/src/stl/stl-manipulator.ts +350 -117
  34. package/src/test/3DBenchy.3mf +0 -0
  35. package/src/test/stl-manipulator-test.js +114 -0
  36. package/src/test/stl-manipulator-test.ts +124 -0
  37. package/src/test/tsconfig.json +13 -0
  38. package/src/types.ts +65 -0
  39. package/.env.example +0 -17
  40. package/scripts/mcp-inspector.js +0 -49
  41. package/tsconfig.json +0 -16
package/README.md CHANGED
@@ -7,7 +7,84 @@
7
7
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com)
8
8
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D%2018.0.0-green.svg)](https://nodejs.org/en/download/)
9
9
  [![Downloads](https://img.shields.io/npm/dm/mcp-3d-printer-server.svg)](https://www.npmjs.com/package/mcp-3d-printer-server)
10
- [![GitHub stars](https://img.shields.io/github/stars/yourusername/mcp-3d-printer-server.svg?style=social&label=Star)](https://github.com/yourusername/mcp-3d-printer-server)
10
+ [![GitHub stars](https://img.shields.io/github/stars/dmontgomery40/mcp-3d-printer-server.svg?style=social&label=Star)](https://github.com/yourusername/mcp-3d-printer-server)
11
+
12
+ <details>
13
+ <summary><strong>✨ What's New / Significant Updates (as of last session)</strong></summary>
14
+
15
+ - **Bambu `.3mf` Printing:** Added the `print_3mf` tool specifically for Bambu Lab printers. This uploads the `.3mf` file and sends the print command directly via MQTT based on OpenBambuAPI specs.
16
+ - **Direct MQTT Communication (Bambu):** Refactored Bambu command handling (`print_3mf`, `cancelJob`) to use direct MQTT (TLS port 8883) instead of relying solely on `bambu-js` for commands.
17
+ - **`.3mf` File Parsing:** Implemented a parser (`src/3mf_parser.ts`) to read metadata and Bambu-specific slicer settings (from `project_settings.config`) within `.3mf` files.
18
+ - **Bambu Preset Resources:** Added support for reading Bambu Studio preset files (`machine`, `filament`, `process`) as MCP resources (e.g., `preset://bambu/process/MyPreset`) if `BAMBU_STUDIO_CONFIG_PATH` is set.
19
+ - **OrcaSlicer Integration:** Added support for using OrcaSlicer via its command-line interface for the `slice_stl` tool.
20
+ - **New STL Manipulation Tools:** Added `merge_vertices`, `center_model`, and `lay_flat` tools for basic model preparation using `three.js`.
21
+ - **Configuration Update:** Added `BAMBU_STUDIO_CONFIG_PATH` environment variable for preset loading.
22
+ - **FTP Usage Note:** Acknowledged in documentation that file operations for Bambu currently use potentially unsecured FTP via `bambu-js`.
23
+
24
+ </details>
25
+
26
+ <details>
27
+ <summary><strong>πŸ—ΊοΈ Roadmap / TODO</strong></summary>
28
+
29
+ - **Achieve Feature Parity:** Bring functionality (status detail, file operations, direct printing where possible, preset handling) for OctoPrint, Klipper, Duet, Repetier, Prusa Connect, and Creality Cloud up to the level of robustness planned for the Bambu implementation.
30
+ - **Implement Full Bambu MQTT Status:** Refactor `getStatus` for Bambu to subscribe to MQTT reports and maintain real-time state.
31
+ - **Implement Robust AMS Mapping:** Replace placeholder logic; correctly parse and use AMS mapping from `.3mf` slicer config or user overrides for the MQTT print command.
32
+ - **Implement `.3mf` Print Overrides:** Add logic to the `print_3mf` tool to handle user-provided overrides (e.g., calibration flags) and potentially common slicer settings if feasible via MQTT/G-code.
33
+ - **Calculate MD5 Hash:** Add logic to calculate and include the MD5 hash of the `.3mf` file in the MQTT print command (optional but recommended by protocol).
34
+ - **Refactor Bambu File Ops:** Investigate replacing `bambu-js` FTP operations (`getFiles`, `uploadFile`) with direct MQTT methods if possible/stable, or contribute FTPS support to `bambu-js`.
35
+ - **Add Preset Discovery Logic:** Improve preset resource listing (currently lists based on potential filenames, could parse index files if they exist).
36
+ - **Expand `.3mf` Support:** Add `.3mf` printing support for other printer types where applicable.
37
+ - **Error Handling & Reporting:** Enhance MQTT error handling and reporting of print progress/completion.
38
+ - **Testing:** Conduct thorough runtime testing of all new Bambu features.
39
+
40
+ </details>
41
+
42
+ <details>
43
+ <summary>Click to expand Table of Contents</summary>
44
+
45
+ ## Table of Contents
46
+
47
+ - [Description](#description)
48
+ - [Features](#features)
49
+ - [Installation](#installation)
50
+ - [Prerequisites](#prerequisites)
51
+ - [Install from npm](#install-from-npm)
52
+ - [Install from source](#install-from-source)
53
+ - [Running with Docker](#running-with-docker)
54
+ - [Using Slicers with Docker](#using-slicers-with-docker)
55
+ - [Configuration](#configuration)
56
+ - [Usage with Claude Desktop](#usage-with-claude-desktop)
57
+ - [Supported Printer Management Systems](#supported-printer-management-systems)
58
+ - [OctoPrint](#octoprint)
59
+ - [Klipper (via Moonraker)](#klipper-via-moonraker)
60
+ - [Duet](#duet)
61
+ - [Repetier](#repetier)
62
+ - [Bambu Labs](#bambu-labs)
63
+ - [Finding Your Bambu Printer's Serial Number and Access Token](#finding-your-bambu-printers-serial-number-and-access-token)
64
+ - [Bambu Communication Notes (MQTT & FTP)](#bambu-communication-notes-mqtt--ftp)
65
+ - [Prusa Connect](#prusa-connect)
66
+ - [Setting up Prusa Connect](#setting-up-prusa-connect)
67
+ - [Creality Cloud](#creality-cloud)
68
+ - [Setting up Creality Cloud](#setting-up-creality-cloud)
69
+ - [Available Tools](#available-tools)
70
+ - [STL Manipulation Tools](#stl-manipulation-tools)
71
+ - [Printer Control Tools](#printer-control-tools)
72
+ - [Bambu-Specific Tools](#bambu-specific-tools)
73
+ - [Available Resources](#available-resources)
74
+ - [Printer Resources](#printer-resources)
75
+ - [Bambu Preset Resources](#bambu-preset-resources)
76
+ - [Example Commands for LLM](#example-commands-for-claude)
77
+ - [Bambu Lab Printer Limitations](#bambu-lab-printer-limitations)
78
+ - [Limitations and Considerations](#limitations-and-considerations)
79
+ - [Memory Usage](#memory-usage)
80
+ - [STL Manipulation Limitations](#stl-manipulation-limitations)
81
+ - [Visualization Limitations](#visualization-limitations)
82
+ - [Performance Considerations](#performance-considerations)
83
+ - [Testing Recommendations](#testing-recommendations)
84
+ - [Badges](#badges)
85
+ - [License](#license)
86
+
87
+ </details>
11
88
 
12
89
  ## Description
13
90
 
@@ -21,7 +98,7 @@ This is a server that allows MCP users to connect with the API endpoints of thes
21
98
  - Prusa Connect
22
99
  - Creality/Ender
23
100
 
24
- This server is a Model Context Protocol (MCP) server for connecting Claude with 3D printer management systems. It allows Claude to interact with 3D printers through the APIs of various printer management systems such as OctoPrint, Klipper (via Moonraker), Duet, Repetier, and Bambu Labs printers.
101
+ This server is a Model Context Protocol (MCP) server for connecting Claude with 3D printer management systems. It allows MCP to interact with 3D printers through the APIs of various printer management systems such as OctoPrint, Klipper (via Moonraker), Duet, Repetier, and Bambu Labs printers.
25
102
 
26
103
  **Note on Resource Usage**: This MCP server includes advanced 3D model manipulation features that can be memory-intensive when working with large STL files. Please see the "Limitations and Considerations" section for important information about memory usage and performance.
27
104
 
@@ -45,6 +122,8 @@ This server is a Model Context Protocol (MCP) server for connecting Claude with
45
122
  - Slice STL files to generate G-code
46
123
  - Confirm temperature settings in G-code files
47
124
  - Complete end-to-end workflow from STL modification to printing
125
+ - Print `.3mf` files directly on Bambu Lab printers (via MQTT command)
126
+ - Read Bambu Studio preset files (printer, filament, process) as resources
48
127
 
49
128
  ## Installation
50
129
 
@@ -62,12 +141,52 @@ npm install -g mcp-3d-printer-server
62
141
  ### Install from source
63
142
 
64
143
  ```bash
65
- git clone https://github.com/yourusername/mcp-3d-printer-server.git
144
+ git clone https://github.com/dmontgomery40/mcp-3d-printer-server.git
66
145
  cd mcp-3d-printer-server
67
146
  npm install
68
147
  npm link # Makes the command available globally
69
148
  ```
70
149
 
150
+ ### Running with Docker
151
+
152
+ You can also run the server using Docker and Docker Compose for a containerized environment.
153
+
154
+ 1. Ensure you have Docker and Docker Compose installed.
155
+ 2. Copy `.env.example` to `.env` and configure your settings.
156
+ 3. Build and run the container:
157
+ ```bash
158
+ docker-compose up --build -d
159
+ ```
160
+
161
+ #### Using Slicers with Docker
162
+
163
+ Please note that the default Docker setup **cannot directly use a slicer installed on your host machine**. Mounting the slicer executable directly from the host into the container is unreliable due to operating system and library differences between your host and the container.
164
+
165
+ The recommended approach is to **install your preferred slicer *inside* the Docker image**. This makes the container self-sufficient.
166
+
167
+ To do this, you will need to modify the `Dockerfile`. Here's a conceptual example of how you might add PrusaSlicer or OrcaSlicer (specific commands may vary depending on the slicer, its dependencies, and current Alpine packages):
168
+
169
+ ```dockerfile
170
+ # ... other Dockerfile commands ...
171
+
172
+ # Example: Install PrusaSlicer or OrcaSlicer (adjust command as needed)
173
+ # Check Alpine package repositories first (e.g., apk add prusaslicer or apk add orcaslicer)
174
+ # If not available, download and install manually (e.g., AppImage):
175
+ # RUN apk add --no-cache fuse # FUSE might be needed for AppImages
176
+ # RUN wget https://example.com/path/to/OrcaSlicer_Linux_Vxxxx.AppImage -O /usr/local/bin/orcaslicer && \
177
+ # chmod +x /usr/local/bin/orcaslicer
178
+
179
+ # Set the SLICER_PATH env var accordingly in docker-compose.yml or when running
180
+ # Example for installed executable:
181
+ ENV SLICER_PATH=/usr/local/bin/orcaslicer
182
+
183
+ # ... rest of Dockerfile ...
184
+ ```
185
+
186
+ After modifying the `Dockerfile`, rebuild your image (`docker-compose build`). You'll also need to ensure the `SLICER_PATH` environment variable in your `.env` file or `docker-compose.yml` points to the correct path *inside the container* (e.g., `/usr/local/bin/orcaslicer`). Set `SLICER_TYPE` to `orcaslicer` as well.
187
+
188
+ Apologies for not including a specific slicer out-of-the-box, but given the wide variety of slicers (PrusaSlicer, OrcaSlicer, Cura, etc.) and configurations available, pre-installing one would unnecessarily bloat the image for many users. If a particular slicer becomes a very common request, I can certainly look into adding official support for it in a future version.
189
+
71
190
  ## Configuration
72
191
 
73
192
  Create a `.env` file in the directory where you'll run the server or set environment variables:
@@ -78,20 +197,26 @@ API_KEY=your_api_key_here
78
197
 
79
198
  # Default printer connection settings
80
199
  PRINTER_HOST=localhost
81
- PRINTER_PORT=80
82
- PRINTER_TYPE=octoprint # Options: octoprint, klipper, duet, repetier, bambu
200
+ PRINTER_PORT=80 # Port for non-Bambu HTTP APIs
201
+ PRINTER_TYPE=octoprint # Options: octoprint, klipper, duet, repetier, bambu, prusa, creality
83
202
 
84
203
  # Optional: Directory for temporary files
85
204
  TEMP_DIR=/path/to/temp/dir
86
205
 
87
206
  # Bambu Labs specific configuration
88
- BAMBU_SERIAL=your_printer_serial
89
- BAMBU_TOKEN=your_access_token
207
+ BAMBU_SERIAL=your_printer_serial # REQUIRED for Bambu
208
+ BAMBU_TOKEN=your_access_token # REQUIRED for Bambu
90
209
 
91
- # Slicer configuration
92
- SLICER_TYPE=prusaslicer # Options: prusaslicer, cura, slic3r
210
+ # Slicer configuration (for slice_stl tool)
211
+ SLICER_TYPE=prusaslicer # Options: prusaslicer, cura, slic3r, orcaslicer
93
212
  SLICER_PATH=/path/to/slicer/executable
94
213
  SLICER_PROFILE=/path/to/slicer/profile
214
+
215
+ # Optional: Path to Bambu Studio user config dir (for loading presets)
216
+ # Example macOS: /Users/your_user/Library/Application Support/BambuStudio/user/YOUR_USER_ID
217
+ # Example Windows: C:\Users\your_user\AppData\Roaming\BambuStudio\user\YOUR_USER_ID
218
+ # Example Linux: /home/your_user/.config/BambuStudio/user/YOUR_USER_ID
219
+ BAMBU_STUDIO_CONFIG_PATH=
95
220
  ```
96
221
 
97
222
  ## Usage with Claude Desktop
@@ -168,7 +293,7 @@ Repetier-Server is a host software for 3D printers.
168
293
 
169
294
  Bambu Lab printers use MQTT for status and control and FTP for file operations.
170
295
 
171
- - Authentication: Serial number and access token required
296
+ - Authentication: Serial number and access token required (set `BAMBU_SERIAL` and `BAMBU_TOKEN`)
172
297
  - Requirements: Printer must be on the same network or have cloud connection enabled
173
298
  - Compatible with: X1C, P1S, P1P, A1, and other Bambu Lab printers
174
299
 
@@ -188,6 +313,11 @@ To connect to your Bambu Lab printer, you need two things:
188
313
 
189
314
  **Note**: If your printer is not on the same local network or you can't find the access token, you may need to update your printer's firmware to the latest version to enable LAN Mode.
190
315
 
316
+ #### Bambu Communication Notes (MQTT & FTP)
317
+
318
+ - **MQTT:** This server uses the local MQTT protocol (port 8883, TLS) based on community findings (e.g., [OpenBambuAPI](https://github.com/Doridian/OpenBambuAPI)) to send commands like starting prints and cancelling jobs.
319
+ - **FTP:** File listing and uploading currently rely on the FTP server running on the printer (via the `bambu-js` library helper). **Note:** This FTP connection might be **unsecured (plain FTP)** based on current library limitations. Use with awareness of your network security.
320
+
191
321
  ### Prusa Connect
192
322
 
193
323
  Prusa Connect is Prusa's own cloud-based solution for managing their printers.
@@ -220,6 +350,9 @@ Creality Cloud is Creality's management system for their printers.
220
350
 
221
351
  ## Available Tools
222
352
 
353
+ <details>
354
+ <summary>Click to expand STL Manipulation Tools</summary>
355
+
223
356
  ### STL Manipulation Tools
224
357
 
225
358
  > **Memory Usage Warning**: The following STL manipulation tools load entire 3D models into memory. For large or complex STL files (>10MB), these operations can consume significant memory. When using these tools within the MCP environment, be mindful of memory constraints.
@@ -293,6 +426,37 @@ Move an STL model along specific axes (in millimeters).
293
426
  }
294
427
  ```
295
428
 
429
+ #### merge_vertices
430
+
431
+ Merge vertices that are closer than a specified tolerance. Helps close small gaps and can slightly simplify the mesh.
432
+
433
+ ```json
434
+ {
435
+ "stl_path": "/path/to/model.stl",
436
+ "tolerance": 0.01 // Optional, default = 0.01mm
437
+ }
438
+ ```
439
+
440
+ #### center_model
441
+
442
+ Translate the model so the center of its bounding box is at the world origin (0,0,0).
443
+
444
+ ```json
445
+ {
446
+ "stl_path": "/path/to/model.stl"
447
+ }
448
+ ```
449
+
450
+ #### lay_flat
451
+
452
+ Attempt to identify the largest flat surface of the model (that isn't already facing directly up or down) and rotate the model so this face is oriented downwards on the XY plane (Z=0). Useful for orienting models for printing.
453
+
454
+ ```json
455
+ {
456
+ "stl_path": "/path/to/model.stl"
457
+ }
458
+ ```
459
+
296
460
  #### modify_stl_section
297
461
 
298
462
  Apply a specific transformation to a selected section of an STL file. This allows for detailed modifications of specific parts of a model.
@@ -380,6 +544,13 @@ Process an STL file (extend base), slice it, confirm temperatures, and start pri
380
544
  }
381
545
  ```
382
546
 
547
+ **Note:** Automatic orientation for optimal printing (minimizing supports, etc.) is a complex task typically handled by slicer GUIs (like OrcaSlicer or PrusaSlicer) and is not implemented in this server.
548
+
549
+ </details>
550
+
551
+ <details>
552
+ <summary>Click to expand Printer Control Tools</summary>
553
+
383
554
  ### Printer Control Tools
384
555
 
385
556
  #### get_printer_status
@@ -394,18 +565,9 @@ Get the current status of the 3D printer.
394
565
  }
395
566
  ```
396
567
 
397
- For Bambu printers:
398
-
399
- ```json
400
- {
401
- "host": "192.168.1.100",
402
- "type": "bambu",
403
- "bambu_serial": "YOUR_PRINTER_SERIAL",
404
- "bambu_token": "YOUR_ACCESS_TOKEN"
405
- }
406
- ```
568
+ For Bambu printers, this currently only confirms MQTT connection.
407
569
 
408
- ### list_printer_files
570
+ #### list_printer_files
409
571
 
410
572
  List files available on the printer.
411
573
 
@@ -417,7 +579,9 @@ List files available on the printer.
417
579
  }
418
580
  ```
419
581
 
420
- ### upload_gcode
582
+ For Bambu printers, lists files in the `gcodes` directory via FTP.
583
+
584
+ #### upload_gcode
421
585
 
422
586
  Upload a G-code file to the printer.
423
587
 
@@ -432,7 +596,9 @@ Upload a G-code file to the printer.
432
596
  }
433
597
  ```
434
598
 
435
- ### start_print
599
+ For Bambu printers, uploads to the `gcodes` directory via FTP. Cannot start print automatically.
600
+
601
+ #### start_print
436
602
 
437
603
  Start printing a file that is already on the printer.
438
604
 
@@ -445,7 +611,9 @@ Start printing a file that is already on the printer.
445
611
  }
446
612
  ```
447
613
 
448
- ### cancel_print
614
+ **Not recommended for Bambu printers.** Use `print_3mf` for Bambu `.3mf` files.
615
+
616
+ #### cancel_print
449
617
 
450
618
  Cancel the current print job.
451
619
 
@@ -457,7 +625,9 @@ Cancel the current print job.
457
625
  }
458
626
  ```
459
627
 
460
- ### set_printer_temperature
628
+ For Bambu printers, sends the `stop_print` command via MQTT.
629
+
630
+ #### set_printer_temperature
461
631
 
462
632
  Set the temperature of a printer component.
463
633
 
@@ -471,11 +641,68 @@ Set the temperature of a printer component.
471
641
  }
472
642
  ```
473
643
 
644
+ **Not supported for Bambu printers** via direct MQTT commands.
645
+
646
+ </details>
647
+
648
+ <details open>
649
+ <summary>Click to expand Bambu-Specific Tools</summary>
650
+
651
+ ### Bambu-Specific Tools
652
+
653
+ #### print_3mf
654
+
655
+ Uploads a `.3mf` file to a Bambu printer via FTP and initiates the print job via an MQTT command. Allows overriding some print parameters like AMS mapping.
656
+
657
+ ```json
658
+ {
659
+ "three_mf_path": "/path/to/your_model.3mf",
660
+ "host": "your_bambu_ip", // Optional if default is set
661
+ "bambu_serial": "YOUR_SERIAL", // Optional if default is set
662
+ "bambu_token": "YOUR_TOKEN", // Optional if default is set
663
+ // Optional Overrides:
664
+ "use_ams": true, // Default: true
665
+ "ams_mapping": [0, 1, 2, 3], // Array of AMS slot indices to use
666
+ "bed_leveling": true, // Default: true
667
+ "flow_calibration": false, // Default: false
668
+ "vibration_calibration": false, // Default: false
669
+ "timelapse": false // Default: false
670
+ }
671
+ ```
672
+
673
+ **Note:** Overriding slicer settings like layer height or temperature via this tool is not supported by the printer's MQTT command. Apply those changes before generating the `.3mf` file.
674
+
675
+ </details>
676
+
474
677
  ## Available Resources
475
678
 
476
- - `printer://{host}/status` - Current status of the 3D printer
477
- - `printer://{host}/files` - List of files available on the 3D printer
478
- - `printer://{host}/file/{filename}` - Content of a specific file on the 3D printer
679
+ <details>
680
+ <summary>Click to expand Printer Resources</summary>
681
+
682
+ ### Printer Resources
683
+
684
+ - `printer://{host}/status` - Current status of the 3D printer (limited for Bambu currently)
685
+ - `printer://{host}/files` - List of files available on the 3D printer (FTP for Bambu)
686
+ - `printer://{host}/file/{filename}` - Content of a specific G-code file (checks existence only for Bambu)
687
+
688
+ </details>
689
+
690
+ <details open>
691
+ <summary>Click to expand Bambu Preset Resources</summary>
692
+
693
+ ### Bambu Preset Resources
694
+
695
+ If the `BAMBU_STUDIO_CONFIG_PATH` environment variable is set to your Bambu Studio user settings directory, you can read your saved presets.
696
+
697
+ - `preset://bambu/machine/{preset_name}` - Reads a machine preset file (e.g., `Bambu Lab P1S 0.4 nozzle.json`)
698
+ - `preset://bambu/filament/{preset_name}` - Reads a filament preset file (e.g., `Generic PLA.json`)
699
+ - `preset://bambu/process/{preset_name}` - Reads a process preset file (e.g., `0.20mm Standard @BBL P1S.json`)
700
+
701
+ **Example Usage:**
702
+ "Read the content of my Bambu process preset named '0.16mm Optimal @BBL P1S'"
703
+ (Claude would call ReadResource with `preset://bambu/process/0.16mm%20Optimal%20%40BBL%20P1S`)
704
+
705
+ </details>
479
706
 
480
707
  ## Example Commands for Claude
481
708
 
@@ -505,6 +732,11 @@ Here are some example commands you can give to Claude after connecting the MCP s
505
732
  - "Slice the modified STL file using PrusaSlicer."
506
733
  - "Confirm that the temperatures in the G-code are 200Β°C for the extruder and 60Β°C for the bed."
507
734
  - "Process this STL file, make the base 2 inches longer, slice it, and start printing, but confirm the temperatures first."
735
+ - "Print `~/Downloads/my_model.3mf` on the Bambu printer."
736
+ - "Upload `~/Desktop/calibration_cube.3mf` to the Bambu printer using AMS slots 0 and 2, and turn off bed leveling."
737
+ - "Cancel the print job on my Bambu P1S."
738
+ - "What are the settings in my Bambu filament preset 'Generic PETG'?"
739
+ - "Show me my Bambu process presets."
508
740
 
509
741
  ## Bambu Lab Printer Limitations
510
742
 
@@ -516,6 +748,12 @@ Due to the nature of the Bambu Lab printer API, there are some limitations:
516
748
 
517
749
  3. **File management**: Files must be uploaded to the "gcodes" directory on the printer.
518
750
 
751
+ 4. **FTP Security:** File operations currently use the printer's FTP server, which may be unsecured (plain FTP).
752
+
753
+ 5. **Parameter Overrides:** Only parameters supported by the MQTT `project_file` command can be overridden via the `print_3mf` tool (e.g., AMS usage, calibration flags). Slicer settings like layer height or temperature cannot be changed at print time via this command.
754
+
755
+ 6. **Status Updates:** Full real-time status monitoring via MQTT needs further implementation.
756
+
519
757
  ## Limitations and Considerations
520
758
 
521
759
  ### Memory Usage
@@ -553,7 +791,7 @@ Due to the nature of the Bambu Lab printer API, there are some limitations:
553
791
  | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com) | We welcome contributions via Pull Requests |
554
792
  | [![Node.js Version](https://img.shields.io/badge/node-%3E%3D%2018.0.0-green.svg)](https://nodejs.org/en/download/) | Requires Node.js 18.0.0 or higher |
555
793
  | [![Downloads](https://img.shields.io/npm/dm/mcp-3d-printer-server.svg)](https://www.npmjs.com/package/mcp-3d-printer-server) | Number of downloads per month from npm |
556
- | [![GitHub stars](https://img.shields.io/github/stars/yourusername/mcp-3d-printer-server.svg?style=social&label=Star)](https://github.com/yourusername/mcp-3d-printer-server) | Number of GitHub stars this project has received |
794
+ | [![GitHub stars](https://img.shields.io/github/stars/dmontgomery40/mcp-3d-printer-server.svg?style=social&label=Star)](https://github.com/yourusername/mcp-3d-printer-server) | Number of GitHub stars this project has received |
557
795
 
558
796
  ## License
559
797
 
@@ -0,0 +1,3 @@
1
+ import { ThreeMFData } from './types.js';
2
+ export { ThreeMFData };
3
+ export declare function parse3MF(filePath: string): Promise<ThreeMFData>;
@@ -0,0 +1,216 @@
1
+ import JSZip from 'jszip';
2
+ import { parseStringPromise } from 'xml2js';
3
+ import * as fs from 'fs/promises';
4
+ // Parses Bambu Studio's JSON config data
5
+ function parseBambuJSONConfig(jsonData) {
6
+ const config = {};
7
+ const amsMapping = {};
8
+ // Map known keys, converting types as needed
9
+ if (jsonData.layer_height !== undefined)
10
+ config.layer_height = parseFloat(jsonData.layer_height);
11
+ if (jsonData.initial_layer_print_height !== undefined)
12
+ config.first_layer_height = parseFloat(jsonData.initial_layer_print_height);
13
+ if (jsonData.sparse_infill_density !== undefined)
14
+ config.sparse_infill_density = parseFloat(jsonData.sparse_infill_density);
15
+ if (jsonData.sparse_infill_pattern !== undefined)
16
+ config.sparse_infill_pattern = String(jsonData.sparse_infill_pattern);
17
+ if (jsonData.enable_support !== undefined)
18
+ config.support_enabled = String(jsonData.enable_support) === '1' || String(jsonData.enable_support).toLowerCase() === 'true';
19
+ if (jsonData.support_type !== undefined)
20
+ config.support_type = String(jsonData.support_type);
21
+ if (jsonData.support_angle !== undefined)
22
+ config.support_threshold_angle = parseFloat(jsonData.support_angle);
23
+ if (jsonData.raft_layers !== undefined)
24
+ config.raft_layers = parseInt(String(jsonData.raft_layers), 10);
25
+ if (jsonData.brim_width !== undefined)
26
+ config.brim_width = parseFloat(jsonData.brim_width);
27
+ if (jsonData.wall_loops !== undefined)
28
+ config.wall_loops = parseInt(String(jsonData.wall_loops), 10);
29
+ if (jsonData.top_shell_layers !== undefined)
30
+ config.top_shell_layers = parseInt(String(jsonData.top_shell_layers), 10);
31
+ if (jsonData.bottom_shell_layers !== undefined)
32
+ config.bottom_shell_layers = parseInt(String(jsonData.bottom_shell_layers), 10);
33
+ // Temperatures (handle potential arrays)
34
+ if (Array.isArray(jsonData.nozzle_temperature) && jsonData.nozzle_temperature.length > 0) {
35
+ config.nozzle_temperature = jsonData.nozzle_temperature.map((t) => parseFloat(String(t)));
36
+ }
37
+ else if (jsonData.nozzle_temperature !== undefined) {
38
+ config.nozzle_temperature = [parseFloat(String(jsonData.nozzle_temperature))];
39
+ }
40
+ // Bed Temp (prefer initial layer temp)
41
+ let bedTempKey = jsonData.hot_plate_temp_initial_layer !== undefined ? 'hot_plate_temp_initial_layer' : 'hot_plate_temp';
42
+ if (jsonData[bedTempKey] !== undefined) {
43
+ if (Array.isArray(jsonData[bedTempKey]) && jsonData[bedTempKey].length > 0) {
44
+ config.bed_temperature = parseFloat(String(jsonData[bedTempKey][0]));
45
+ }
46
+ else {
47
+ config.bed_temperature = parseFloat(String(jsonData[bedTempKey]));
48
+ }
49
+ }
50
+ // Filaments and Flow (handle potential arrays)
51
+ if (Array.isArray(jsonData.filament_type) && jsonData.filament_type.length > 0) {
52
+ config.filament_type = jsonData.filament_type.map(String);
53
+ }
54
+ if (Array.isArray(jsonData.filament_flow_ratio) && jsonData.filament_flow_ratio.length > 0) {
55
+ config.flow_ratio = jsonData.filament_flow_ratio.map((f) => parseFloat(String(f)));
56
+ }
57
+ // AMS Mapping (derive from filament_settings_id order)
58
+ if (Array.isArray(jsonData.filament_settings_id) && jsonData.filament_settings_id.length > 0) {
59
+ jsonData.filament_settings_id.forEach((filamentId, index) => {
60
+ // Map filament ID (e.g., "Generic PLA @BBL P1P") to its index (assumed AMS slot)
61
+ if (filamentId) { // Ensure filamentId is not empty
62
+ amsMapping[filamentId] = index;
63
+ }
64
+ });
65
+ if (Object.keys(amsMapping).length > 0) {
66
+ config.ams_mapping = amsMapping;
67
+ }
68
+ }
69
+ // Store remaining keys found in the config
70
+ for (const key in jsonData) {
71
+ if (!config.hasOwnProperty(key)) {
72
+ // Simple assignment for remaining keys, preserving original type if possible
73
+ config[key] = jsonData[key];
74
+ }
75
+ }
76
+ return config;
77
+ }
78
+ async function parseBambuConfig(zip) {
79
+ // Look for common Bambu config file names
80
+ const potentialFiles = ['Metadata/project_settings.config', 'Metadata/model_settings.config', 'Metadata/slice_info.config'];
81
+ let configFile = null;
82
+ let configContent = '';
83
+ for (const name of potentialFiles) {
84
+ const file = zip.file(name);
85
+ if (file) {
86
+ configFile = file;
87
+ configContent = await file.async('string');
88
+ console.log(`Found Bambu config file: ${configFile.name}`);
89
+ break; // Use the first one found (project usually has the most)
90
+ }
91
+ }
92
+ if (configFile && configContent) {
93
+ try {
94
+ // Attempt to parse as JSON
95
+ const jsonData = JSON.parse(configContent);
96
+ const parsedConfig = parseBambuJSONConfig(jsonData);
97
+ console.log('Bambu config parsed successfully as JSON.');
98
+ return parsedConfig;
99
+ }
100
+ catch (jsonError) {
101
+ console.warn(`Failed to parse ${configFile.name} as JSON: ${jsonError.message}. Attempting INI parse as fallback...`);
102
+ // Fallback: Try parsing as INI if JSON fails (though unlikely based on sample)
103
+ try {
104
+ const parsedConfig = parseFallbackINIConfig(configContent); // Keep a simple INI parser as fallback
105
+ console.log('Bambu config parsed successfully as INI (fallback).');
106
+ return parsedConfig;
107
+ }
108
+ catch (iniError) {
109
+ console.error(`Error parsing Bambu config ${configFile.name} as INI (fallback):`, iniError);
110
+ return {};
111
+ }
112
+ }
113
+ }
114
+ else {
115
+ console.log('No Bambu-specific config file found in Metadata directory.');
116
+ return {};
117
+ }
118
+ }
119
+ // Fallback INI parser (Simplified version of the previous one)
120
+ function parseFallbackINIConfig(configContent) {
121
+ const config = {};
122
+ const lines = configContent.split(/\r?\n/);
123
+ for (const line of lines) {
124
+ const trimmedLine = line.trim();
125
+ if (!trimmedLine || trimmedLine.startsWith('#') || trimmedLine.startsWith(';'))
126
+ continue;
127
+ const equalsIndex = trimmedLine.indexOf('=');
128
+ if (equalsIndex === -1)
129
+ continue;
130
+ const key = trimmedLine.substring(0, equalsIndex).trim();
131
+ const value = trimmedLine.substring(equalsIndex + 1).trim();
132
+ // Simple assignment - no type conversion here for fallback
133
+ config[key] = value;
134
+ }
135
+ return config;
136
+ }
137
+ async function parse3DModelConfig(zip) {
138
+ const modelFile = zip.file('3D/3dmodel.model');
139
+ if (!modelFile) {
140
+ throw new Error('3D/3dmodel.model not found in 3MF archive');
141
+ }
142
+ const modelContent = await modelFile.async('string');
143
+ try {
144
+ const parsedXml = await parseStringPromise(modelContent, {
145
+ explicitArray: false,
146
+ mergeAttrs: true,
147
+ charkey: 'value'
148
+ });
149
+ const modelData = parsedXml.model;
150
+ if (!modelData) {
151
+ throw new Error('Invalid 3dmodel.model format: <model> tag not found.');
152
+ }
153
+ const metadata = { unit: modelData.unit || 'millimeter' };
154
+ if (modelData.metadata) {
155
+ const metas = Array.isArray(modelData.metadata) ? modelData.metadata : [modelData.metadata];
156
+ metas.forEach((meta) => {
157
+ if (meta.name && meta.value !== undefined) {
158
+ metadata[meta.name] = String(meta.value);
159
+ }
160
+ });
161
+ }
162
+ const objects = [];
163
+ if (modelData.resources?.object) {
164
+ const resources = Array.isArray(modelData.resources.object) ? modelData.resources.object : [modelData.resources.object];
165
+ resources.forEach((objResource) => {
166
+ objects.push({
167
+ id: objResource.id,
168
+ name: objResource.name,
169
+ type: objResource.type || 'model'
170
+ // Actual mesh data (vertices, triangles) is nested deeper if needed
171
+ });
172
+ });
173
+ }
174
+ const buildItems = [];
175
+ if (modelData.build?.item) {
176
+ const items = Array.isArray(modelData.build.item) ? modelData.build.item : [modelData.build.item];
177
+ items.forEach((item) => {
178
+ buildItems.push({
179
+ objectId: item.objectid,
180
+ transform: item.transform
181
+ });
182
+ });
183
+ }
184
+ console.log('3dmodel.model parsed successfully.');
185
+ return { metadata, objects, build: { items: buildItems } };
186
+ }
187
+ catch (error) {
188
+ console.error('Error parsing 3dmodel.model XML:', error);
189
+ throw new Error(`Failed to parse 3dmodel.model: ${error.message}`);
190
+ }
191
+ }
192
+ export async function parse3MF(filePath) {
193
+ console.log(`Parsing 3MF file: ${filePath}`);
194
+ try {
195
+ const data = await fs.readFile(filePath);
196
+ const zip = await JSZip.loadAsync(data);
197
+ // List files for debugging
198
+ // const fileList = Object.keys(zip.files);
199
+ // console.log('Files in 3MF:', fileList);
200
+ const { metadata, objects, build } = await parse3DModelConfig(zip);
201
+ const bambuConfig = await parseBambuConfig(zip);
202
+ // Combine data into the final structure
203
+ const combinedData = {
204
+ metadata,
205
+ objects,
206
+ build,
207
+ slicerConfig: bambuConfig
208
+ };
209
+ console.log('3MF parsing completed.');
210
+ return combinedData;
211
+ }
212
+ catch (error) {
213
+ console.error(`Error parsing 3MF file ${filePath}:`, error);
214
+ throw new Error(`Failed to parse 3MF file: ${error.message}`);
215
+ }
216
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};