Skip to main content
Glama
billford

mcp-bambu

by billford

mcp-bambu

Local-network tooling for a small fleet of Bambu Lab 3D printers: an MCP server so Claude Code can see and drive the machines, an always-on monitor that turns the MQTT telemetry stream into alerts and a durable event log, and a media sync. No cloud dependency — everything talks to the printers directly over the LAN.

It exists because the printers are poor witnesses to their own failures. A P1S will report FINISH on a print that produced a six-hour bird's nest, its own logs are encrypted, and the only usable record of what actually happened is the live MQTT stream — which nothing keeps unless you keep it.

entry point

run as

what it does

bambu_mcp.server

MCP (Claude Code)

status, files, print control, filament, maintenance, diagnosis

bambu_mcp.monitor

launchd, always on

MQTT watcher; alerts, event log, filament billing, maintenance clock

bambu_mcp.sync

launchd, hourly

pulls timelapses to the NAS

bambu_mcp.export

by hand

render + lint + record a model (see below)

Three entry points, one config, one set of credentials.

Requirements

  • Python 3.11+

  • A Bambu Lab printer reachable on the LAN, and its access code (printer screen → Settings → Network/WLAN)

  • Optional: an ntfy server for push notifications

  • Optional, for bambu_mcp.export: OpenSCAD and print-lint

Related MCP server: CNCjs MCP Server

Install

git clone <this repo> && cd mcp-bambu
python3 -m venv .venv && . .venv/bin/activate
pip install -e .

The editable install is not optional — see the first gotcha below.

Configure

cp config.example.toml config.toml     # printers, paths, ntfy
cp creds.example.toml  creds.toml      # access codes
chmod 600 creds.toml

Both real files are gitignored. config.toml holds printers, paths and ntfy; creds.toml holds access codes and the ntfy password. Adding a printer is one [[printers]] block plus its access code, and it joins all three entry points automatically.

serial must be exact — it is the MQTT topic (device/<serial>/report). Both it and the IP are on the same screen as the access code.

Alert routing lives in alert_rules.toml, re-read on every event so edits need no restart. A missing or corrupt rules file fails open — everything alerts — because on this fleet quiet has meant broken. Maintenance rules deliberately fail quiet: a missed reminder is an inconvenience, a missed fault is not.

Run

python -m bambu_mcp.monitor          # foreground; run under launchd in anger
python -m bambu_mcp.server           # registered as an MCP server in Claude Code

Checks

pip install -e ".[dev]"
pytest tests/                        # 105 tests, no hardware needed
pylint bambu_mcp tests               # 10.00/10
bandit -c pyproject.toml -r .        # 0 issues

Both linters are configured in pyproject.toml, and every disabled check carries the reason it is disabled. Nothing is silenced to raise a score: the subprocess warnings are accepted because the calls pass argument lists and never touch a shell, and the two # nosec B310 markers point at the scheme guard in Notifier._request() that bandit cannot see from the call site.

The dashboard

