mcp-3d-printer-server 1.2.24 → 1.2.26

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 (96) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/CONTRIBUTORS.md +5 -1
  3. package/README.md +64 -29
  4. package/dist/ams-mapping.d.ts +38 -0
  5. package/dist/ams-mapping.js +125 -0
  6. package/dist/bambu-network-bridge.d.ts +200 -0
  7. package/dist/bambu-network-bridge.js +881 -0
  8. package/dist/bambu-network-certs.d.ts +13 -0
  9. package/dist/bambu-network-certs.js +152 -0
  10. package/dist/control-reply.d.ts +21 -0
  11. package/dist/control-reply.js +44 -0
  12. package/dist/counterpart/camera-sources.d.ts +16 -0
  13. package/dist/counterpart/camera-sources.js +138 -0
  14. package/dist/counterpart/camera-stream.d.ts +66 -0
  15. package/dist/counterpart/camera-stream.js +246 -0
  16. package/dist/counterpart/camera-tools.d.ts +247 -0
  17. package/dist/counterpart/camera-tools.js +35 -0
  18. package/dist/counterpart/contract.d.ts +21 -5
  19. package/dist/counterpart/contract.js +8 -2
  20. package/dist/counterpart/route.d.ts +31 -0
  21. package/dist/counterpart/route.js +70 -0
  22. package/dist/counterpart/service.d.ts +60 -4
  23. package/dist/counterpart/service.js +142 -16
  24. package/dist/design-guidance.d.ts +26 -0
  25. package/dist/design-guidance.js +177 -0
  26. package/dist/fulu-orca.d.ts +7 -0
  27. package/dist/fulu-orca.js +18 -2
  28. package/dist/index.js +528 -72
  29. package/dist/printers/bambu-filaments.d.ts +68 -0
  30. package/dist/printers/bambu-filaments.js +262 -0
  31. package/dist/printers/bambu.d.ts +99 -6
  32. package/dist/printers/bambu.js +478 -72
  33. package/dist/printers/creality.d.ts +2 -1
  34. package/dist/printers/creality.js +5 -1
  35. package/dist/printers/generic-base.d.ts +11 -6
  36. package/dist/printers/generic-base.js +21 -25
  37. package/dist/printers/klipper.d.ts +2 -1
  38. package/dist/printers/klipper.js +10 -7
  39. package/dist/printers/octoprint.d.ts +2 -1
  40. package/dist/printers/octoprint.js +15 -8
  41. package/dist/printers/printer-factory.d.ts +1 -1
  42. package/dist/printers/printer-factory.js +3 -3
  43. package/dist/printers/prusa.js +0 -1
  44. package/dist/safety/confirmation.d.ts +11 -16
  45. package/dist/safety/confirmation.js +8 -32
  46. package/dist/safety/hidden-hms.d.ts +32 -0
  47. package/dist/safety/hidden-hms.js +405 -0
  48. package/dist/safety/limits.d.ts +8 -2
  49. package/dist/safety/limits.js +14 -5
  50. package/dist/safety/print-file.d.ts +5 -0
  51. package/dist/safety/print-file.js +156 -19
  52. package/dist/safety/printer-state.d.ts +59 -8
  53. package/dist/safety/printer-state.js +174 -39
  54. package/dist/slicer/bambu-cli.d.ts +6 -2
  55. package/dist/slicer/bambu-cli.js +19 -12
  56. package/dist/slicer/layer-pauses.d.ts +47 -0
  57. package/dist/slicer/layer-pauses.js +130 -0
  58. package/dist/slicer/slicer-error.d.ts +11 -1
  59. package/dist/slicer/slicer-error.js +16 -2
  60. package/dist/stl/stl-manipulator.d.ts +27 -1
  61. package/dist/stl/stl-manipulator.js +216 -76
  62. package/dist/studio-print-options.d.ts +57 -0
  63. package/dist/studio-print-options.js +179 -0
  64. package/package.json +25 -4
  65. package/server.json +2 -2
  66. package/src/ams-mapping.ts +132 -0
  67. package/src/bambu-network-bridge.ts +1075 -0
  68. package/src/bambu-network-certs.ts +154 -0
  69. package/src/control-reply.ts +41 -0
  70. package/src/counterpart/camera-sources.ts +102 -0
  71. package/src/counterpart/camera-stream.ts +195 -0
  72. package/src/counterpart/camera-tools.ts +35 -0
  73. package/src/counterpart/contract.ts +12 -4
  74. package/src/counterpart/route.ts +82 -0
  75. package/src/counterpart/service.ts +150 -16
  76. package/src/design-guidance.ts +188 -0
  77. package/src/fulu-orca.ts +18 -2
  78. package/src/index.ts +546 -87
  79. package/src/printers/bambu-filaments.ts +346 -0
  80. package/src/printers/bambu.ts +506 -81
  81. package/src/printers/creality.ts +8 -3
  82. package/src/printers/generic-base.ts +33 -40
  83. package/src/printers/klipper.ts +12 -8
  84. package/src/printers/octoprint.ts +16 -9
  85. package/src/printers/printer-factory.ts +3 -3
  86. package/src/printers/prusa.ts +0 -1
  87. package/src/safety/confirmation.ts +15 -38
  88. package/src/safety/hidden-hms.ts +406 -0
  89. package/src/safety/limits.ts +15 -5
  90. package/src/safety/print-file.ts +139 -17
  91. package/src/safety/printer-state.ts +179 -40
  92. package/src/slicer/bambu-cli.ts +22 -13
  93. package/src/slicer/layer-pauses.ts +143 -0
  94. package/src/slicer/slicer-error.ts +21 -2
  95. package/src/stl/stl-manipulator.ts +254 -84
  96. package/src/studio-print-options.ts +201 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.26] – 2026-11-04
