centauri-mcp
by blanders2
README.md
# centauri-mcp
MCP server for the **Elegoo Centauri Carbon** 3D printer, built on the SDCP 3.0
protocol as documented by the [OpenCentauri](https://github.com/OpenCentauri/OpenCentauri)
project (`docs/software/api.md`). Works over the local network — no cloud.
## Features
**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 |
## Notification priority and sound
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).
## The watcher
`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`.
### Continuous vs one-shot
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.
## Install
```
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
```
## Configuration (environment variables, all optional)
- `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, use `set_pushover_credentials` instead, 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
```
## Safety design
- Tools that can affect a running print carry MCP annotations
(`readOnlyHint: false`, and `destructiveHint: true` for stop/delete), so
clients prompt for approval before running them.
- `stop_print` and `delete_files` additionally require an explicit
`confirm=true` argument — 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_snapshot`
enables the stream, grabs one frame, and releases it.
## Protocol notes
- WebSocket JSON on `ws://<ip>:3030/websocket`; UDP discovery `M99999` on
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.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues