MCP server for the Elegoo Centauri Carbon 3D printer, built on the SDCP 3.0
protocol as documented by the OpenCentauri
project (docs/software/api.md). Works over the local network — no cloud.
Read-only (no approval needed)
| Tool | Description |
|---|---|
discover_printers |
UDP broadcast discovery (port 3000) |
get_status |
Temps, position, fans, lights, live print progress |
get_attributes |
Firmware, build volume, capabilities, storage |
list_files |
Files on /local/ or /usb/ storage |
get_print_history |
Past jobs with decoded failure reasons + fixes |
get_print_stats |
Success rate, print hours, failure-cause breakdown |
get_timelapse |
Per-job timelapse video URL / MP4 download |
get_recent_events |
Errors/notices pushed by the printer |
decode_error |
SDCP error code → cause + suggested fix |
get_monitor_status |
Monitor state and log |
Control (annotated so MCP clients require approval)
| Tool | Description |
|---|---|
start_print |
Start a file; start_layer resumes a failed print mid-way |
pause_print / resume_print |
Pause/resume the active job |
stop_print |
Cancel (irreversible; requires confirm=true) |
skip_preheating / stop_material_feeding |
Phase controls |
set_speed_profile |
silent/balanced/sport/ludicrous or a raw % |
set_fan_speeds |
Part-cooling / auxiliary / chamber fans individually |
set_timelapse |
Enable/disable timelapse recording |
set_printer_name |
Rename the printer |
upload_gcode / send_and_print |
MD5-verified HTTP upload (± auto-start) |
delete_files |
Permanent delete (requires confirm=true) |
get_snapshot |
Chamber-camera JPEG (toggles the single video stream slot) |
start_monitor / stop_monitor |
Start/stop the detached watcher (stop_after_print for one-shot) |
set_pushover_credentials |
Save Pushover keys for the watcher |
set_notification_settings |
Global priority and sound for all alerts |
get_notification_settings |
Current settings + all valid options (read-only) |
send_test_notification |
Verify the notification path end to end |
One global setting covers every alert the watcher sends — there is no per-event configuration:
set_notification_settings(priority=1, sound="cosmic")
Either argument may be given alone; the other is left unchanged. Settings are re-read on every send, so a running watcher picks up changes without a restart.
| Priority | Behavior |
|---|---|
| -2 | Lowest — no notification, badge only |
| -1 | Low — no sound or vibration |
| 0 | Normal — default alert |
| 1 | High — bypasses quiet hours |
| 2 | Emergency — repeats until acknowledged (retry/expire added automatically) |
Sounds: pushover, bike, bugle, cashregister, classical, cosmic,
falling, gamelan, incoming, intermission, magic, mechanical,
pianobar, siren, spacealarm, tugboat, alien, climb, persistent,
echo, updown, vibrate, none.
With nothing set, priority falls back to per-event defaults (pause and printer errors high, stop and complete normal) and the sound is your Pushover account default. Setting a global priority overrides those defaults everywhere.
Environment equivalents: PUSHOVER_PRIORITY, PUSHOVER_SOUND (the config
file wins if both are present).
start_monitor spawns centauri_mcp.watcher as a detached process. It
outlives the MCP server and your client session, so alerts still arrive hours
into a print with nothing open.
It holds a WebSocket to the printer and reacts to every pushed status
message rather than polling a cache — a brief Complete -> Idle transition
cannot slip between samples. Notifications fire on:
| Event | Pushover priority |
|---|---|
| Print paused (with reason when the printer reports one) | 1 |
| Print stopped | 0 |
| Print complete | 0 |
Printer error pushed on sdcp/error |
1 |
Each carries filename, layer, percent complete, and a camera snapshot unless
started with with_snapshots=false.
By default the watcher is continuous: after notifying you it keeps
running, so subsequent prints are covered too. It stops only on
stop_monitor, the stop flag, a kill, or a reboot.
Pass stop_after_print=true for one-shot mode — it shuts itself down once
the print reaches Complete or Stopped, after the notification has been sent. A
pause does not end it, since the print can still resume.
start_monitor(stop_after_print=True)
Run it standalone (e.g. from Task Scheduler at logon, so it covers prints started from the printer's own screen):
python -m centauri_mcp.watcher --ip 192.168.1.50
python -m centauri_mcp.watcher --ip 192.168.1.50 --exit-on-complete
State lives in ~/.centauri-mcp/:
| File | Purpose |
|---|---|
watcher_state.json |
Live state + heartbeat, read by get_monitor_status |
watcher.log |
Human-readable event log |
watcher.pid |
Liveness check / stop target |
watcher.stop |
Touch to request a graceful exit |
pushover.json |
Credentials, when not supplied via env |
Override the directory with CENTAURI_MCP_HOME. It deliberately avoids
%LOCALAPPDATA%: the Microsoft Store build of Python virtualizes AppData into
a per-package sandbox, which hides these files from you and from any other
interpreter.
git clone https://github.com/blanders2/centauri-mcp.git
cd centauri-mcp
pip install -e .
Register with Claude Code:
claude mcp add centauri-carbon -- python -m centauri_mcp.server
CENTAURI_IP— printer IP; skips discovery (recommended if your printer has a DHCP reservation). Without it, the first discovered printer is used.PUSHOVER_TOKEN/PUSHOVER_USER— enables phone notifications from the watcher (print complete / paused / stopped / errors, with camera snapshot attached). The detached watcher inherits these from the MCP server; to run it independently at boot, useset_pushover_credentialsinstead, which writes~/.centauri-mcp/pushover.json.CENTAURI_MCP_HOME— override the watcher's state directory.
Example registration with env vars:
claude mcp add centauri-carbon -e CENTAURI_IP=192.168.1.50 -e PUSHOVER_TOKEN=xxx -e PUSHOVER_USER=yyy -- python -m centauri_mcp.server
- Tools that can affect a running print carry MCP annotations
(
readOnlyHint: false, anddestructiveHint: truefor stop/delete), so clients prompt for approval before running them. stop_printanddelete_filesadditionally require an explicitconfirm=trueargument — a bare call refuses and explains why.- The dangerous reverse-engineered config G-codes (
M8803/M8807, which can brick the printer) are deliberately not exposed. - The printer allows only one concurrent MJPEG stream;
get_snapshotenables the stream, grabs one frame, and releases it.
- WebSocket JSON on
ws://<ip>:3030/websocket; UDP discoveryM99999on port 3000; MJPEG camera on port 3031; HTTP multipart upload with MD5 check. - Several SDCP field names are misspelled in the protocol itself
(
CurrenCoord,RelaseFilmState, ...) — the client handles both spellings.