Skip to main content
Glama
bambu_mcp

bambu_mcp

README.md
# bambu_mcp

MCP server (Python, stdio) that lets Claude control Bambu Lab printers over the LAN
(MQTT/TLS on 8883, FTPS on 990) and offloads long work (slice -> upload -> print -> watch) to a
background job manager. Claude only polls (`job_status`) or long-polls (`job_wait`).

## Setup

```
cd ~/projects/bambu_mcp
uv venv .venv && uv pip install --python .venv/bin/python mcp paho-mqtt pytest
.venv/bin/python -m pytest -q tests          # mock-based, never touches a printer
.venv/bin/python tests/stdio_smoke.py        # read-only + dry-run against the real printers
```

Printer credentials live OUTSIDE the repo in `~/.config/bambu_mcp/printers.json` (mode 0600,
`{"a1mini": {"ip","serial","code","name"}, ...}`). If the file is missing it is fetched once from
`streamer-officina:/home/tommaso/mmu_say/bambu.json` over ssh (25 s hard timeout) and cached.
Access codes and serials are never logged or returned (they are scrubbed from all output; the FTPS
password is passed to curl through stdin, not argv).

## Tools

Read-only: `list_printers`, `printer_status`, `ams_info`, `recent_events`.

Jobs (return `job_id` immediately): `print_3mf`, `slice_and_print`, `slice_model`, `watch_print`;
manage with `job_status`, `job_wait(job_id, timeout_s<=240)`, `job_list`, `job_cancel`.

Control: `pause`, `resume`, `stop` (confirm), `set_light`, `set_speed`, `send_gcode` (confirm).

- `tray` is the AMS tray index `ams_id*4 + slot_index` (slot 3 of AMS 0 = `2`).
- `print_3mf` needs a sliced `.3mf` containing `Metadata/plate_N.gcode`. It refuses unless the printer
  is IDLE/FINISH and every tray matches `expect_filament` / `expect_color` (RRGGBBAA or RRGGBB).
- With `watch=true` the job follows the print to FINISH (done), FAILED (failed) or IDLE-after-run
  (done, "stopped"). `job_cancel` stops the server-side job only, not the physical print.
- `slice_and_print` uses OrcaSlicer CLI (`BAMBU_ORCA_BIN`, default
  `~/projects/orca-pr12998/build/src/orca-slicer`) with `machine_flat.json`, `process_flat.json`,
  `filament_flat.json` from `profile_dir`; outputs go to `~/.local/state/bambu_mcp/slices/<job_id>/`.

## Persistence

Job snapshots and print events (state changes, 10% progress milestones, errors, finish) are appended to
`~/.local/state/bambu_mcp/events.jsonl`. After a restart, old jobs are visible (`job_list`); in-flight
ones are marked `interrupted`, and a background re-attach starts a new `watch_print` job for any printer
that is still mid-print.

## Safety rules

- `stop` and `send_gcode` need `confirm=true`; set it only after explicit user approval.
- `dry_run=true` per call, or `BAMBU_MCP_DRY_RUN=1`: builds and returns the payload, never publishes or
  uploads. `print_3mf` dry-run still reads printer state (read-only) and reports `problems`.
- `BAMBU_MCP_READONLY=a1mini,p1s` (or `*`): every control command and print start for those printers is
  refused (dry-run still allowed). The smoke test forces `*`.
- Unit tests use a fake MQTT client and a fake curl; no network.

## Register in a Claude config (not done globally)

```json
{
  "mcpServers": {
    "bambu": {
      "command": "/home/tommaso/projects/bambu_mcp/.venv/bin/python",
      "args": ["-m", "bambu_mcp"],
      "cwd": "/home/tommaso/projects/bambu_mcp"
    }
  }
}
```

## Status of verification

Verified on hardware: read-only status of the A1 mini, FTPS/MQTT payload shapes are the ones proven
manually. NOT verified on hardware through this server: real print start/upload, pause, resume, stop,
light (`work_light` vs `chamber_light` node names), speed, gcode.

## License

MIT, see [LICENSE](LICENSE).