Python client and MCP server for Elegoo Centauri Carbon 3D printers
Project description
pycentauri
Python client, CLI, and MCP server for the Elegoo Centauri Carbon 3D printer.
pycentauri speaks the printer's native SDCP v3 protocol over its local
WebSocket (port 3030) — no cloud account required. It exposes five surfaces:
- An async Python library for direct integration.
- A
centauriCLI for quick status checks, snapshots, and control. - An MCP server so AI agents (Claude Code, Claude Desktop, Cursor, any MCP-compatible client) can monitor and drive the printer as a tool.
- An HTTP + SSE server for dashboards, reverse-proxy integration, and anything that wants a plain REST API.
- A built-in web UI (dark theme, mobile-friendly) served at
/ui/by the HTTP server — live webcam, progress, temperatures, and control.
Status: alpha. The protocol has been reverse-engineered from the official
elegoo-linkC++ SDK and theCentauriLinkproject. It works against the original Centauri Carbon on current firmware. The newer Centauri Carbon 2 (which uses MQTT) is not supported.
Install
pip install pycentauri # library + CLI
pip install "pycentauri[mcp]" # + MCP server
pip install "pycentauri[server]" # + HTTP REST/SSE server
pip install "pycentauri[mcp,server]" # everything
Quick start — CLI
# Find printers on your LAN
centauri discover
# One-shot status (pretty or JSON)
centauri status --host 192.168.1.209
centauri status --host 192.168.1.209 --json
# Stream live status updates
centauri watch --host 192.168.1.209
# Grab a webcam snapshot
centauri snapshot --host 192.168.1.209 shot.jpg
# Files on the printer
centauri files --host 192.168.1.209
# Control actions require --enable-control
centauri print start cube.gcode --host 192.168.1.209 --enable-control
centauri print pause --host 192.168.1.209 --enable-control
centauri print resume --host 192.168.1.209 --enable-control
centauri print stop --host 192.168.1.209 --enable-control
The host can also come from PYCENTAURI_HOST or ~/.config/pycentauri/config.toml.
Quick start — Python
import asyncio
from pycentauri import Printer
async def main():
async with await Printer.connect("192.168.1.209") as printer:
status = await printer.status()
print(status.state, status.progress, status.temp_nozzle)
jpeg = await printer.snapshot()
with open("shot.jpg", "wb") as f:
f.write(jpeg)
async for update in printer.watch():
print(update.state, update.progress)
asyncio.run(main())
Quick start — HTTP server
# Read-only, bound to loopback so only this box can hit it:
centauri server --host 192.168.1.209 --port 8787
# Read + write, bound to all interfaces (put a reverse proxy in front):
centauri server --host 192.168.1.209 --bind 0.0.0.0 --port 8787 --enable-control
| Method | Path | Notes |
|---|---|---|
GET |
/ |
Redirects to /ui/ |
GET |
/ui/ |
Built-in web dashboard |
GET |
/api/info |
Health + version (JSON) |
GET |
/status |
Latest status push (cached; updates every ~5s) |
GET |
/attributes |
Printer attributes |
GET |
/snapshot |
image/jpeg response |
GET |
/stream |
MJPEG stream proxied from the printer (embeds in <img>) |
GET |
/discover |
LAN scan |
GET |
/events/status |
Server-Sent Events stream of pushes |
POST |
/print/start |
Body: {"filename": "cube.gcode"}. Requires --enable-control. |
POST |
/print/{pause,resume,stop} |
Requires --enable-control. |
The server holds a single long-lived WebSocket to the printer and reuses it for every request — no per-request reconnect, and it won't bump into the firmware's 5-slot limit.
Quick start — MCP
Register the server with your agent. With Claude Code:
claude mcp add pycentauri -- python -m pycentauri.mcp
# or, with control actions enabled (gives the agent start/pause/stop/upload):
claude mcp add pycentauri -- python -m pycentauri.mcp --enable-control
Set PYCENTAURI_HOST in your MCP server env so the agent can't target an
arbitrary IP. The server exposes these tools:
| Tool | Always available | Description |
|---|---|---|
get_status |
yes | State, temperatures, progress, elapsed/remaining |
get_attributes |
yes | Model, firmware, mainboard ID |
list_files |
yes | Files stored on the printer |
get_snapshot |
yes | Webcam frame as MCP Image content |
discover_printers |
yes | LAN scan |
start_print |
only with --enable-control |
Starts a print |
pause_print |
only with --enable-control |
Pauses the current print |
resume_print |
only with --enable-control |
Resumes a paused print |
stop_print |
only with --enable-control |
Stops the current print |
upload_file |
only with --enable-control |
Uploads a file to the printer |
Safety
Control actions are gated behind an explicit enable_control=True (library) or
--enable-control flag (CLI / MCP). Destructive MCP tools are not even
registered when the flag is off, so an LLM never sees them. Still: leaving a
printer running unattended with write-capable agents is your responsibility.
Known firmware quirks
- 5 concurrent WebSocket connections max. The printer's SDCP server
accepts up to 5 open WebSockets on port 3030; the 6th returns HTTP 500
with body
"too many client". Slots release immediately when a connection closes — the CLI and MCP server each open/close one per invocation, so this is almost never a problem in practice. - Paused / errored states don't auto-push Attributes. The printer only
sends its
Attributesframe spontaneously while idle or printing. In paused and errored states it stays silent until asked. Since every SDCP command needs the printer'sMainboardID, the client takes care of this by pre-seeding the mainboard ID from a UDP discovery on every connect (as of v0.1.1). If you callPrinter.connect()directly without discovery, passmainboard_id=yourself.
Credits & licensing
- Protocol reference:
elegoo-link(Apache-2.0) andCentauriLink. pycentauriis licensed under Apache-2.0. See LICENSE.- Not affiliated with or endorsed by Elegoo.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pycentauri-0.3.1.tar.gz.
File metadata
- Download URL: pycentauri-0.3.1.tar.gz
- Upload date:
- Size: 43.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b4e52bc5ea2a30fa0896ce9a239e86af8f90784d052f85728f8f8a8c7e80bb7
|
|
| MD5 |
f947f05f2ec5a1d6949558e6fd63864b
|
|
| BLAKE2b-256 |
e52a0d8d7899eb51b66d44f5887260f0575964b4f2e52c309e574827e6adad17
|
Provenance
The following attestation bundles were made for pycentauri-0.3.1.tar.gz:
Publisher:
publish.yml on bjan/pycentauri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pycentauri-0.3.1.tar.gz -
Subject digest:
0b4e52bc5ea2a30fa0896ce9a239e86af8f90784d052f85728f8f8a8c7e80bb7 - Sigstore transparency entry: 1359507957
- Sigstore integration time:
-
Permalink:
bjan/pycentauri@70b745eed160407e824ffac602954ab677a7f1a2 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/bjan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@70b745eed160407e824ffac602954ab677a7f1a2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pycentauri-0.3.1-py3-none-any.whl.
File metadata
- Download URL: pycentauri-0.3.1-py3-none-any.whl
- Upload date:
- Size: 43.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32270f2ecdc14a5f4329d4899e8639fe24034ea5af163f1c037f5e6afa689423
|
|
| MD5 |
6a9276c3ee463ca41fd1baf5ad14a44d
|
|
| BLAKE2b-256 |
12d45835893af7c52e7d11017234c997e402b589760a614b7dc9d046769b3bab
|
Provenance
The following attestation bundles were made for pycentauri-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on bjan/pycentauri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pycentauri-0.3.1-py3-none-any.whl -
Subject digest:
32270f2ecdc14a5f4329d4899e8639fe24034ea5af163f1c037f5e6afa689423 - Sigstore transparency entry: 1359507977
- Sigstore integration time:
-
Permalink:
bjan/pycentauri@70b745eed160407e824ffac602954ab677a7f1a2 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/bjan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@70b745eed160407e824ffac602954ab677a7f1a2 -
Trigger Event:
push
-
Statement type: