bambu-printer-mcp 1.1.2 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ A Bambu Lab-focused MCP server for controlling Bambu printers, manipulating STL
11
11
 
12
12
  This is a stripped-down, Bambu-only fork of [mcp-3D-printer-server](https://github.com/DMontgomery40/mcp-3D-printer-server). All OctoPrint, Klipper, Duet, Repetier, Prusa Connect, and Creality Cloud support has been removed. What remains is a focused, lean implementation for Bambu Lab hardware.
13
13
 
14
- Local handoff note: see [REMOTE-DEPLOYMENT.md](./REMOTE-DEPLOYMENT.md) for the custom H2D/H2S patches, per-printer MCP split, and remote deployment plan used in this clone.
14
+ Local handoff note: see [REMOTE-DEPLOYMENT.md](./REMOTE-DEPLOYMENT.md) for the custom H2D/H2S/H2C patches, per-printer MCP split, and remote deployment plan used in this clone.
15
15
 
16
16
  ---
17
17
 
@@ -29,7 +29,7 @@ This fork adds a substantial set of printer control tools beyond the upstream `m
29
29
  - **Skip objects** (`skip_objects`) — skip specific object IDs during a running multi-object print. IDs from `list_3mf_plate_objects`.
30
30
  - **HMS diagnostics** (`printer://{host}/hms` MCP resource) — read-only error summary with automatic settle retry.
31
31
  - **Utility controls** — `set_print_speed` (silent/standard/sport/ludicrous), `clear_hms_errors`, `reread_ams_rfid`, `set_airduct_mode` (cooling/heating for H2/P2).
32
- - **H2/H2D-safe print path** — correct `project_file` format with `ams_mapping2` parallel array, H2 firmware quirks handled.
32
+ - **H2-family-safe print path** — correct `project_file` format with `ams_mapping2` parallel array, H2 firmware quirks handled.
33
33
  - **BambuStudio CLI auto-flatten** (`BAMBU_CLI_FLATTEN=true`) — works around upstream profile inheritance bugs.
34
34
  - **Print collar charm** (`print_collar_charm`) — specialized two-color wrapper with fixed tray policy.
35
35
 
@@ -100,8 +100,8 @@ This fork adds a substantial set of printer control tools beyond the upstream `m
100
100
  - List, upload, and delete files on the printer's SD card via FTPS
101
101
  - Capture a JPEG snapshot from the chamber camera. Supports A1, A1 mini, P1S, P1P (TCP-on-6000), and X1, X1C, X1E, P2S, H2, H2S, H2D, H2C, H2D Pro (RTSP via ffmpeg). Requires ffmpeg in PATH for the RTSP path.
102
102
  - Upload and print pre-sliced `.gcode.3mf` files with full plate selection and calibration flag control (recommended path — see [docs/SLICING.md](./docs/SLICING.md))
103
- - Optional single-color auto-slice path via BambuStudio CLI. Set `BAMBU_CLI_FLATTEN=true` to enable a workaround that flattens BBL profile inheritance before invoking the CLI — works around upstream bugs in BambuStudio CLI mode ([#9636](https://github.com/bambulab/BambuStudio/issues/9636), [#9968](https://github.com/bambulab/BambuStudio/issues/9968)). Single-color smoke is verified on H2S/H2D/X1C/P1S. H2D two-color CLI slicing is blocked upstream ([#10408](https://github.com/bambulab/BambuStudio/issues/10408)); use a GUI-sliced `.gcode.3mf` for that workflow. Default off; Path A (GUI-slice) remains the recommended workflow for non-BBL profiles, multi-color H2D jobs, or first-time prints. See [docs/SLICING.md](./docs/SLICING.md).
104
- - Parse AMS mapping from the 3MF's embedded slicer metadata (`Metadata/plate_<n>.json` + gcode filament header) and send it correctly formatted per the OpenBambuAPI spec, with correct H2S/H2D `ams_mapping2` parallel array format
103
+ - Optional single-color auto-slice path via BambuStudio CLI. Set `BAMBU_CLI_FLATTEN=true` to enable a workaround that flattens BBL profile inheritance before invoking the CLI — works around upstream bugs in BambuStudio CLI mode ([#9636](https://github.com/bambulab/BambuStudio/issues/9636), [#9968](https://github.com/bambulab/BambuStudio/issues/9968)). Single-color smoke is verified on H2S/H2D/X1C/P1S; H2C requires Bambu Studio 2.4.0 or newer and should use `BAMBU_MODEL=h2c`, not an H2D fallback. H2D two-color CLI slicing is blocked upstream ([#10408](https://github.com/bambulab/BambuStudio/issues/10408)); use a GUI-sliced `.gcode.3mf` for that workflow. Default off; Path A (GUI-slice) remains the recommended workflow for non-BBL profiles, multi-color H2 jobs, or first-time prints. See [docs/SLICING.md](./docs/SLICING.md).
104
+ - Parse AMS mapping from the 3MF's embedded slicer metadata (`Metadata/plate_<n>.json` + gcode filament header) and send it correctly formatted per the OpenBambuAPI spec, with correct H2S/H2D/H2C `ams_mapping2` parallel array format
105
105
  - **Auto-match AMS slots by RFID** (`auto_match_ams` flag on `print_3mf`). Resolves required `tray_info_idx` from the sliced 3MF against live AMS inventory. Handles same-SKU different-color filaments by matching on `(tray_info_idx, tray_color)` and tracking already-claimed slots. Dry-run with `resolve_3mf_ams_slots` before printing.
106
106
  - Cancel, pause, and resume in-progress print jobs via MQTT
107
107
  - Skip specific objects during a running multi-object print via `skip_objects` (use `list_3mf_plate_objects` to find object IDs first)
@@ -178,7 +178,7 @@ BAMBU_TOKEN=your_access_token # LAN access token from printer touchscreen
178
178
  # BAMBU_PRINTER_HOST / BAMBU_PRINTER_SERIAL / BAMBU_PRINTER_ACCESS_TOKEN
179
179
 
180
180
  # --- Printer model (CRITICAL for safe operation) ---
181
- BAMBU_MODEL=p1s # Your printer model: p1s, p1p, x1c, x1e, a1, a1mini, h2d, h2s
181
+ BAMBU_MODEL=p1s # Your printer model: p1s, p1p, p2s, x1c, x1e, a1, a1mini, h2d, h2s, h2c
182
182
  # Alias also accepted: BAMBU_PRINTER_MODEL
183
183
  BED_TYPE=textured_plate # Bed plate type: textured_plate, cool_plate, engineering_plate, hot_plate, supertack_plate
184
184
  NOZZLE_DIAMETER=0.4 # Nozzle diameter in mm (default: 0.4)
@@ -215,7 +215,7 @@ BLENDER_MCP_BRIDGE_COMMAND= # Shell command to invoke your Blender MCP bri
215
215
  | `PRINTER_HOST` | `localhost` | Yes | IP address of the Bambu printer. Alias: `BAMBU_PRINTER_HOST` |
216
216
  | `BAMBU_SERIAL` | | Yes | Printer serial number. Alias: `BAMBU_PRINTER_SERIAL` |
217
217
  | `BAMBU_TOKEN` | | Yes | LAN access token. Alias: `BAMBU_PRINTER_ACCESS_TOKEN` |
218
- | `BAMBU_MODEL` | | **Yes** | Printer model: `p1s`, `p1p`, `x1c`, `x1e`, `a1`, `a1mini`, `h2d`, `h2s`. **Required for safe operation** -- determines the correct G-code generation. Alias: `BAMBU_PRINTER_MODEL`. If omitted and the MCP client supports elicitation, the server will ask you interactively. |
218
+ | `BAMBU_MODEL` | | **Yes** | Printer model: `p1s`, `p1p`, `p2s`, `x1c`, `x1e`, `a1`, `a1mini`, `h2d`, `h2s`, `h2c`. **Required for safe operation** -- determines the correct G-code generation. Alias: `BAMBU_PRINTER_MODEL`. If omitted and the MCP client supports elicitation, the server will ask you interactively. Use `h2c` for H2C; do not use `h2d` as a fallback. |
219
219
  | `BED_TYPE` | `textured_plate` | No | Bed plate type: `textured_plate`, `cool_plate`, `engineering_plate`, `hot_plate`, `supertack_plate` |
220
220
  | `NOZZLE_DIAMETER` | `0.4` | No | Nozzle diameter in mm. Used to select the correct BambuStudio machine preset. |
221
221
  | `SLICER_TYPE` | `bambustudio` | No | Slicer to use for slicing operations |
@@ -230,7 +230,7 @@ BLENDER_MCP_BRIDGE_COMMAND= # Shell command to invoke your Blender MCP bri
230
230
  | `MCP_HTTP_JSON_RESPONSE` | `true` | No | Return structured JSON alongside text responses |
231
231
  | `MCP_HTTP_ALLOWED_ORIGINS` | | No | Comma-separated list of allowed CORS origins |
232
232
  | `BLENDER_MCP_BRIDGE_COMMAND` | | No | Command to invoke Blender MCP bridge |
233
- | `BAMBU_CLI_FLATTEN` | `false` | No | When `true`, the MCP flattens BBL profile inheritance before invoking the BambuStudio CLI. Workaround for upstream issues [#9636](https://github.com/bambulab/BambuStudio/issues/9636) / [#9968](https://github.com/bambulab/BambuStudio/issues/9968). BBL printers only. Single-color smoke verified on H2S/H2D/X1C/P1S; H2D two-color CLI slicing remains blocked by [#10408](https://github.com/bambulab/BambuStudio/issues/10408). See [docs/SLICING.md](./docs/SLICING.md). |
233
+ | `BAMBU_CLI_FLATTEN` | `false` | No | When `true`, the MCP flattens BBL profile inheritance before invoking the BambuStudio CLI. Workaround for upstream issues [#9636](https://github.com/bambulab/BambuStudio/issues/9636) / [#9968](https://github.com/bambulab/BambuStudio/issues/9968). BBL printers only. Single-color smoke verified on H2S/H2D/X1C/P1S; H2C requires Bambu Studio 2.4.0 or newer. H2D two-color CLI slicing remains blocked by [#10408](https://github.com/bambulab/BambuStudio/issues/10408). See [docs/SLICING.md](./docs/SLICING.md). |
234
234
  | `BAMBU_PROFILES_ROOT` | derived from `SLICER_PATH` | No | Override path to the BambuStudio `Resources/profiles` directory used by the CLI flattener. Useful for non-standard installs or dev environments. |
235
235
 
236
236
  SuperTack can be passed for pre-sliced print jobs, but BambuStudio CLI slicing currently fails fast for `supertack_plate` because the accepted CLI bed identifier is not verified. Use a pre-sliced 3MF for SuperTack until this is confirmed.
@@ -513,7 +513,7 @@ This fork sends the `project_file` command directly via `bambu-node` (bypassing
513
513
  // P1/A1/X1-series: 5-element project lookup table
514
514
  ams_mapping = [0, -1, -1, -1, -1];
515
515
 
516
- // H2S/H2D: project-length lookup table + parallel ams_mapping2
516
+ // H2S/H2D/H2C: project-length lookup table + parallel ams_mapping2
517
517
  ams_mapping = [-1, 1, -1, -1];
518
518
  ams_mapping2 = [
519
519
  { ams_id: 255, slot_id: 255 },
@@ -1026,7 +1026,7 @@ The primary tool for starting a Bambu print. **Recommended input: a pre-sliced `
1026
1026
  3. Parses the sliced 3MF to extract the correct plate file and compute its MD5 hash.
1027
1027
  4. Also parses `Metadata/project_settings.config` to read AMS mapping embedded by Bambu Studio.
1028
1028
  5. Uploads the 3MF to the printer's `cache/` directory via FTPS using `basic-ftp` directly (avoiding the bambu-js double-path bug).
1029
- 6. Sends the correct MQTT print command for the target printer family. For H2S/H2D that means `project_file` with project-length `ams_mapping`, parallel `ams_mapping2`, and H2-compatible calibration flags.
1029
+ 6. Sends the correct MQTT print command for the target printer family. For H2S/H2D/H2C that means `project_file` with project-length `ams_mapping`, parallel `ams_mapping2`, and H2-compatible calibration flags.
1030
1030
 
1031
1031
  ```json
1032
1032
  {
package/dist/index.js CHANGED
@@ -25,7 +25,8 @@ const DEFAULT_BAMBU_MODEL = process.env.BAMBU_PRINTER_MODEL?.trim().toLowerCase(
25
25
  "";
26
26
  const DEFAULT_BED_TYPE = process.env.BED_TYPE?.trim().toLowerCase() || "textured_plate";
27
27
  const DEFAULT_NOZZLE_DIAMETER = process.env.NOZZLE_DIAMETER?.trim() || "0.4";
28
- const VALID_BAMBU_MODELS = ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"];
28
+ const VALID_BAMBU_MODELS = ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s", "h2c"];
29
+ const H2_BAMBU_MODELS = new Set(["h2d", "h2s", "h2c"]);
29
30
  const VALID_BED_TYPES = ["textured_plate", "cool_plate", "engineering_plate", "hot_plate", "supertack_plate"];
30
31
  const VALID_BAMBUSTUDIO_CLI_BED_TYPES = ["textured_plate", "cool_plate", "engineering_plate", "hot_plate"];
31
32
  // Map model IDs to BambuStudio --load-machine preset names
@@ -39,6 +40,7 @@ const BAMBU_MODEL_PRESETS = {
39
40
  a1mini: (n) => `Bambu Lab A1 mini ${n} nozzle`,
40
41
  h2d: (n) => `Bambu Lab H2D ${n} nozzle`,
41
42
  h2s: (n) => `Bambu Lab H2S ${n} nozzle`,
43
+ h2c: (n) => `Bambu Lab H2C ${n} nozzle`,
42
44
  };
43
45
  const FILAMENT_PROFILE_DIR = "/Applications/BambuStudio.app/Contents/Resources/profiles/BBL/filament";
44
46
  const FILAMENT_MODEL_CODES = {
@@ -51,6 +53,7 @@ const FILAMENT_MODEL_CODES = {
51
53
  a1mini: "A1M",
52
54
  h2d: "H2D",
53
55
  h2s: "H2S",
56
+ h2c: "H2C",
54
57
  };
55
58
  const COLLAR_CHARM_POLICY = getCollarCharmRolePolicy();
56
59
  let filamentProfileIndexCache = null;
@@ -794,6 +797,7 @@ class BambuPrinterMCPServer {
794
797
  { const: "a1mini", title: "A1 Mini" },
795
798
  { const: "h2d", title: "H2D" },
796
799
  { const: "h2s", title: "H2S" },
800
+ { const: "h2c", title: "H2C" },
797
801
  ],
798
802
  },
799
803
  },
@@ -1311,7 +1315,7 @@ class BambuPrinterMCPServer {
1311
1315
  },
1312
1316
  bambu_model: {
1313
1317
  type: "string",
1314
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1318
+ enum: [...VALID_BAMBU_MODELS],
1315
1319
  description: "Optional model hint used to resolve Bambu/Orca filament profile JSONs for each tray."
1316
1320
  },
1317
1321
  nozzle_diameter: {
@@ -1331,7 +1335,7 @@ class BambuPrinterMCPServer {
1331
1335
  plate_index: { type: "number", description: "0-based plate index to inspect (default: 0)" },
1332
1336
  bambu_model: {
1333
1337
  type: "string",
1334
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1338
+ enum: [...VALID_BAMBU_MODELS],
1335
1339
  description: "Optional model hint used to resolve Bambu/Orca filament profile JSONs for each tray."
1336
1340
  },
1337
1341
  nozzle_diameter: { type: "string", description: "Nozzle diameter in mm (default: 0.4)" },
@@ -1472,7 +1476,7 @@ class BambuPrinterMCPServer {
1472
1476
  template_dir: { type: "string", description: "Optional template directory override when resolving template_name." },
1473
1477
  bambu_model: {
1474
1478
  type: "string",
1475
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1479
+ enum: [...VALID_BAMBU_MODELS],
1476
1480
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
1477
1481
  },
1478
1482
  slicer_type: {
@@ -1518,7 +1522,7 @@ class BambuPrinterMCPServer {
1518
1522
  stl_path: { type: "string", description: "Path to the STL or 3MF file to slice" },
1519
1523
  bambu_model: {
1520
1524
  type: "string",
1521
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1525
+ enum: [...VALID_BAMBU_MODELS],
1522
1526
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
1523
1527
  },
1524
1528
  slicer_type: {
@@ -1615,7 +1619,7 @@ class BambuPrinterMCPServer {
1615
1619
  three_mf_path: { type: "string", description: "Path to the 3MF file to print; unsliced 3MFs are auto-sliced before sending." },
1616
1620
  bambu_model: {
1617
1621
  type: "string",
1618
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1622
+ enum: [...VALID_BAMBU_MODELS],
1619
1623
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
1620
1624
  },
1621
1625
  connection_type: { type: "string", enum: ["cloud", "lan"], description: "BambuNetwork connection type to put in FULU PrintParams; cloud uses restored internet printing, lan uses local bridge printing." },
@@ -1730,7 +1734,7 @@ class BambuPrinterMCPServer {
1730
1734
  print: { type: "boolean", description: "Start printing after upload (default: false)" },
1731
1735
  bambu_model: {
1732
1736
  type: "string",
1733
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1737
+ enum: [...VALID_BAMBU_MODELS],
1734
1738
  description: "Required when print is true. Bambu Lab printer model used as a safety confirmation before starting the uploaded file."
1735
1739
  },
1736
1740
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
@@ -1749,7 +1753,7 @@ class BambuPrinterMCPServer {
1749
1753
  filename: { type: "string", description: "Name of the file to print" },
1750
1754
  bambu_model: {
1751
1755
  type: "string",
1752
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1756
+ enum: [...VALID_BAMBU_MODELS],
1753
1757
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Starting G-code for the wrong model can damage the printer."
1754
1758
  },
1755
1759
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
@@ -1768,7 +1772,7 @@ class BambuPrinterMCPServer {
1768
1772
  filename: { type: "string", description: "Name of the file to print" },
1769
1773
  bambu_model: {
1770
1774
  type: "string",
1771
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1775
+ enum: [...VALID_BAMBU_MODELS],
1772
1776
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Starting G-code for the wrong model can damage the printer."
1773
1777
  },
1774
1778
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
@@ -1981,7 +1985,7 @@ class BambuPrinterMCPServer {
1981
1985
  three_mf_path: { type: "string", description: "Path to the 3MF file to print" },
1982
1986
  bambu_model: {
1983
1987
  type: "string",
1984
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1988
+ enum: [...VALID_BAMBU_MODELS],
1985
1989
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
1986
1990
  },
1987
1991
  connection_mode: {
@@ -2051,8 +2055,8 @@ class BambuPrinterMCPServer {
2051
2055
  template_dir: { type: "string", description: "Optional template directory override when resolving template_name." },
2052
2056
  bambu_model: {
2053
2057
  type: "string",
2054
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2055
- description: "REQUIRED: Bambu Lab printer model. H2D and H2S are the primary intended paths."
2058
+ enum: [...VALID_BAMBU_MODELS],
2059
+ description: "REQUIRED: Bambu Lab printer model. H2D, H2S, and H2C are the primary intended paths."
2056
2060
  },
2057
2061
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
2058
2062
  bambu_serial: { type: "string", description: "Serial number (default: value from env)" },
@@ -2590,7 +2594,7 @@ class BambuPrinterMCPServer {
2590
2594
  console.warn("Could not check/slice 3MF, proceeding with original:", sliceCheckErr.message);
2591
2595
  }
2592
2596
  const parsed3MFData = await parse3MF(threeMFPath);
2593
- const isH2Print = printModel === "h2s" || printModel === "h2d";
2597
+ const isH2Print = H2_BAMBU_MODELS.has(printModel);
2594
2598
  let parsedAmsMapping;
2595
2599
  if (!isH2Print && parsed3MFData.slicerConfig?.ams_mapping) {
2596
2600
  const slots = normalizeAmsMappingObject(parsed3MFData.slicerConfig.ams_mapping);
@@ -2661,6 +2665,7 @@ class BambuPrinterMCPServer {
2661
2665
  result = await this.bambu.print3mf(host, bambuSerial, bambuToken, {
2662
2666
  projectName,
2663
2667
  filePath: threeMFPath,
2668
+ bambuModel: printModel,
2664
2669
  plateIndex,
2665
2670
  useAMS: useAMS,
2666
2671
  amsMapping: finalAmsMapping,
@@ -2696,6 +2701,7 @@ class BambuPrinterMCPServer {
2696
2701
  result = await this.bambu.print3mf(host, bambuSerial, bambuToken, {
2697
2702
  projectName,
2698
2703
  filePath: preparedThreeMFPath,
2704
+ bambuModel: printModel,
2699
2705
  plateIndex: collarAnalysis.plateIndex,
2700
2706
  useAMS: true,
2701
2707
  amsSlots: collarAnalysis.amsSlots,
@@ -1,6 +1,7 @@
1
1
  interface BambuPrintOptionsInternal {
2
2
  projectName: string;
3
3
  filePath: string;
4
+ bambuModel?: string;
4
5
  useAMS?: boolean;
5
6
  plateIndex?: number;
6
7
  bedType?: string;
@@ -94,7 +95,7 @@ export declare class BambuImplementation {
94
95
  * [16..16+payloadSize] JPEG (FF D8 ... FF D9)
95
96
  *
96
97
  * Verified models per upstream docs: A1, A1 mini, P1S, P1P. X1/X1C/X1E
97
- * and P2S use RTSP on port 322 instead -- not implemented yet. H2/H2S/H2D
98
+ * and P2S use RTSP on port 322 instead. H2/H2S/H2D/H2C
98
99
  * are not documented; we fail fast rather than guess at the protocol.
99
100
  *
100
101
  * Read-only; no confirm gate. Default 8s timeout for cold-start latency.
@@ -32,6 +32,8 @@ function loadClientCreds() {
32
32
  const CLIENT_CREDS = loadClientCreds();
33
33
  const COMMAND_SETTLE_MS = 300;
34
34
  const MODEL_ID_TO_NAME = {
35
+ O1C: "H2C",
36
+ O1C2: "H2C",
35
37
  O1D: "H2D",
36
38
  O1E: "H2D Pro",
37
39
  O1S: "H2S",
@@ -43,6 +45,10 @@ const MODEL_ID_TO_NAME = {
43
45
  "BL-P002": "X1",
44
46
  C13: "X1E",
45
47
  };
48
+ const H2_MODEL_NAMES = new Set(["h2", "h2c", "h2d", "h2dpro", "h2d pro", "h2s"]);
49
+ function isH2ModelName(model) {
50
+ return H2_MODEL_NAMES.has(String(model ?? "").trim().toLowerCase().replace(/\s+/g, " "));
51
+ }
46
52
  function sleep(ms) {
47
53
  return new Promise((resolve) => setTimeout(resolve, ms));
48
54
  }
@@ -72,7 +78,7 @@ class TolerantBambuClient extends BambuClient {
72
78
  await subscribe(`device/${this.config.serialNumber}/report`);
73
79
  }
74
80
  /**
75
- * H2S/H2D printers don't respond to get_version with module info.
81
+ * H2-series printers don't respond to get_version with module info.
76
82
  * Infer model from serial number prefix so downstream code (Job, status
77
83
  * parsing, etc.) has a valid printerModel instead of undefined.
78
84
  */
@@ -155,7 +161,7 @@ class TolerantBambuClient extends BambuClient {
155
161
  reject(err);
156
162
  });
157
163
  });
158
- // H2S/H2D printers don't respond to get_version with module info.
164
+ // H2-series printers don't respond to get_version with module info.
159
165
  // Infer model from serial number prefix so downstream code works.
160
166
  if (!this.data.model) {
161
167
  const inferred = this.inferModelFromSerial();
@@ -450,9 +456,11 @@ export class BambuImplementation {
450
456
  // the container format from the extension.
451
457
  let remoteFileName = path.basename(options.filePath);
452
458
  remoteFileName = remoteFileName.replace(/\.gcode\.3mf\.gcode\.3mf$/i, ".gcode.3mf");
453
- // H2S/H2D land files at the FTP root and reference them via ftp:///<name>.
459
+ // H2-series printers land files at the FTP root and reference them via ftp:///<name>.
454
460
  // P1/A1/X1 use /cache/<name> and file:///sdcard/cache/<name>.
455
- const isH2 = serial.startsWith("093") || serial.startsWith("094");
461
+ const isH2 = serial.startsWith("093") ||
462
+ serial.startsWith("094") ||
463
+ isH2ModelName(options.bambuModel);
456
464
  const remoteProjectPath = isH2 ? remoteFileName : `cache/${remoteFileName}`;
457
465
  const remoteUploadPath = isH2 ? `/${remoteFileName}` : `/cache/${remoteFileName}`;
458
466
  const projectUrl = isH2
@@ -463,7 +471,7 @@ export class BambuImplementation {
463
471
  // Pre-sliced .gcode.3mf files: routing depends on firmware generation.
464
472
  // P1/A1/X1 series: project_file returns 405004002 for .gcode.3mf (firmware
465
473
  // doesn't recognise the container), so use gcode_file instead.
466
- // H2S/H2D: gcode_file is not supported; project_file works because the
474
+ // H2-series: gcode_file is not supported; project_file works because the
467
475
  // firmware can open the zip and find Metadata/plate_<n>.gcode directly.
468
476
  if (options.filePath.toLowerCase().endsWith(".gcode.3mf")) {
469
477
  if (!isH2) {
@@ -475,7 +483,7 @@ export class BambuImplementation {
475
483
  remoteProjectPath,
476
484
  };
477
485
  }
478
- // H2S/H2D: fall through to project_file path below
486
+ // H2-series: fall through to project_file path below
479
487
  }
480
488
  const projectMetadata = await this.resolveProjectFileMetadata(options.filePath, options.plateIndex);
481
489
  // Send project_file command via bambu-node MQTT (bypasses bambu-js
@@ -970,7 +978,7 @@ export class BambuImplementation {
970
978
  * [16..16+payloadSize] JPEG (FF D8 ... FF D9)
971
979
  *
972
980
  * Verified models per upstream docs: A1, A1 mini, P1S, P1P. X1/X1C/X1E
973
- * and P2S use RTSP on port 322 instead -- not implemented yet. H2/H2S/H2D
981
+ * and P2S use RTSP on port 322 instead. H2/H2S/H2D/H2C
974
982
  * are not documented; we fail fast rather than guess at the protocol.
975
983
  *
976
984
  * Read-only; no confirm gate. Default 8s timeout for cold-start latency.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bambu-printer-mcp",
3
- "version": "1.1.2",
3
+ "version": "1.1.5",
4
4
  "description": "MCP server for Bambu Lab 3D printers with STL manipulation, slicing, and print management",
5
5
  "main": "dist/index.js",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -46,8 +46,9 @@ const DEFAULT_BAMBU_MODEL =
46
46
  const DEFAULT_BED_TYPE = process.env.BED_TYPE?.trim().toLowerCase() || "textured_plate";
47
47
  const DEFAULT_NOZZLE_DIAMETER = process.env.NOZZLE_DIAMETER?.trim() || "0.4";
48
48
 
49
- const VALID_BAMBU_MODELS = ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"] as const;
49
+ const VALID_BAMBU_MODELS = ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s", "h2c"] as const;
50
50
  type BambuModel = typeof VALID_BAMBU_MODELS[number];
51
+ const H2_BAMBU_MODELS = new Set<string>(["h2d", "h2s", "h2c"]);
51
52
 
52
53
  const VALID_BED_TYPES = ["textured_plate", "cool_plate", "engineering_plate", "hot_plate", "supertack_plate"] as const;
53
54
  const VALID_BAMBUSTUDIO_CLI_BED_TYPES = ["textured_plate", "cool_plate", "engineering_plate", "hot_plate"] as const;
@@ -63,6 +64,7 @@ const BAMBU_MODEL_PRESETS: Record<string, (nozzle: string) => string> = {
63
64
  a1mini: (n) => `Bambu Lab A1 mini ${n} nozzle`,
64
65
  h2d: (n) => `Bambu Lab H2D ${n} nozzle`,
65
66
  h2s: (n) => `Bambu Lab H2S ${n} nozzle`,
67
+ h2c: (n) => `Bambu Lab H2C ${n} nozzle`,
66
68
  };
67
69
 
68
70
  const FILAMENT_PROFILE_DIR =
@@ -77,6 +79,7 @@ const FILAMENT_MODEL_CODES: Record<string, string> = {
77
79
  a1mini: "A1M",
78
80
  h2d: "H2D",
79
81
  h2s: "H2S",
82
+ h2c: "H2C",
80
83
  };
81
84
 
82
85
  type FilamentProfileIndex = {
@@ -1078,6 +1081,7 @@ class BambuPrinterMCPServer {
1078
1081
  { const: "a1mini", title: "A1 Mini" },
1079
1082
  { const: "h2d", title: "H2D" },
1080
1083
  { const: "h2s", title: "H2S" },
1084
+ { const: "h2c", title: "H2C" },
1081
1085
  ],
1082
1086
  },
1083
1087
  },
@@ -1776,7 +1780,7 @@ class BambuPrinterMCPServer {
1776
1780
  },
1777
1781
  bambu_model: {
1778
1782
  type: "string",
1779
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1783
+ enum: [...VALID_BAMBU_MODELS],
1780
1784
  description: "Optional model hint used to resolve Bambu/Orca filament profile JSONs for each tray."
1781
1785
  },
1782
1786
  nozzle_diameter: {
@@ -1796,7 +1800,7 @@ class BambuPrinterMCPServer {
1796
1800
  plate_index: { type: "number", description: "0-based plate index to inspect (default: 0)" },
1797
1801
  bambu_model: {
1798
1802
  type: "string",
1799
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1803
+ enum: [...VALID_BAMBU_MODELS],
1800
1804
  description: "Optional model hint used to resolve Bambu/Orca filament profile JSONs for each tray."
1801
1805
  },
1802
1806
  nozzle_diameter: { type: "string", description: "Nozzle diameter in mm (default: 0.4)" },
@@ -1937,7 +1941,7 @@ class BambuPrinterMCPServer {
1937
1941
  template_dir: { type: "string", description: "Optional template directory override when resolving template_name." },
1938
1942
  bambu_model: {
1939
1943
  type: "string",
1940
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1944
+ enum: [...VALID_BAMBU_MODELS],
1941
1945
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
1942
1946
  },
1943
1947
  slicer_type: {
@@ -1983,7 +1987,7 @@ class BambuPrinterMCPServer {
1983
1987
  stl_path: { type: "string", description: "Path to the STL or 3MF file to slice" },
1984
1988
  bambu_model: {
1985
1989
  type: "string",
1986
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
1990
+ enum: [...VALID_BAMBU_MODELS],
1987
1991
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
1988
1992
  },
1989
1993
  slicer_type: {
@@ -2080,7 +2084,7 @@ class BambuPrinterMCPServer {
2080
2084
  three_mf_path: { type: "string", description: "Path to the 3MF file to print; unsliced 3MFs are auto-sliced before sending." },
2081
2085
  bambu_model: {
2082
2086
  type: "string",
2083
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2087
+ enum: [...VALID_BAMBU_MODELS],
2084
2088
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
2085
2089
  },
2086
2090
  connection_type: { type: "string", enum: ["cloud", "lan"], description: "BambuNetwork connection type to put in FULU PrintParams; cloud uses restored internet printing, lan uses local bridge printing." },
@@ -2195,7 +2199,7 @@ class BambuPrinterMCPServer {
2195
2199
  print: { type: "boolean", description: "Start printing after upload (default: false)" },
2196
2200
  bambu_model: {
2197
2201
  type: "string",
2198
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2202
+ enum: [...VALID_BAMBU_MODELS],
2199
2203
  description: "Required when print is true. Bambu Lab printer model used as a safety confirmation before starting the uploaded file."
2200
2204
  },
2201
2205
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
@@ -2214,7 +2218,7 @@ class BambuPrinterMCPServer {
2214
2218
  filename: { type: "string", description: "Name of the file to print" },
2215
2219
  bambu_model: {
2216
2220
  type: "string",
2217
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2221
+ enum: [...VALID_BAMBU_MODELS],
2218
2222
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Starting G-code for the wrong model can damage the printer."
2219
2223
  },
2220
2224
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
@@ -2233,7 +2237,7 @@ class BambuPrinterMCPServer {
2233
2237
  filename: { type: "string", description: "Name of the file to print" },
2234
2238
  bambu_model: {
2235
2239
  type: "string",
2236
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2240
+ enum: [...VALID_BAMBU_MODELS],
2237
2241
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Starting G-code for the wrong model can damage the printer."
2238
2242
  },
2239
2243
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
@@ -2446,7 +2450,7 @@ class BambuPrinterMCPServer {
2446
2450
  three_mf_path: { type: "string", description: "Path to the 3MF file to print" },
2447
2451
  bambu_model: {
2448
2452
  type: "string",
2449
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2453
+ enum: [...VALID_BAMBU_MODELS],
2450
2454
  description: "REQUIRED: Bambu Lab printer model. Ask the user if not known. Using the wrong model can damage the printer."
2451
2455
  },
2452
2456
  connection_mode: {
@@ -2516,8 +2520,8 @@ class BambuPrinterMCPServer {
2516
2520
  template_dir: { type: "string", description: "Optional template directory override when resolving template_name." },
2517
2521
  bambu_model: {
2518
2522
  type: "string",
2519
- enum: ["p1s", "p1p", "p2s", "x1c", "x1e", "a1", "a1mini", "h2d", "h2s"],
2520
- description: "REQUIRED: Bambu Lab printer model. H2D and H2S are the primary intended paths."
2523
+ enum: [...VALID_BAMBU_MODELS],
2524
+ description: "REQUIRED: Bambu Lab printer model. H2D, H2S, and H2C are the primary intended paths."
2521
2525
  },
2522
2526
  host: { type: "string", description: "Hostname or IP of the printer (default: value from env)" },
2523
2527
  bambu_serial: { type: "string", description: "Serial number (default: value from env)" },
@@ -3201,7 +3205,7 @@ class BambuPrinterMCPServer {
3201
3205
  }
3202
3206
 
3203
3207
  const parsed3MFData = await parse3MF(threeMFPath);
3204
- const isH2Print = printModel === "h2s" || printModel === "h2d";
3208
+ const isH2Print = H2_BAMBU_MODELS.has(printModel);
3205
3209
  let parsedAmsMapping: number[] | undefined;
3206
3210
  if (!isH2Print && parsed3MFData.slicerConfig?.ams_mapping) {
3207
3211
  const slots = normalizeAmsMappingObject(parsed3MFData.slicerConfig.ams_mapping as Record<string, unknown>);
@@ -3290,6 +3294,7 @@ class BambuPrinterMCPServer {
3290
3294
  result = await this.bambu.print3mf(host, bambuSerial, bambuToken, {
3291
3295
  projectName,
3292
3296
  filePath: threeMFPath,
3297
+ bambuModel: printModel,
3293
3298
  plateIndex,
3294
3299
  useAMS: useAMS,
3295
3300
  amsMapping: finalAmsMapping,
@@ -3353,6 +3358,7 @@ class BambuPrinterMCPServer {
3353
3358
  result = await this.bambu.print3mf(host, bambuSerial, bambuToken, {
3354
3359
  projectName,
3355
3360
  filePath: preparedThreeMFPath,
3361
+ bambuModel: printModel,
3356
3362
  plateIndex: collarAnalysis.plateIndex,
3357
3363
  useAMS: true,
3358
3364
  amsSlots: collarAnalysis.amsSlots,
@@ -42,6 +42,8 @@ const CLIENT_CREDS = loadClientCreds();
42
42
  const COMMAND_SETTLE_MS = 300;
43
43
 
44
44
  const MODEL_ID_TO_NAME: Record<string, string> = {
45
+ O1C: "H2C",
46
+ O1C2: "H2C",
45
47
  O1D: "H2D",
46
48
  O1E: "H2D Pro",
47
49
  O1S: "H2S",
@@ -54,9 +56,16 @@ const MODEL_ID_TO_NAME: Record<string, string> = {
54
56
  C13: "X1E",
55
57
  };
56
58
 
59
+ const H2_MODEL_NAMES = new Set(["h2", "h2c", "h2d", "h2dpro", "h2d pro", "h2s"]);
60
+
61
+ function isH2ModelName(model: unknown): boolean {
62
+ return H2_MODEL_NAMES.has(String(model ?? "").trim().toLowerCase().replace(/\s+/g, " "));
63
+ }
64
+
57
65
  interface BambuPrintOptionsInternal {
58
66
  projectName: string;
59
67
  filePath: string;
68
+ bambuModel?: string;
60
69
  useAMS?: boolean;
61
70
  plateIndex?: number;
62
71
  bedType?: string;
@@ -128,7 +137,7 @@ class TolerantBambuClient extends BambuClient {
128
137
  }
129
138
 
130
139
  /**
131
- * H2S/H2D printers don't respond to get_version with module info.
140
+ * H2-series printers don't respond to get_version with module info.
132
141
  * Infer model from serial number prefix so downstream code (Job, status
133
142
  * parsing, etc.) has a valid printerModel instead of undefined.
134
143
  */
@@ -205,7 +214,7 @@ class TolerantBambuClient extends BambuClient {
205
214
  reject(err);
206
215
  });
207
216
  });
208
- // H2S/H2D printers don't respond to get_version with module info.
217
+ // H2-series printers don't respond to get_version with module info.
209
218
  // Infer model from serial number prefix so downstream code works.
210
219
  if (!this.data.model) {
211
220
  const inferred = this.inferModelFromSerial();
@@ -562,9 +571,12 @@ export class BambuImplementation {
562
571
  let remoteFileName = path.basename(options.filePath);
563
572
  remoteFileName = remoteFileName.replace(/\.gcode\.3mf\.gcode\.3mf$/i, ".gcode.3mf");
564
573
 
565
- // H2S/H2D land files at the FTP root and reference them via ftp:///<name>.
574
+ // H2-series printers land files at the FTP root and reference them via ftp:///<name>.
566
575
  // P1/A1/X1 use /cache/<name> and file:///sdcard/cache/<name>.
567
- const isH2 = serial.startsWith("093") || serial.startsWith("094");
576
+ const isH2 =
577
+ serial.startsWith("093") ||
578
+ serial.startsWith("094") ||
579
+ isH2ModelName(options.bambuModel);
568
580
  const remoteProjectPath = isH2 ? remoteFileName : `cache/${remoteFileName}`;
569
581
  const remoteUploadPath = isH2 ? `/${remoteFileName}` : `/cache/${remoteFileName}`;
570
582
  const projectUrl = isH2
@@ -577,7 +589,7 @@ export class BambuImplementation {
577
589
  // Pre-sliced .gcode.3mf files: routing depends on firmware generation.
578
590
  // P1/A1/X1 series: project_file returns 405004002 for .gcode.3mf (firmware
579
591
  // doesn't recognise the container), so use gcode_file instead.
580
- // H2S/H2D: gcode_file is not supported; project_file works because the
592
+ // H2-series: gcode_file is not supported; project_file works because the
581
593
  // firmware can open the zip and find Metadata/plate_<n>.gcode directly.
582
594
  if (options.filePath.toLowerCase().endsWith(".gcode.3mf")) {
583
595
  if (!isH2) {
@@ -589,7 +601,7 @@ export class BambuImplementation {
589
601
  remoteProjectPath,
590
602
  };
591
603
  }
592
- // H2S/H2D: fall through to project_file path below
604
+ // H2-series: fall through to project_file path below
593
605
  }
594
606
 
595
607
  const projectMetadata = await this.resolveProjectFileMetadata(
@@ -1210,7 +1222,7 @@ export class BambuImplementation {
1210
1222
  * [16..16+payloadSize] JPEG (FF D8 ... FF D9)
1211
1223
  *
1212
1224
  * Verified models per upstream docs: A1, A1 mini, P1S, P1P. X1/X1C/X1E
1213
- * and P2S use RTSP on port 322 instead -- not implemented yet. H2/H2S/H2D
1225
+ * and P2S use RTSP on port 322 instead. H2/H2S/H2D/H2C
1214
1226
  * are not documented; we fail fast rather than guess at the protocol.
1215
1227
  *
1216
1228
  * Read-only; no confirm gate. Default 8s timeout for cold-start latency.