4
+
5
+ ### Changed
6
+
7
+ - Start prints and positive heating without a server-side confirmation prompt, on every printer type. The client's own print action is the approval and the printer runs its own checks, so clients without MCP elicitation support now print and heat normally. `PRINT_REQUIRE_CONFIRMATION` and `BAMBU_REQUIRE_CONFIRMATION` are removed. File inspection, hardware and material ceilings, declared materials, fresh printer state (read, rechecked before upload and again before dispatch) and Bambu identity checks are unchanged. Matches bambu-printer-mcp.
8
+ - `resume_print` asks a person only when the printer cannot hand the file back for re-reading (PrusaLink, Repetier, Creality), where the job is matched by name only. OctoPrint, Klipper and Duet (DSF) re-read the file, compare its SHA-256 and resume without a prompt. `PRINT_CONFIRMATION_TIMEOUT_MS` now bounds that prompt and the Bambu printer-model prompt.
9
+ - Stop treating a finished job as a bed-clearance case: a printer that reports a finished or cancelled job (Moonraker complete or cancelled, PrusaLink FINISHED or STOPPED, Creality finished, Bambu FINISH) starts the next print like an idle one. Matches bambu-printer-mcp's bed-check removal ([bambu-printer-mcp #90](https://github.com/DMontgomery40/bambu-printer-mcp/pull/90)).
10
+ - Evidence: mocked Bambu MQTT/FTPS boundaries and loopback HTTP printer APIs only; no real printer was used.
11
+
12
+ ### Fixed
13
+
14
+ - Include the authenticated control reply module in the npm package. The route imported it but the explicit package allowlist omitted it, so a packed install exited before MCP initialization. The packed-install smoke test covers startup and discovery through all three entrypoints.
15
+
16
+ - Keep authenticated FULU Pause, Resume and Stop request IDs as strings in Studio's 20000–29999 window, with a shared counter across LAN, cloud and retries that wraps inside the window. A cloud-bound P1S 01.10 rejects larger IDs with 0502400A even when their signatures are valid. Preserve reply correlation and the delivery hold; remove guidance claiming controls require Studio/Handy or Developer Mode. Automated regressions cover both transports and wraparound; hardware validation is limited to the maintainer's P1S.
17
+
18
+ - An `ams_mapping` object (and the 3MF's own mapping) is ordered by project filament index, as bambu-printer-mcp orders it. Before, its tray values were sorted, so `{"0": 3, "1": 0}` sent tray 0 for filament 0.
19
+ - Bambu printers slice with Bambu Studio when `SLICER_TYPE` is not set, as bambu-printer-mcp does. The previous default, FULU OrcaSlicer-bambulab, crashes on every command-line slice in its only release (v1.0.0, macOS DMG and Linux builds; fixes in FULU #56 and #57 are not released), so `slice_stl`, estimates and auto-slicing failed for every Bambu user without a slicer setting. FULU's Orca stays fully supported with `SLICER_TYPE=orcaslicer-bambulab`.
20
+ - A relative `TEMP_DIR` (such as `./temp`) is resolved against the server's working directory, as bambu-printer-mcp 1.1.44 does. Bambu Studio fails every 3MF export to a relative output folder ("Project export to temp/…/…_sliced.3mf failed").
21
+
22
+ - Accept Bambu Studio 2.9 layer-temperature changes (`M104 M S<temperature>` with an optional `T` selector) in the Bambu file inspector. Preserve hardware/material ceilings, tool validation, and rejection of numeric markers, other heaters and extra parameters. Reproduced using isolated Studio 02.09.01.70 output; no physical print was run. This ports the fix prepared in [bambu-printer-mcp #97](https://github.com/DMontgomery40/bambu-printer-mcp/pull/97).
23
+
24
+ - Update `basic-ftp` to 6.2.5, including its 6.2.4 fix for directory listings containing inherited-property fact names. Offline listing reproduction fails with 6.2.3 and preserves the file name and size with 6.2.5; no printer was contacted.
25
+
26
+ - Resolve H2C first-layer temperature tables by the selected filament index and extruder variant instead of treating every variant row as a filament. Preserve unique bindings, header agreement and independent base/offset ceilings. Ports the correction reported by [Steve Pritchard (sjpritchard)](https://github.com/DMontgomery40/bambu-printer-mcp/issues/81); an unchanged Studio 2.9 H2C slice passes offline inspection after reproducing the failure.
27
+
28
+ - `print_3mf` with a `plate_index` prints, inspects and checks pauses on that plate on every route: FULU's BambuNetwork route used to inspect and dispatch plate 1, and a pre-sliced file's requested pauses were checked against plate 1. Tested with the fake FULU host; no printer was contacted.
29
+
30
+ ### Added
31
+
32
+ - `slice_stl`, `slice_with_template` and `print_3mf` accept `pause_at_layers`, matching bambu-printer-mcp. Bambu Studio and Bambu-compatible OrcaSlicer CLIs place the printer's own pause before each requested 1-based layer (2 or greater), using a first slice to find exact heights. Existing project pauses and custom G-code are preserved. Output must contain every requested pause; already sliced files missing one are refused before dispatch. Other slicers refuse the option rather than ignoring it. Evidence: fake slicer/MCP tests only; no physical print or real slicer was run in this port.
33
+
34
+ - Bambu AMS inventory and tray choice, ported from bambu-printer-mcp:
35
+ - `get_printer_filaments` reads the AMS from the printer's MQTT report for `PRINTER_TYPE=bambu` instead of refusing. The result carries bambu-printer-mcp's fields unchanged (`trays` with absolute `slot`, `loaded`, `tray_type`, `tray_color`, `tray_info_idx` and the resolved Bambu Studio/Orca filament profile; `summary`; `recommended`; `load_filaments_all`), plus counterpart/1 `slots` for every loaded AMS tray and the external spool (`254`, and `254`/`255` on dual-nozzle printers) with `source: "printer"`. Optional `bambu_model` and `nozzle_diameter` pick model-specific profiles. `counterpart_printer_info` reports `filaments: "ams"` for Bambu.
36
+ - `print_3mf` accepts `ams_slots`: one tray per used filament in plate order, expanded to the project-level `ams_mapping` from the plate's `filament_ids` and the G-code header, as bambu-printer-mcp does. `ams_mapping` also accepts the project-level array. Tray values are checked (0–15, 128–254, and 255 on dual-nozzle printers), `ams_slots` must name exactly one tray per used filament, and the fresh-state check still compares each mapped tray's material with the file. `use_ams: false` maps every used filament to the external spool of its nozzle on the wire. Counterpart's printer handoff sends exactly these arguments.
37
+ - Evidence: mocked MQTT reports and FTPS/MQTT dispatch only; no real printer was used.
38
+ - Bambu `pause_print` and `resume_print`, and `counterpart_printer_info` reports `pause` and `resume` for Bambu, ported from bambu-printer-mcp:
39
+ - Pause and stop (`cancel_print`) are never gated. Stop also retires the job's resume record.
40
+ - Resume keeps bambu-printer-mcp's checked-job safety: only the job this server inspected and started, while a fresh report shows it paused under its unique name, with the fresh-state check repeated. Its bytes are re-read over FTPS (the uploaded 3MF, else the `cache/<name>_plate_<n>.gcode` a P1S extracts from it) and their SHA-256 compared; a changed file retires the record. A file this server uploaded that cannot be re-read refuses the resume. Only a file Bambu's plug-in placed (FULU's BambuNetwork route) that cannot be re-read asks a person through MCP elicitation.
41
+ - Every MQTT report retires a record whose job ended or was replaced, even when nobody polls.
42
+ - Checked 3MF prints are started with their unique remote name as `subtask_name`, as bambu-printer-mcp does, so the printer reports back which job is paused.
43
+ - Evidence: in-process FTPS/MQTT fakes only; no real printer was paused or resumed.
44
+ - Bambu connection routes, including FULU's authenticated BambuNetwork route, ported from bambu-printer-mcp, so a P1S on authorization firmware without Developer Mode prints and is controlled over the local network:
45
+ - One route decision (`resolveConnection`) for `print_3mf`, `cancel_print`, `pause_print` and `resume_print`: `connection_mode` (`lan_mqtt_ftps`, the default, or `bambu_network`) and `connection_type` (`lan` or `cloud`; `cloud` when unset, as in bambu-printer-mcp), defaulting to `BAMBU_DEFAULT_CONNECTION_MODE` and `BAMBU_DEFAULT_CONNECTION_TYPE`. A job is controlled over the connection that printed it. bambu-printer-mcp's `bambu_native` (X2D) and `bambu_connect` modes are refused with a clear error; nothing is sent over another connection.
46
+ - `print_3mf` with `bambu_network` runs the same gates as the LAN path (inspection of the exact snapshot bytes, the AMS mapping, fresh LAN MQTT state before the bridge starts and again immediately before the print frame), then opens the plug-in's own session: `net.start_local_print` for `lan`, `net.start_print` for `cloud`. It sends Bambu Studio's `ams_mapping`, `ams_mapping2` and `ams_mapping_info`, the Send dialog's calibration options as Bambu Studio resolves them, and the unique checked name as the task name. A synchronous -4030 is retried once after the certificate exchange; other non-zero results report the plug-in's code and messages. The job is recorded for a checked resume.
47
+ - Stop, Pause and Resume over `bambu_network` go through the plug-in's authenticated session, held open until the printer answers; a refusal reports the printer's `err_code`, a -4/-4030 send is retried after reinstalling the certificate (three tries), and every event and send is written to `control-trace.log` in the bridge's config folder without the access code.
48
+ - New `counterpart_print_route` tool, same contract as bambu-printer-mcp's: reads the route (`lan` or `cloud`, with the resolved mode and type) and changes it in the running server and in the `.env` file in its working directory when one exists (`savedTo`).
49
+ - The bridge host is `BAMBU_NETWORK_BRIDGE_COMMAND` (bambu-printer-mcp's name), else `FULU_BAMBU_BRIDGE_COMMAND`, else the host `check_fulu_orca_setup` discovers next to FULU's OrcaSlicer-bambulab. Its config folder defaults to `~/.config/mcp-3d-printer-server/bambu-network` (`BAMBU_NETWORK_CONFIG_DIR`). `fulu_bambu_network_rpc` still refuses raw print methods.
50
+ - Evidence: a fake FULU host process that reproduces the real v1.0.0 host and plug-in behaviour bambu-printer-mcp recorded on a P1S, plus patched printer state; no real FULU host, plug-in, printer or Bambu Cloud was used by this server.
51
+
52
+ - Serves design guidance for agents that design or edit models to print as MCP prompts: `fdm_design`, `fdm_multicolor`, `fdm_assembly` and `fdm_after_slice`, plus the resource `guidance://fdm/capability-guidance` for bridges and overhangs. The server's `instructions` point every client to them. The text is David Montgomery's FDM design prompt pack of 3 November 2026, unchanged; bambu-printer-mcp serves the same guidance. Tested over a real stdio MCP connection; whether an agent follows it is a model behavior, not tested here.
53
+
54
+ ### Documentation
55
+
56
+ - README, `docs/FULU.md`, `docs/SETUP.md` and `.env.example` describe Bambu AMS inventory and tray choice, Bambu pause, resume and stop, the connection routes (`BAMBU_DEFAULT_CONNECTION_MODE`, `BAMBU_DEFAULT_CONNECTION_TYPE`, `counterpart_print_route`) and printing and controlling through FULU's BambuNetwork route, including its evidence limits here (a fake FULU host only).
57
+
58
+ ## [1.2.25] – 2026-11-02
59
+
60
+ ### Fixed
61
+
62
+ - `lay_flat` rests a model on a face that is its real bottom. Before, it turned the face-normal group with the largest area to face down and moved the lowest point to Z=0, without checking that those faces were the lowest. The stove gap filler from Scan to Make (six T strips) printed upside down: its cap undersides' 23,838 mm² face down but sit 12 mm above the fin tips, so it stood on its fins.
63
+ - A pose qualifies only when some face, turned down, lies on the rotated model's lowest plane, within 0.5% of its height and at most 0.2 mm. The candidate poses are the 64 largest face groups and the model's six axes, so a domed part can flip exactly.
64
+ - Among qualifying poses, `lay_flat` picks the one with the least part and support volume per mm² of bed contact. Support is the overhang more than 45° from vertical, times its height above the bed. Ties go to the larger contact, then to the face that already points down.
65
+ - When no pose qualifies, the model is unchanged. The text result is still the STL path, and the new `structuredContent` `{path, laid_flat, message}` says why.
66
+ - The gap filler now comes out cap-down, fin tips 14.4 mm up. This is checked from the output vertices only. The same rule in bambu-printer-mcp was also checked by slicing with Bambu Studio 02.08.02.61 for a P1S, which gave no warning; that slice was not repeated here.
67
+ - `slice_stl` returns Bambu Studio's own checks on the Bambu-compatible CLI route, and OrcaSlicer's where it writes the same `result.json`. When the CLI writes `result.json` (Bambu Studio 02.08.02.61 writes it on macOS on success too), `structuredContent.slicer_result` carries it parsed and unchanged: `error_string`, `return_code` and each `sliced_plates[].warning_message`. Before, the run's output directory was deleted with `result.json` in it, and warnings such as "It seems object … has floating cantilever. Please re-orient the object or enable support generation." were lost.
68
+ - The text result stays the sliced file's path.
69
+ - A failed slice is an error result whose text starts with Studio's `error_string` (unless it reports success), then each plate's `warning_message`, then the exit code, slicer output and suggestion, with no `Error: ` prefix. Its `structuredContent` carries `slicer_result` beside the existing `slicer` details.
70
+ - A `result.json` reporting a nonzero `return_code` fails the slice even when the CLI exits 0.
71
+ - The G-code slicers (PrusaSlicer, Cura, Slic3r, generic OrcaSlicer without a Bambu preset) write no `result.json` and return `structuredContent` `{path}` only.
72
+ - Tested with real `result.json` files from Bambu Studio 02.08.02.61 slicing the Scan to Make stove gap filler for a P1S on macOS, replayed by a fake CLI. The optional installed-Studio test also checks `slicer_result` on macOS; CI skips it. OrcaSlicer's `result.json` follows the same libslic3r format but was not run here.
73
+
3
74
  ## [1.2.24] – 2026-11-02
4
75
 
5
76
  ### Fixed
package/CONTRIBUTORS.md CHANGED
@@ -39,7 +39,7 @@ Thank you to everyone who builds, tests, reports problems, and shares real print
39
39
 
40
40
  | Contributor | Contribution |
41
41
  | --- | --- |
42
- | [Raza Sharif (razashariff)](https://github.com/razashariff) | Proposes cryptographic signing, replay protection, and agent identity checks before print commands in [#13](https://github.com/DMontgomery40/mcp-3D-printer-server/issues/13). Signing is not implemented; since 1.2.10, every print start and positive heating command requires human confirmation through MCP elicitation, and printed files are inspected before dispatch. |
42
+ | [Raza Sharif (razashariff)](https://github.com/razashariff) | Proposes cryptographic signing, replay protection, and agent identity checks before print commands in [#13](https://github.com/DMontgomery40/mcp-3D-printer-server/issues/13). Signing is not implemented; printed files are inspected and printer state is checked before every print start and positive heating command. The human confirmation added in 1.2.10 was removed for print starts and heating after 1.2.25. |
43
43
  | [DarkCraven (sweidinger)](https://github.com/sweidinger) | Requests Klipper print-job status (progress, file, times) beyond Moonraker's `/printer/info` in [#12](https://github.com/DMontgomery40/mcp-3D-printer-server/issues/12). Not yet implemented. |
44
44
  | [Hex (hexatriene)](https://github.com/hexatriene) | Proposes migrating Bambu support to `bambu-js` v3 for H2D support and FTP operations in [#10](https://github.com/DMontgomery40/mcp-3D-printer-server/pull/10). Closed without merging; the server keeps `bambu-node` for MQTT and uses `basic-ftp` directly for uploads. |
45
45
  | [Jean-Laurent de Morlhon (jeanlaurent)](https://github.com/jeanlaurent) | Proposes Docker support in [#2](https://github.com/DMontgomery40/mcp-3D-printer-server/pull/2). Closed without merging; the repository's Docker setup was later fixed in #3. |
@@ -63,3 +63,7 @@ Special thanks to the [FULU Foundation](https://www.fulu.org/), [Louis Rossmann]
63
63
  ## Dependency maintenance
64
64
 
65
65
  Thank you to the [MCP TypeScript SDK contributors](https://github.com/modelcontextprotocol/typescript-sdk/graphs/contributors) and [Axios contributors](https://github.com/axios/axios/graphs/contributors) for the compatible 1.32.2 and 1.21.0 releases validated in this project.
66
+
67
+ Thanks again to [Patrick Juchli](https://github.com/patrickjuchli) and the basic-ftp maintainers for the [6.2.4 listing correction](https://github.com/patrickjuchli/basic-ftp/releases/tag/v6.2.4) included in the prepared 6.2.5 dependency update. Offline regression and package validation cover this update; publication is pending.
68
+
69
+ Thanks to [Steve Pritchard (sjpritchard)](https://github.com/sjpritchard) for the official H2C first-layer temperature-table report in [bambu-printer-mcp #81](https://github.com/DMontgomery40/bambu-printer-mcp/issues/81). Its safety-preserving correction is ported here and checked with isolated Studio output; publication of this port is pending.
package/README.md CHANGED
@@ -16,7 +16,9 @@ An MCP server that connects Claude, Codex, and other MCP clients to 3D printers
16
16
 
17
17
  **[Browse the documentation site](https://dmontgomery40.github.io/mcp-3D-printer-server/)** for searchable setup guides, per-printer credentials, slicing, and the full tool reference. It is generated from this README and the docs folder.
18
18
 
19
- **Only print on Bambu Lab printers?** [bambu-printer-mcp](https://github.com/DMontgomery40/bambu-printer-mcp) is a Bambu-focused fork of this project with more Bambu features, including AMS inventory and matching, camera snapshots, X2D support, and a Claude Desktop extension.
19
+ **Only print on Bambu Lab printers?** [bambu-printer-mcp](https://github.com/DMontgomery40/bambu-printer-mcp) is a Bambu-focused fork of this project with more Bambu features, including AMS auto-matching, Bambu camera snapshots and streams, X2D native printing, Bambu Connect, and a Claude Desktop extension. Both servers read the AMS, choose trays, pause, resume, stop, and print through FULU's authenticated BambuNetwork route.
20
+
21
+ Camera tools also include `camera_stream_start`, `camera_frame`, and `camera_stream_stop` from version 1.2.26. They reuse configured MJPEG or snapshot sources, send only changed frames, and close when idle. See [camera stream configuration and limits](docs/CAMERA-STREAM.md).
20
22
 
21
23
  Built with help from our [contributors](./CONTRIBUTORS.md). Thank you to everyone sharing fixes, careful bug reports, and real printer testing!
22
24
 
@@ -64,9 +66,7 @@ Offer these optional extras, and set them up only if I choose them:
64
66
  FULU OrcaSlicer-bambulab, Bambu Studio, or CuraEngine before suggesting
65
67
  an install.
66
68
  Configure executables in the server environment. Do not enable
67
- MCP_ALLOW_EXECUTABLE_ARG. Leave print confirmation on: do not set
68
- PRINT_REQUIRE_CONFIRMATION=0 or BAMBU_REQUIRE_CONFIRMATION=0 unless I ask
69
- for a headless setup.
69
+ MCP_ALLOW_EXECUTABLE_ARG.
70
70
 
71
71
  Verify that the MCP initializes, lists its tools, and reads printer status.
72
72
  Do not start a print or change printer settings, including temperatures, as
@@ -105,9 +105,9 @@ This server provides the printer, slicing, and mesh tools. Web search, photos, a
105
105
  ### Slice and print
106
106
 
107
107
  - **"Print this in PETG."** *(with an STL)*\
108
- It slices with the PETG filament profile you've set up and checks the job's peak temperatures. The server then asks you to confirm before the print starts.
108
+ It slices with the PETG filament profile you've set up and checks the job's peak temperatures and the printer's state before the print starts.
109
109
  - **"Print the bracket I sliced last night on the Bambu."**\
110
- It checks the plate's model, nozzle, bed type, materials, and temperatures against the live printer, then asks you to confirm before it uploads and starts the job.
110
+ It checks the plate's model, nozzle, bed type, materials, and temperatures against the live printer before it uploads and starts the job.
111
111
  - **"Send benchy.gcode to the Klipper printer, but don't start it yet."**\
112
112
  It uploads the file through Moonraker so you can start it later.
113
113
 
@@ -134,7 +134,7 @@ This server provides the printer, slicing, and mesh tools. Web search, photos, a
134
134
 
135
135
  ## What's new
136
136
 
137
- See the [changelog](./CHANGELOG.md) for versioned changes. The next release brings the print safety gate to every printer backend: a human confirms every print start and positive heating command, and the server checks the exact G-code against hardware and material ceilings. It also adds Bambu-compatible CLI slicing with resolved machine presets and a template registry, `blender_mcp_export_stl` for verified STL export from Blender, and this documentation site. Release 1.2.9 fixes Bambu FTPS uploads that failed with "Premature close" ([#22](https://github.com/DMontgomery40/mcp-3D-printer-server/issues/22)), adds standard Blender MCP discovery, forwarding, and verified STL edits, requires the Bambu printer model for raw print starts, and isolates each server's scratch files. Separately, [#20](https://github.com/DMontgomery40/mcp-3D-printer-server/pull/20) and [#21](https://github.com/DMontgomery40/mcp-3D-printer-server/pull/21) made per-call executable selectors, such as a slicer path or bridge command, require an explicit opt-in.
137
+ See the [changelog](./CHANGELOG.md) for versioned changes. The next release brings the print safety gate to every printer backend: the server checks the exact G-code against hardware and material ceilings. It also adds Bambu-compatible CLI slicing with resolved machine presets and a template registry, `blender_mcp_export_stl` for verified STL export from Blender, and this documentation site. Release 1.2.9 fixes Bambu FTPS uploads that failed with "Premature close" ([#22](https://github.com/DMontgomery40/mcp-3D-printer-server/issues/22)), adds standard Blender MCP discovery, forwarding, and verified STL edits, requires the Bambu printer model for raw print starts, and isolates each server's scratch files. Separately, [#20](https://github.com/DMontgomery40/mcp-3D-printer-server/pull/20) and [#21](https://github.com/DMontgomery40/mcp-3D-printer-server/pull/21) made per-call executable selectors, such as a slicer path or bridge command, require an explicit opt-in.
138
138
 
139
139
  </details>
140
140
 
@@ -193,9 +193,9 @@ What each backend reads and sends differs. See [printer backend setup](https://g
193
193
 
194
194
  ## FULU and open-source printing
195
195
 
196
- [FULU OrcaSlicer-bambulab](https://github.com/FULU-Foundation/OrcaSlicer-bambulab) is a first-class slicer target for Bambu Lab projects (`SLICER_TYPE=orcaslicer-bambulab`; aliases include `fulu-orca` and `orca-studio`), and the default slicer when `PRINTER_TYPE=bambu` and no slicer is configured. `slice_stl` and `print_3mf` auto-slicing use its project command line, and `check_fulu_orca_setup` inspects the install and runtime payload.
196
+ [FULU OrcaSlicer-bambulab](https://github.com/FULU-Foundation/OrcaSlicer-bambulab) is a first-class slicer target for Bambu Lab projects (`SLICER_TYPE=orcaslicer-bambulab`; aliases include `fulu-orca` and `orca-studio`). Choose it with `SLICER_TYPE=orcaslicer-bambulab`: Bambu printers default to Bambu Studio, because FULU v1.0.0's command line segfaults on every slice until [FULU #56](https://github.com/FULU-Foundation/OrcaSlicer-bambulab/pull/56) and [#57](https://github.com/FULU-Foundation/OrcaSlicer-bambulab/pull/57) are released. `slice_stl` and `print_3mf` auto-slicing use its project command line, and `check_fulu_orca_setup` inspects the install and runtime payload.
197
197
 
198
- The optional FULU **BambuNetwork bridge** is a separate runtime. `fulu_bambu_network_rpc` can probe it and send guarded RPC calls, but the server never switches a print from its local MQTT and FTPS path to BambuNetwork or the cloud on its own.
198
+ The optional FULU **BambuNetwork bridge** is a separate runtime. With `BAMBU_DEFAULT_CONNECTION_MODE=bambu_network` (or `connection_mode: "bambu_network"`), `print_3mf`, `cancel_print`, `pause_print` and `resume_print` go through it: the plug-in's authenticated local-network session (`connection_type: "lan"`), which a P1S on authorization firmware accepts without Developer Mode, or Bambu Cloud (`"cloud"`). Every print still passes the same inspection and fresh-state checks first. `counterpart_print_route` reads and changes the route. The server never switches routes on its own; `fulu_bambu_network_rpc` probes the bridge and refuses raw print methods.
199
199
 
200
200
  **[Follow the FULU guide](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/FULU.md)** for platform runtime setup, bridge probes, and the current macOS and Windows testing status.
201
201
 
@@ -211,10 +211,11 @@ The optional FULU **BambuNetwork bridge** is a separate runtime. `fulu_bambu_net
211
211
  - Printer status, file listing, G-code upload from inline content or a local path, starting a stored file, pausing, resuming, cancelling a job, and setting bed or nozzle temperatures
212
212
  - The `counterpart/1` tools shared with [bambu-printer-mcp](https://github.com/DMontgomery40/bambu-printer-mcp): one progress shape for every firmware (state, job, progress, time left, layers, temperatures, problems), detected printer capabilities, camera snapshots, and the loaded filament (Spoolman on Moonraker)
213
213
  - STL tools: inspect dimensions, scale, rotate, translate, extend the base, merge vertices, center, lay flat, transform one section of a model, and render multi-angle SVG previews
214
- - A print and heating safety gate on every backend: the exact G-code is inspected, every heater target is checked against hardware and material ceilings, the printer's state is checked, and a human confirms through MCP elicitation. Heater-off, pause, and cancel are never gated; resume only continues a paused job this server inspected and started, after a person confirms
214
+ - A print and heating safety gate on every backend: the exact G-code is inspected, every heater target is checked against hardware and material ceilings, and the printer's state is checked. There is no confirmation prompt and no bed check: your client's own print action is the approval. Heater-off, pause, and cancel are never gated; resume only continues a paused job this server inspected and started, and asks a person only when the printer cannot hand the file back for re-reading
215
215
  - Slicing through PrusaSlicer, Slic3r, OrcaSlicer, CuraEngine, FULU OrcaSlicer-bambulab, or Bambu Studio command lines, with G-code peak-temperature checks and a one-call process-and-print pipeline
216
216
  - Bambu-compatible CLI slicing with the exact model and nozzle machine preset resolved from your slicer installation, filament slots and colours, placement options, and a local template registry
217
- - Bambu Lab project printing: upload a sliced `.3mf` over FTPS and start it over MQTT with the plate's G-code path, MD5, AMS mapping, and calibration flags, after checking the plate against a fresh printer report; auto-slice unsliced projects with FULU OrcaSlicer-bambulab or Bambu Studio
217
+ - Bambu Lab project printing: upload a sliced `.3mf` over FTPS and start it over MQTT with the plate's G-code path, MD5, AMS mapping (`ams_slots`, one tray per used filament), and calibration flags, after checking the plate against a fresh printer report; or print through FULU's authenticated BambuNetwork route. Auto-slice unsliced projects with FULU OrcaSlicer-bambulab or Bambu Studio
218
+ - Bambu AMS inventory (`get_printer_filaments`), pause, resume and stop over the connection that printed
218
219
  - Required Bambu printer model for print operations, asked for through MCP elicitation when missing
219
220
  - FULU OrcaSlicer-bambulab setup inspection and diagnostic BambuNetwork bridge RPC, with raw print methods refused
220
221
  - Optional Blender MCP bridge: discover a standard Blender MCP server's tools, forward calls, export named scene objects to a verified STL, and run verified STL edits
@@ -394,7 +395,7 @@ Every printer tool accepts `host`, `port`, `type`, and `api_key`, and Bambu prin
394
395
 
395
396
  What each call does depends on the backend; see [printer backend setup](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/SETUP.md#printer-backend-setup) for the exact API routes.
396
397
 
397
- Every print start and positive heating command, on every backend, goes through a [safety gate](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/SETUP.md#print-and-heating-safety): the server inspects the exact G-code it will start, checks each heater target against hardware and material ceilings, checks that the printer is ready, and asks a human to confirm through MCP elicitation. Turning a heater off (`temperature: 0`), `pause_print`, and `cancel_print` are never gated. `resume_print` reheats and moves the printer, so it only resumes a job this server inspected and started, and asks a person first.
398
+ Every print start and positive heating command, on every backend, goes through a [safety gate](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/SETUP.md#print-and-heating-safety): the server inspects the exact G-code it will start, checks each heater target against hardware and material ceilings, and checks that the printer is ready. It does not ask for confirmation or for a bed check: the client's own print action is the approval. Turning a heater off (`temperature: 0`), `pause_print`, and `cancel_print` are never gated. `resume_print` reheats and moves the printer, so it only resumes a job this server inspected and started. It asks a person first only when the printer cannot hand the file back for re-reading.
398
399
 
399
400
  #### get_printer_status
400
401
 
@@ -435,14 +436,14 @@ Upload G-code to the printer, and optionally start it. Pass `gcode_path` for a l
435
436
  ```
436
437
 
437
438
  - Without `print`, the file is only uploaded and nothing is inspected or started.
438
- - With `print: true`, the exact uploaded bytes are inspected first (every `S` and `R` heater target, tool changes, and hardware and material ceilings), the printer's state is checked, and a human confirms before the job starts.
439
+ - With `print: true`, the exact uploaded bytes are inspected first (every `S` and `R` heater target, tool changes, and hardware and material ceilings), and the printer's state is checked before the job starts.
439
440
  - Printing needs a declared material: slicer metadata in the file (`; filament_type = PLA`) or the `material` argument, which must not contradict the file.
440
441
  - On Bambu printers, files go to `cache/` over FTPS, and printing also needs the model (`bambu_model` or `BAMBU_MODEL`). Automatic printing after upload supports `.gcode` only; print `.3mf` projects with `print_3mf`.
441
442
  - The result's `jobName` is the remote file name. `counterpart_print_progress` reports the same name as `job.name` while that job runs.
442
443
 
443
444
  #### start_print
444
445
 
445
- Start a G-code file that is already stored on the printer. The server downloads that exact file, inspects it, checks the printer's state, asks a human to confirm, and then starts a uniquely named checked copy. This works on Bambu Lab, OctoPrint, Klipper (Moonraker), and Duet. Repetier, Prusa, and Creality refuse, because their adapters have no verified download route; use `upload_gcode` with `print: true` instead.
446
+ Start a G-code file that is already stored on the printer. The server downloads that exact file, inspects it, checks the printer's state, and then starts a uniquely named checked copy. This works on Bambu Lab, OctoPrint, Klipper (Moonraker), and Duet. Repetier, Prusa, and Creality refuse, because their adapters have no verified download route; use `upload_gcode` with `print: true` instead.
446
447
 
447
448
  ```json
448
449
  {
@@ -457,7 +458,7 @@ Pass `material` when the file has no `filament_type` metadata. On Bambu printers
457
458
 
458
459
  #### cancel_print
459
460
 
460
- Cancel the current print job. Cancelling is never gated and also cancels checked prints still waiting to start, including a pending `resume_print`. A cancelled job cannot be resumed; use `pause_print` to stop temporarily.
461
+ Cancel the current print job. Cancelling is never gated and also cancels checked prints still waiting to start, including a pending `resume_print`. A cancelled job cannot be resumed; use `pause_print` to stop temporarily. Bambu stops go over the connection that printed (`connection_mode`, `connection_type`, default `BAMBU_DEFAULT_CONNECTION_MODE`/`_TYPE`): LAN MQTT, or FULU's BambuNetwork session, which reports the printer's `err_code` when it refuses.
461
462
 
462
463
  ```json
463
464
  {
@@ -468,7 +469,7 @@ Cancel the current print job. Cancelling is never gated and also cancels checked
468
469
 
469
470
  #### pause_print
470
471
 
471
- Pause the current print. Pausing is never gated. Routes: OctoPrint `POST /api/job` (`{"command":"pause","action":"pause"}`), Moonraker `POST /printer/print/pause`, PrusaLink `PUT /api/v1/job/<id>/pause`, Duet `M25`, Repetier `@pause`, and Creality printers that run Moonraker. Bambu pause is in [bambu-printer-mcp](https://github.com/DMontgomery40/bambu-printer-mcp).
472
+ Pause the current print. Pausing is never gated. Routes: OctoPrint `POST /api/job` (`{"command":"pause","action":"pause"}`), Moonraker `POST /printer/print/pause`, PrusaLink `PUT /api/v1/job/<id>/pause`, Duet `M25`, Repetier `@pause`, Creality printers that run Moonraker, and Bambu `{"print":{"command":"pause"}}` over LAN MQTT or FULU's BambuNetwork session (the connection that printed, as for `cancel_print`).
472
473
 
473
474
  ```json
474
475
  {
@@ -480,7 +481,9 @@ Pause the current print. Pausing is never gated. Routes: OctoPrint `POST /api/jo
480
481
 
481
482
  #### resume_print
482
483
 
483
- Resume a paused print. Resume reheats and moves the printer, so it is gated like a print start. It resumes only the job this server instance inspected and started, after a fresh report that the same job is paused, and after a person confirms through MCP elicitation (unless the server sets `PRINT_REQUIRE_CONFIRMATION=0`). OctoPrint, Klipper and Duet re-download the file and compare its SHA-256 with the inspected bytes before asking; PrusaLink, Repetier and Creality have no download route here, so the job is matched by name only. The printer is read again after the confirmation. Cancelling the job, or a report that it ended, retires it, so a later job that reuses the name is never resumed. A job started at the printer, from another app, or before the server restarted is refused; resume it at the printer. Duet's download route needs the Duet Software Framework, so a standalone board's resume is refused. The result's `bytesVerified` says whether the bytes were re-read. OctoPrint drops a resume sent while it is still pausing, so the server waits for a settled pause (`job.stage` "Pausing" means not yet). Routes: OctoPrint `{"command":"pause","action":"resume"}`, Moonraker `POST /printer/print/resume`, PrusaLink `PUT /api/v1/job/<id>/resume`, Duet `M24`, and Repetier `continueJob`.
484
+ Resume a paused print. Resume reheats and moves the printer, so it is gated like a print start. It resumes only the job this server instance inspected and started, and after a fresh report that the same job is paused. OctoPrint, Klipper and Duet re-download the file and compare its SHA-256 with the inspected bytes, and resume without a prompt. PrusaLink, Repetier and Creality have no download route here, so the job is matched by name only and a person confirms through MCP elicitation first. The printer is read again just before the resume is sent. Cancelling the job, or a report that it ended, retires it, so a later job that reuses the name is never resumed. A job started at the printer, from another app, or before the server restarted is refused; resume it at the printer. Duet's download route needs the Duet Software Framework, so a standalone board's resume is refused. The result's `bytesVerified` says whether the bytes were re-read. OctoPrint drops a resume sent while it is still pausing, so the server waits for a settled pause (`job.stage` "Pausing" means not yet). Routes: OctoPrint `{"command":"pause","action":"resume"}`, Moonraker `POST /printer/print/resume`, PrusaLink `PUT /api/v1/job/<id>/resume`, Duet `M24`, and Repetier `continueJob`.
485
+
486
+ Bambu resumes as bambu-printer-mcp does: the paused job must report the unique name this server started it under (`subtask_name`), the fresh-state check is repeated, and its bytes are re-read over FTPS (the uploaded 3MF, else the `cache/<name>_plate_<n>.gcode` a P1S extracts) and compared. A file this server uploaded that cannot be re-read refuses the resume; only a file FULU's plug-in placed that cannot be re-read asks a person. The resume goes over the connection that printed.
484
487
 
485
488
  #### counterpart_printer_info
486
489
 
@@ -528,7 +531,9 @@ Relative URLs and loopback hosts in the firmware's settings are read as the prin
528
531
 
529
532
  #### get_printer_filaments
530
533
 
531
- Report the loaded filament as one external slot, `{"slots": [{"id": "ext", "material", "color", "name", "source", "profile"}]}`. Moonraker printers with Spoolman report the active spool's material, `#RRGGBB` colour and name (`source: "spoolman"`); every other printer reports `source: "unknown"`. Bambu AMS inventory is in [bambu-printer-mcp](https://github.com/DMontgomery40/bambu-printer-mcp).
534
+ Report the loaded filament as one external slot, `{"slots": [{"id": "ext", "material", "color", "name", "source", "profile"}]}`. Moonraker printers with Spoolman report the active spool's material, `#RRGGBB` colour and name (`source: "spoolman"`); other non-Bambu printers report `source: "unknown"`.
535
+
536
+ Bambu printers report the live AMS from the printer's MQTT report, with bambu-printer-mcp's fields unchanged: `trays` (one per AMS tray, empty ones included: absolute `slot` = AMS id × 4 + tray, `loaded`, `tray_type`, `tray_color`, `tray_info_idx`, `remain_percent`, the resolved Bambu Studio/Orca filament profile `resolved_profile_path` with `profile_resolution` and `match_confidence`, and `display_name`), `summary`, `recommended` and `load_filaments_all`. Profiles resolve by `tray_info_idx` in the slicer's `BBL/filament` folder (`BAMBU_PROFILES_ROOT`, else the `SLICER_PATH` installation), preferring the `bambu_model` and `nozzle_diameter` preset. `slots` lists every loaded tray and the external spool (`"254"`, and `"254"`/`"255"` on dual-nozzle printers) with `source: "printer"`. Pass a tray's `slot` to `print_3mf` as `ams_slots`.
532
537
 
533
538
  Each slot also carries `profile`: the absolute path of the installed slicer filament profile for its material, which `slice_stl` takes as `load_filaments`. It is resolved in the slicer `slice_stl` uses by default (`SLICER_TYPE`, `SLICER_PATH`), for the printer in `SLICER_PROFILE`. OrcaSlicer searches its bundled `profiles/<vendor>/filament`, the `OrcaSlicer/system` copies and the user's `OrcaSlicer/user/<id>/filament` presets, reading `filament_type` and `compatible_printers` through `inherits`. PrusaSlicer and Slic3r search the data folder's `filament/*.ini` presets. The material must equal the profile's `filament_type` exactly (case aside): a `PLA` spool never gets a `PLA-CF` profile. Profiles that list other printers are skipped; one that lists this printer comes first, then profiles for every printer, user presets before bundled ones. With no reported material, `profile` is the filament `slice_stl` loads by default (`FILAMENT_PROFILE`), when that file exists. When nothing resolves, `profile` is omitted.
534
539
 
@@ -548,7 +553,7 @@ Set a target temperature for a printer component. Use `bed` or `extruder`; Bambu
548
553
  ```
549
554
 
550
555
  - `temperature` must be a finite number of 0 or more. `0` switches the heater off and is never gated.
551
- - Positive targets are checked before connecting against independent hardware ceilings and the material's ceiling, need a ready printer, and ask a human to confirm.
556
+ - Positive targets are checked before connecting against independent hardware ceilings and the material's ceiling, and need a ready printer.
552
557
  - Positive nozzle heating needs `material` (for example PLA, PETG, or ABS), including for spools without RFID.
553
558
  - On Bambu printers, positive heating also needs `bambu_model` (or `BAMBU_MODEL`), and nozzle heating uses `nozzle_diameter` (0.2, 0.4, 0.6, or 0.8; default `NOZZLE_DIAMETER` or 0.4). Both are checked against the live printer.
554
559
 
@@ -561,11 +566,19 @@ Set a target temperature for a printer component. Use `bed` or `extruder`; Bambu
561
566
 
562
567
  These tools work with `PRINTER_TYPE=bambu` (or `type: "bambu"`). Set up LAN Only Mode, the serial number, the access code, and the model as described in [Bambu Lab setup](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/SETUP.md#bambu-lab).
563
568
 
569
+ #### inspect_print_file
570
+
571
+ Inspect a local prepared `.3mf` or textual `.gcode` file without connecting to a printer or slicing. Pass `file_path` and an explicit `bambu_model`; optional `plate_index` is zero-based (default 0), `nozzle_diameters` contains one or two supported sizes, and `bed_type` checks the file's declared plate type.
572
+
573
+ The version-1 receipt identifies the exact private snapshot by SHA-256, byte length and format, records the selected plate/model, and includes the existing file inspector's material, nozzle and peak-temperature evidence. `evidence: "file-only"` and `dispatch_authorized: false` are deliberate: this does not establish current hardware readiness or authorize a print. Unsliced projects, missing plates, unsafe temperatures and mismatched metadata reject. Actual printing still performs its existing checks and human confirmation.
574
+
564
575
  #### print_3mf
565
576
 
566
- Upload a sliced `.3mf` project to a Bambu printer over FTPS and start it with an MQTT `project_file` command that carries the plate's G-code path, its MD5, the AMS mapping, and the calibration flags. **`bambu_model` is required** (or `BAMBU_MODEL`); without it the server asks through MCP elicitation when the client supports it, or returns an error. The wrong model can crash the bed into the nozzle.
577
+ `plate_index` selects the same zero-based plate for file checks and dispatch (default 0). If that plate needs slicing, the slicer receives its one-based plate number.
567
578
 
568
- Before anything is uploaded, the server inspects a private copy of the selected plate (model, nozzle, bed type, materials, and every heater target). It checks them against per-model hardware limits, material ceilings, and a fresh MQTT report of the printer's model, serial, nozzle, state, errors, and loaded filament. A human then confirms, and the report is checked again before dispatch.
579
+ Upload a sliced `.3mf` project to a Bambu printer over FTPS and start it with an MQTT `project_file` command that carries the plate's G-code path, its MD5, the AMS mapping, and the calibration flags, or print it through FULU's BambuNetwork bridge (`connection_mode: "bambu_network"`). **`bambu_model` is required** (or `BAMBU_MODEL`); without it the server asks through MCP elicitation when the client supports it, or returns an error. The wrong model can crash the bed into the nozzle.
580
+
581
+ Before anything is uploaded, the server inspects a private copy of the selected plate (model, nozzle, bed type, materials, and every heater target). It checks them against per-model hardware limits, material ceilings, and a fresh MQTT report of the printer's model, serial, nozzle, state, errors, and loaded filament. The report is read again before upload and again before dispatch.
569
582
 
570
583
  ```json
571
584
  {
@@ -573,7 +586,7 @@ Before anything is uploaded, the server inspects a private copy of the selected
573
586
  "bambu_model": "p1s",
574
587
  "bed_type": "textured_plate",
575
588
  "use_ams": true,
576
- "ams_mapping": { "Generic PLA": 0 },
589
+ "ams_slots": [2],
577
590
  "bed_leveling": true,
578
591
  "flow_calibration": true,
579
592
  "vibration_calibration": true,
@@ -583,7 +596,9 @@ Before anything is uploaded, the server inspects a private copy of the selected
583
596
  ```
584
597
 
585
598
  - If the project has no plate G-code, the server tries to [auto-slice it](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/SLICING.md#print_3mf-auto-slicing) with FULU OrcaSlicer-bambulab or Bambu Studio. If slicing fails, or the selected plate still has no G-code, it stops with an error and never uploads the original project.
586
- - `ams_mapping` is an object whose values are AMS slot numbers. When it is omitted, the mapping embedded in the 3MF is used; with no mapping at all, the print runs without AMS. `use_ams: false` turns AMS off.
599
+ - `ams_slots` names one absolute tray per used filament, in plate order: `[2]` prints a one-filament plate from AMS 0 slot 2, `254` is the external spool (on dual-nozzle printers `254` left, `255` right). It is expanded to the project-level `ams_mapping` from the plate's `filament_ids` and the G-code header, and must name exactly one tray per used filament.
600
+ - `ams_mapping` is the raw project-level array (position = project filament, value = tray, `-1` unused), or an object of trays keyed by project filament index. When neither is given, the mapping embedded in the 3MF is used; with no mapping at all, the print runs from the external spool. `use_ams: false` maps every used filament to the external spool. Each mapped tray's material is checked against the file in the fresh printer report.
601
+ - `connection_mode` and `connection_type` choose the route (default `BAMBU_DEFAULT_CONNECTION_MODE` and `BAMBU_DEFAULT_CONNECTION_TYPE`; see [FULU's BambuNetwork route](https://github.com/DMontgomery40/mcp-3D-printer-server/blob/main/docs/FULU.md#print-and-control-through-fulus-bambunetwork-route)). The bridge route sends Bambu Studio's `ams_mapping`, `ams_mapping2` and `ams_mapping_info` and the Send dialog's calibration options as Bambu Studio resolves them.
587
602
  - After sending the command, the server watches fresh reports for up to 15 seconds (`BAMBU_DISPATCH_CHECK_MS`) and returns `dispatch: "started"` or `"unconfirmed"`. If the firmware refuses the command (HMS 0500-0500-0001-0007 on firmware 01.08.05 and later without Developer Mode), the call fails and says so; the checked file stays on the printer's storage.
588
603
  - `nozzle_type` (`stainless_steel`, `hardened_steel`, `tungsten_carbide`, `brass`) sets the installed nozzle when the project must be auto-sliced. The job's nozzle type must match the printer's report.
589
604
  - `bed_type` is one of `textured_plate`, `cool_plate`, `engineering_plate`, or `hot_plate` (default `BED_TYPE`, else `textured_plate`). It must match the plate's bed metadata, so a file sliced for another plate, or without bed metadata, is refused until `bed_type` matches.
@@ -617,6 +632,14 @@ Call one FULU BambuNetwork bridge method for diagnostics. Read-only methods such
617
632
 
618
633
  The bridge command comes from `FULU_BAMBU_BRIDGE_COMMAND`; a per-call `bridge_command` requires `MCP_ALLOW_EXECUTABLE_ARG=1`.
619
634
 
635
+ #### counterpart_print_route
636
+
637
+ Read or change how this server reaches a Bambu printer for prints and for Stop, Pause and Resume, with the same contract as bambu-printer-mcp's. `route` is `lan` (the local network: direct LAN MQTT/FTPS, or FULU's authenticated LAN session when the mode is `bambu_network`) or `cloud` (Bambu Cloud through FULU's bridge). Pass `route` to change it: the next print and control use it at once, and it is saved to the `.env` file in the server's working directory when that file exists. Sends nothing to the printer.
638
+
639
+ ```json
640
+ { "route": "lan", "mode": "bambu_network", "type": "lan", "choices": ["lan", "cloud"], "savedTo": "/path/to/.env" }
641
+ ```
642
+
620
643
  </details>
621
644
 
622
645
  <details>
@@ -724,7 +747,7 @@ Report every heater target in a G-code file: `S` and `R` forms, tool-addressed t
724
747
 
725
748
  #### process_and_print_stl
726
749
 
727
- Extend an STL's base, slice it, and print it through the same checked print gate as `upload_gcode` and `print_3mf`, including the human confirmation. If you pass `extruder_temp` or `bed_temp`, each must equal the sliced job's highest target (`S` and `R` forms, every tool); a mismatch stops before anything is uploaded. Pass `material` when the sliced G-code has no `filament_type` metadata. On a Bambu printer with a Bambu-compatible slicer, the sliced `.3mf` goes through the `print_3mf` checks, and the printer model is required.
750
+ Extend an STL's base, slice it, and print it through the same checked print gate as `upload_gcode` and `print_3mf`. If you pass `extruder_temp` or `bed_temp`, each must equal the sliced job's highest target (`S` and `R` forms, every tool); a mismatch stops before anything is uploaded. Pass `material` when the sliced G-code has no `filament_type` metadata. On a Bambu printer with a Bambu-compatible slicer, the sliced `.3mf` goes through the `print_3mf` checks, and the printer model is required.
728
751
 
729
752
  ```json
730
753
  {
@@ -738,7 +761,7 @@ Extend an STL's base, slice it, and print it through the same checked print gate
738
761
  }
739
762
  ```
740
763
 
741
- Use `slice_stl`, `confirm_temperatures`, and `upload_gcode` separately when you want to review the sliced file before the print is offered for confirmation.
764
+ Use `slice_stl`, `confirm_temperatures`, and `upload_gcode` separately when you want to review the sliced file before it prints.
742
765
 
743
766
  </details>
744
767
 
@@ -860,6 +883,18 @@ Resources follow the MCP resource protocol. They use the configured `PRINTER_TYP
860
883
 
861
884
  For example, `printer://192.168.1.100/status` reads the status of the printer at that address.
862
885
 
886
+ ### Design guidance
887
+
888
+ The server carries manufacturing judgment for agents that design or edit a model to print. It is served as MCP prompts, so the client attaches each piece when it applies; the server's `instructions` tell any client they exist.
889
+
890
+ - `fdm_design`: for every design or model-editing task. Plan how the object prints as part of the design: geometry, orientation, supports, colours and assembly, using the actual printer, filaments and slice. It keeps what the person asked for and judges overhangs, bridges and supports locally, with no fixed 45-degree or bridge-length ban.
891
+ - `fdm_multicolor`: when several colours or support materials share a print: filament transitions across layers, colour placement, plate scheduling and flushing.
892
+ - `fdm_assembly`: when a split, accent insert or joint could materially improve the result.
893
+ - `fdm_after_slice`: a separate review turn after `slice_stl`, before presenting the manufacturing recommendation: keep, revise, or validate one specific uncertainty, using the returned slicer report.
894
+ - `guidance://fdm/capability-guidance` (resource): planning bands and sources for bridges and overhangs, for when one is consequential and there is no sufficiently specific measured capability for the printer, material and profile.
895
+
896
+ bambu-printer-mcp serves the same guidance.
897
+
863
898
  </details>
864
899
 
865
900
  <details>
@@ -872,11 +907,11 @@ For example, `printer://192.168.1.100/status` reads the status of the printer at
872
907
  3. **Plain HTTP for most backends.** OctoPrint, Klipper, Duet, Repetier, and Creality adapters connect over `http://`. The Prusa adapter uses HTTPS for Prusa Connect, an `https://` host, or port 443.
873
908
  4. **Moonraker authentication is optional; Duet has no password support.** Set `API_KEY` for Moonraker hosts that require authentication. Leave it empty only when Moonraker trusts the MCP host. A password-protected Duet cannot be reached yet.
874
909
  5. **Command sent is not print finished.** A success response means the printer or its host accepted the request. Check status, and the printer itself, before you walk away.
875
- 6. **Bambu prints need a sliced project and the right model.** `print_3mf` needs a `.3mf` with `Metadata/plate_<n>.gcode`, uploads it to `cache/`, and starts plate 1. Print settings such as layer height and temperatures cannot be changed at print time. `start_print` handles plain `.gcode` files only.
876
- 7. **Bambu AMS mapping is simple.** `ams_mapping` values are sorted and padded to five entries; real behavior still depends on firmware, loaded filament, and the project's metadata. For AMS inventory and color matching, see [bambu-printer-mcp](https://github.com/DMontgomery40/bambu-printer-mcp).
910
+ 6. **Bambu prints need a sliced project and the right model.** `print_3mf` needs a `.3mf` with `Metadata/plate_<n>.gcode`, uploads it to `cache/`, and starts the zero-based `plate_index` selection (default 0). Print settings such as layer height and temperatures cannot be changed at print time. `start_print` handles plain `.gcode` files only.
911
+ 7. **Bambu AMS choice is explicit.** `ams_slots` and `ams_mapping` choose trays; there is no automatic colour matching here (bambu-printer-mcp has `auto_match_ams`). The LAN mapping is padded to five entries as Bambu's P1/X1 firmware expects; real behavior still depends on firmware, loaded filament, and the project's metadata. The LAN route sends P1/X1-style `project_file` paths for every model; bambu-printer-mcp's H2, P2S and A1 path differences and X2D native route are not in this server.
877
912
  8. **Bambu temperatures go through G-code.** Temperature targets are sent as `M104` or `M140` over MQTT, so the printer's firmware and current state decide whether they apply.
878
913
  9. **Bambu networking assumes a trusted LAN.** MQTT and FTPS use the printer's self-signed certificate. Uploads use TLS 1.2 with session reuse; confirmation on the X1C firmware reported in [#22](https://github.com/DMontgomery40/mcp-3D-printer-server/issues/22) is still outstanding.
879
- 10. **Safety checks read files and reports, not the physical printer.** A declared material cannot prove what spool is loaded, which is why a human confirms. There is no bed check: the printer detects an occupied bed itself during bed leveling. Klipper macro parameters that name a heater are checked, but macro bodies stored on the printer cannot be inspected. On Duet/RepRapFirmware, selecting a tool (`T0`) heats it to the temperature already stored on the printer; activation-only `M568` and `M144` are refused, but a plain tool change is allowed because every firmware uses it. The safety gate is tested with mocked printer transports and loopback HTTP APIs, plus one real Bambu P1S run that reached the firmware's command check; other printer systems have not been exercised on real hardware in this release.
914
+ 10. **Safety checks read files and reports, not the physical printer.** A declared material cannot prove what spool is loaded, and the server does not ask a person to check it: the client's print action is the approval. There is no bed check: the printer detects an occupied bed itself during bed leveling. Klipper macro parameters that name a heater are checked, but macro bodies stored on the printer cannot be inspected. On Duet/RepRapFirmware, selecting a tool (`T0`) heats it to the temperature already stored on the printer; activation-only `M568` and `M144` are refused, but a plain tool change is allowed because every firmware uses it. The safety gate is tested with mocked printer transports and loopback HTTP APIs, plus one real Bambu P1S run that reached the firmware's command check; other printer systems have not been exercised on real hardware in this release.
880
915
  11. **Remote starts need a download route.** `start_print` of a file already on the printer works on Bambu Lab, OctoPrint, Klipper, and Duet. Repetier, Prusa, and Creality refuse it; upload the local G-code with `print: true` instead.
881
916
 
882
917
  </details>
@@ -921,7 +956,7 @@ Prompt injection is an open problem for tool-using agents. A downloaded model's
921
956
  - Treat tool output and downloaded files as untrusted input.
922
957
  - Keep printer keys and access codes in server configuration, with the least privilege your printer software allows.
923
958
  - Keep per-call executable selectors off. Slicer, bridge, and Blender commands come from server configuration unless `MCP_ALLOW_EXECUTABLE_ARG=1` is set.
924
- - Keep print confirmation on. The server asks a human through MCP elicitation before every print start and positive heating command, and refuses clients that cannot ask. `PRINT_REQUIRE_CONFIRMATION=0` (all printers) or `BAMBU_REQUIRE_CONFIRMATION=0` (Bambu only) opts out for headless setups; the first print after a finished job still asks.
959
+ - Prints and positive heating start without a server prompt: the client's own print action is the approval, so give print tools only to clients and agents you trust to start a print. The file, ceiling and printer-state checks still run, and there is no bed check, so clear the plate before you print.
925
960
  - Heater ceilings come only from server configuration (`PRINTER_MAX_NOZZLE_TEMP`, `PRINTER_MAX_BED_TEMP`, `PRINTER_MAX_CHAMBER_TEMP`, or Bambu's per-model limits). Tool arguments and G-code never raise them.
926
961
  - Run the streamable HTTP transport on a trusted network. It binds to `127.0.0.1` by default and has no built-in authentication.
927
962
  - Set `BAMBU_MODEL` correctly and never substitute a similar model.
@@ -0,0 +1,38 @@
1
+ /** Tray-value options. `dualNozzle` admits 255, the right/main nozzle external spool that
2
+ * dual-nozzle printers report beside the left/deputy external spool 254 (Bambu Studio
3
+ * VIRTUAL_TRAY_MAIN_ID / VIRTUAL_TRAY_DEPUTY_ID). Single-nozzle printers keep 254 only. */
4
+ export interface AmsTrayValueOptions {
5
+ dualNozzle?: boolean;
6
+ }
7
+ export declare function describeAmsTrayRange(options?: AmsTrayValueOptions): string;
8
+ export declare function normalizeBridgeAmsTrayValue(value: unknown, label: string, options?: AmsTrayValueOptions): number;
9
+ export declare function normalizeAmsMappingObject(mapping: Record<string, unknown>, label?: string, options?: AmsTrayValueOptions): number[];
10
+ export declare function hasAmsMappingInput(value: unknown): boolean;
11
+ /** Flat wire entries use -1 for dual-nozzle virtual trays; ams_mapping2 identifies their spool. */
12
+ export declare function bridgeAmsMappingValue(value: number, dualNozzle?: boolean): number;
13
+ /** Select an external spool only from a verified physical nozzle on dual-nozzle printers. */
14
+ export declare function externalTrayForNozzle(nozzle: number | undefined, dualNozzle?: boolean): number;
15
+ /** Virtual trays use slot zero on dual-nozzle printers; retain the historical single-nozzle form. */
16
+ export declare function bridgeAmsMapping2Entry(value: number, dualNozzle?: boolean): {
17
+ ams_id: number;
18
+ slot_id: number;
19
+ };
20
+ /** A project filament as the sliced 3MF's Metadata/project_settings.config declares it. */
21
+ export interface ProjectFilament {
22
+ type: string;
23
+ filamentId: string;
24
+ colour: string;
25
+ nozzle?: number;
26
+ }
27
+ /** Read every project filament (not just the plate's used ones) from project_settings.config. */
28
+ export declare function projectFilaments(projectSettings: Record<string, unknown>): ProjectFilament[];
29
+ /** The colour the printer reports for an absolute tray value (AMS, AMS-HT or external spool). */
30
+ export declare function trayColour(rawStatus: any, tray: number): string | undefined;
31
+ /**
32
+ * PrintParams.ams_mapping_info exactly as Bambu Studio and FULU's slicer build it
33
+ * (SelectMachineDialog::get_ams_mapping_result) and send it with every print: one entry per
34
+ * project filament. A mapped filament names its tray, its filament id and type, its colour as
35
+ * #RRGGBBAA and the tray's colour as the printer reports it; an unmapped one carries ams -1.
36
+ * Filament-map nozzle 1 (left) is cloud nozzle 1 and 2 (right) is 0 (CloudTaskNozzleId).
37
+ */
38
+ export declare function studioAmsMappingInfo(filaments: ProjectFilament[], mapping: number[], colourOfTray: (tray: number) => string | undefined): Array<Record<string, unknown>>;
@@ -0,0 +1,125 @@
1
+ export function describeAmsTrayRange(options = {}) {
2
+ return options.dualNozzle
3
+ ? "[-1, 15] (absolute tray), 128-253 (AMS-HT), 254 (left/deputy external) or 255 (right/main external)"
4
+ : "[-1, 15] (absolute tray) or 128-254 (HT/external)";
5
+ }
6
+ export function normalizeBridgeAmsTrayValue(value, label, options = {}) {
7
+ let numeric;
8
+ if (typeof value === "number") {
9
+ numeric = value;
10
+ }
11
+ else if (typeof value === "string" && value.trim() !== "") {
12
+ numeric = Number(value);
13
+ }
14
+ else {
15
+ throw new Error(`${label} must be an integer tray index; got ${JSON.stringify(value)}`);
16
+ }
17
+ if (!Number.isInteger(numeric) ||
18
+ numeric < -1 ||
19
+ (numeric > 15 && numeric < 128) ||
20
+ numeric > (options.dualNozzle ? 255 : 254)) {
21
+ throw new Error(`${label} must be an integer in ${describeAmsTrayRange(options)}; got ${JSON.stringify(value)}`);
22
+ }
23
+ return numeric;
24
+ }
25
+ function isNumericMappingKey(key) {
26
+ const numeric = Number(key);
27
+ return Number.isInteger(numeric) && numeric >= 0;
28
+ }
29
+ export function normalizeAmsMappingObject(mapping, label = "ams_mapping", options = {}) {
30
+ return Object.entries(mapping)
31
+ .sort(([leftKey], [rightKey]) => {
32
+ const leftNumeric = isNumericMappingKey(leftKey);
33
+ const rightNumeric = isNumericMappingKey(rightKey);
34
+ if (leftNumeric && rightNumeric)
35
+ return Number(leftKey) - Number(rightKey);
36
+ if (leftNumeric)
37
+ return -1;
38
+ if (rightNumeric)
39
+ return 1;
40
+ return 0;
41
+ })
42
+ .map(([key, value]) => normalizeBridgeAmsTrayValue(value, `${label}[${key}]`, options));
43
+ }
44
+ export function hasAmsMappingInput(value) {
45
+ if (Array.isArray(value))
46
+ return value.length > 0;
47
+ if (value && typeof value === "object")
48
+ return Object.keys(value).length > 0;
49
+ return value !== undefined && value !== null;
50
+ }
51
+ /** Flat wire entries use -1 for dual-nozzle virtual trays; ams_mapping2 identifies their spool. */
52
+ export function bridgeAmsMappingValue(value, dualNozzle = false) {
53
+ return dualNozzle && (value === 254 || value === 255) ? -1 : value;
54
+ }
55
+ /** Select an external spool only from a verified physical nozzle on dual-nozzle printers. */
56
+ export function externalTrayForNozzle(nozzle, dualNozzle = false) {
57
+ if (!dualNozzle)
58
+ return 254;
59
+ if (nozzle === 0)
60
+ return 255;
61
+ if (nozzle === 1)
62
+ return 254;
63
+ throw new Error("External-spool printing requires inspected filament_map and physical_extruder_map nozzle bindings.");
64
+ }
65
+ /** Virtual trays use slot zero on dual-nozzle printers; retain the historical single-nozzle form. */
66
+ export function bridgeAmsMapping2Entry(value, dualNozzle = false) {
67
+ if (value < 0)
68
+ return { ams_id: 255, slot_id: 255 };
69
+ if (value === 255)
70
+ return dualNozzle ? { ams_id: 255, slot_id: 0 } : { ams_id: 255, slot_id: 255 };
71
+ if (value === 254)
72
+ return { ams_id: 254, slot_id: dualNozzle ? 0 : 254 };
73
+ if (value >= 128)
74
+ return { ams_id: 128, slot_id: value - 128 };
75
+ return { ams_id: Math.floor(value / 4), slot_id: value % 4 };
76
+ }
77
+ /** Read every project filament (not just the plate's used ones) from project_settings.config. */
78
+ export function projectFilaments(projectSettings) {
79
+ const list = (key) => Array.isArray(projectSettings[key]) ? projectSettings[key].map(String) : [];
80
+ const types = list("filament_type"), ids = list("filament_ids"), colours = list("filament_colour"), nozzles = list("filament_map");
81
+ return types.map((type, index) => ({
82
+ type, filamentId: ids[index] ?? "", colour: colours[index] ?? "",
83
+ ...(nozzles[index] !== undefined && Number.isInteger(Number(nozzles[index])) ? { nozzle: Number(nozzles[index]) } : {}),
84
+ }));
85
+ }
86
+ /** The colour the printer reports for an absolute tray value (AMS, AMS-HT or external spool). */
87
+ export function trayColour(rawStatus, tray) {
88
+ const units = Array.isArray(rawStatus?.ams?.ams) ? rawStatus.ams.ams : [];
89
+ let colour;
90
+ if (tray === 254 || tray === 255) {
91
+ const slots = Array.isArray(rawStatus?.vir_slot) ? rawStatus.vir_slot : [];
92
+ colour = slots.find(slot => Number(slot?.id) === tray)?.tray_color ?? (tray === 254 ? rawStatus?.vt_tray?.tray_color : undefined);
93
+ }
94
+ else if (tray >= 128) {
95
+ colour = units.find(unit => Number(unit?.id) === tray)?.tray?.[0]?.tray_color;
96
+ }
97
+ else if (tray >= 0) {
98
+ const unit = units.find(candidate => Number(candidate?.id) === Math.floor(tray / 4));
99
+ colour = (Array.isArray(unit?.tray) ? unit.tray : []).find((slot) => Number(slot?.id) === tray % 4)?.tray_color;
100
+ }
101
+ return typeof colour === "string" ? colour : undefined;
102
+ }
103
+ /**
104
+ * PrintParams.ams_mapping_info exactly as Bambu Studio and FULU's slicer build it
105
+ * (SelectMachineDialog::get_ams_mapping_result) and send it with every print: one entry per
106
+ * project filament. A mapped filament names its tray, its filament id and type, its colour as
107
+ * #RRGGBBAA and the tray's colour as the printer reports it; an unmapped one carries ams -1.
108
+ * Filament-map nozzle 1 (left) is cloud nozzle 1 and 2 (right) is 0 (CloudTaskNozzleId).
109
+ */
110
+ export function studioAmsMappingInfo(filaments, mapping, colourOfTray) {
111
+ return filaments.map((filament, position) => {
112
+ const tray = mapping[position];
113
+ if (tray === undefined || tray < 0)
114
+ return { ams: -1, targetColor: "", filamentId: "", filamentType: "" };
115
+ const hex = filament.colour.replace(/^#/, "").toUpperCase();
116
+ return {
117
+ ams: tray,
118
+ targetColor: colourOfTray(tray) ?? "",
119
+ filamentId: filament.filamentId,
120
+ filamentType: filament.type,
121
+ ...(filament.nozzle === 1 ? { nozzleId: 1 } : filament.nozzle === 2 ? { nozzleId: 0 } : {}),
122
+ sourceColor: /^[0-9A-F]{6}$/.test(hex) ? `#${hex}FF` : /^[0-9A-F]{8}$/.test(hex) ? `#${hex}` : "",
123
+ };
124
+ });
125
+ }