A static page, published to Cloudflare Pages. There is no server and no API: publish.py reads what the monitor already wrote and exports site/data/*.json; site/index.html fetches them.

python -m bambu_mcp.publish     # export only
./run_and_publish.sh            # export, then wrangler pages deploy

Freshness is the whole problem with a static page. It is a snapshot, and a stale one is indistinguishable from a calm fleet - which is this repo's oldest lesson. So there are two guards, deliberately overlapping:

  • run_and_publish.sh turns a failed export or a failed deploy into an ntfy alert. Neither step can report its own death, and Cloudflare will happily keep serving the last good build forever.

  • Every payload is stamped, and the page computes its own age in the browser. A JSON file cannot know how long it has been sitting there; only the viewer's clock can tell a fresh deploy from a fossil. So the page says so even if the publisher never runs again.

Cadence is a parameter: fast while anything is printing, slow when the fleet is idle. any_active in the payload exists for a wrapper to decide that. See com.billfordx.bambu-publish.plist.example - an example, not an installed job.

What is published. No IPs, serials, access codes, hostnames or filesystem paths - the payload is built to carry none of them, and a test asserts it. Job names DO appear, because they are what a dashboard is for; set redact_jobs = true under [publish] to leave them out. Generated payloads are gitignored, so they reach Cloudflare and never the repo.

Findings that cost real time — do not re-derive them

  1. The printers' own .log files are encrypted. Only Bambu support can read them. Shipping them anywhere is pointless; the live MQTT report stream is the only usable local fault source.

  2. MQTT (8883), FTPS (990) and the camera (6000) all work in CLOUD mode on the P1S. LAN Only is not required for monitoring — but the H2D will not display an access code outside LAN Only, which is the real reason to convert it. Note the cost: LAN Only ends Bambu Handy, which is cloud-only.

  3. Reads work; starting a print may not. A project_file command was refused by a cloud-mode P1S with HMS_0500_0500_0001_0007"MQTT Command verification failed" — while system/ledctrl succeeded over the same connection seconds later. Monitoring is unaffected.

  4. Undocumented HMS codes are in a table on your own disk. The public wiki 404s on some of them. Bambu Studio caches the real per-model table at ~/Library/Application Support/BambuStudio/hms/hms_en_<model>.json (01P = P1S). Search it for the code with the underscores stripped.

  5. One MQTT client per printer. Several short-lived connections opened a few seconds apart made both machines return empty reports — no error, no refusal, no log line. A second collector does not fail loudly, it silently returns nothing. Read this project's event log instead of opening a second connection.

The export wrapper

python -m bambu_mcp.export ufo.scad -o ufo_body.stl -D part=body --layer-height 0.2

Renders, lints and records job → source + parameters in one step. Use it instead of calling openscad directly — the manifest entry is what makes a later failure diagnosable, since the printer only ever knows a filename.

Diagnosis

On FAILED or PAUSE the monitor classifies the failure and pushes a second notification saying why. The machine's own fault reporting outranks anything inferred: if the printer raised an HMS code, that is the answer and the geometry is not touched. Only when no fault was reported does it re-lint the source and locate the problem at the height the print stopped.

bambu_repair will then sweep the recorded parameters, render each candidate with OpenSCAD and score it with print-lint, so a proposal is only ever returned because it measurably beat the original. If nothing improves, that is the answer — "this needs a shape change, not a number."

Both run on a background thread. A dozen OpenSCAD renders on paho's callback thread would stall telemetry for minutes and trip the staleness alert.

Gotchas

  • mcp is pinned <2. 2.x removed the low-level Server decorator API this is written against.

  • The package must be pip install -e .'d into its venv. It is registered as python -m bambu_mcp.server, and -m resolves from the current working directory — so it starts fine from the project dir and fails everywhere else, which is where Claude Code actually launches it. Worse, claude mcp list inherits the shell's cwd, so running it from the project directory reports a cheerful "Connected" for a server the session cannot start.

  • The P1S never decrements filament remaining. remain is -1 on every slot, tagged or not. Hence filament.py, which bills each finished job from the slicer's own figures. The H2D does report it, so the same field carries two different confidence levels depending on the machine.

  • AMS trays are keyed by unit and tray, never tray alone. Both AMS units number their trays 0-3, so flattening on tray id gave the H2D's eight trays four ledger keys — the units overwrote each other on every sync, zeroing used_g and reporting spool swaps that never happened. Keys are A1..A4 / B1..B4, lettered because Bambu's own fault strings say "AMS A". Old numeric ledgers migrate on read.

  • chamber_temper is not a sensor on the P1S. It stayed pinned at exactly 5 through a 180 °C swing while nozzle and bed tracked normally. The H2D omits the key entirely. One machine lies, the other is silent — do not default it to zero and plot it.

  • subtask_name is empty at FINISH, and is often the slicer profile rather than the model (0.16mm layer, 2 walls, 10% infill). The job name only appears during SLICING/PREPARE, so the monitor caches the last one it saw — without that, no finished print could ever be charged.

  • SD card filenames contain spaces and commas. Parse FTP listings with split(maxsplit=8) and percent-encode URLs, or files silently vanish.

  • Timelapse is a slicer setting and does not persist across projects. With it off there is no independent check on a completion at all, which matters given the P1S will report FINISH on a failure. The monitor fires finish_unverified when a print ends with no recent timelapse.

Tested on

A Bambu Lab P1S and an H2D, on macOS, with launchd running the monitor and the sync. Nothing here is macOS-specific except the launchd plists and the Bambu Studio cache paths; the rest is plain Python over MQTT and FTPS.

This is personal tooling for one small fleet, published because the findings above were expensive to arrive at and are hard to find written down anywhere else. It is not a product and carries no support promise.

License

MIT - see LICENSE.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Enables comprehensive control and monitoring of Bambu Lab 3D printers through Claude using local MQTT, FTPS, and X.509 authentication. Users can manage print jobs, monitor real-time status, handle filament through AMS, and adjust hardware settings like temperature and lighting.
    24
    30
    17
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Bridges Claude Code to CNCjs to enable remote control and monitoring of GRBL-based CNC machines. It provides a comprehensive toolset for managing G-code jobs, machine movement, and safety operations through natural language.
    30
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables users to connect Claude with various 3D printer management systems, allowing tasks like printer status monitoring, file management, print job control, and STL file manipulation.
    103
    GPL 2.0

View all related MCP servers

Related MCP Connectors

  • WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.

  • Connect Claude to Fathom meeting recordings, transcripts, and summaries

  • Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/billford/mcp-bambu'

If you have feedback or need assistance with the MCP directory API, please join our Discord server