Skip to main content
Glama
Synx-x

printer-mcp

by Synx-x

printer-mcp

An MCP server that drives a Moonraker/Klipper 3D printer end to end: inspect, slice, upload, start, monitor. Every machine-specific value lives in one JSON profile, so pointing it at a different printer is a config change, not a code change.

Built and verified against a Creality Ender 3 V3 KE on Klipper + Moonraker with OrcaSlicer 2.3.2. Nothing in the code names that printer.

Install

git clone https://github.com/Synx-x/printer-mcp
cd printer-mcp
uv venv .venv
uv pip install --python .venv/bin/python mcp -e .

Python 3.11 or newer. The only runtime dependency is mcp.

Related MCP server: OrcaSlicer MCP Server

Configure

Copy profile.example.json to ~/.config/printer-mcp/profile.json and replace every path with your own. ~ expands, so a profile written that way works on a different machine.

Resolution order, first existing file wins:

  1. $PRINTER_MCP_PROFILE

  2. --profile <path>

  3. ~/.config/printer-mcp/profile.json

  4. ./printer-profile.json

Validate without serving:

.venv/bin/python -m printer_mcp.server --check

That prints the parsed values and exits. A bad key names the offending field and its parent object.

Register with an agent

claude mcp add printer --scope user -- \
  /absolute/path/to/.venv/bin/python -m printer_mcp.server

--scope user makes it available in every project. Confirm the connection:

claude mcp list | grep printer

Tools

Read-only, always safe:

Tool

Returns

printer_profile

loaded config: printer, materials, limits, process profiles

printer_status

live state, temps, progress, job name, position

printer_files

gcode already on the printer

inspect_gcode

declared time, filament, nozzle, completeness

measure_model

bounding box in mm plus a bed-fit verdict

printer_snapshot

camera JPEG path

Mutating, gated by the profile's safety checks:

Tool

Gate

slice_model_tool

process and material must exist in the profile

upload_gcode

filename safety, nozzle match

start_print

completeness, time, filament, nozzle, printer idle, bed cool

upload_and_start

all of the above in one call

pause_print / resume_print / cancel_print

none, always allowed

slice_model_tool takes process_profile, not process.

Why an MCP and not a skill or a pile of scripts

Scripts already existed for this pipeline: slice.sh, upload_and_print.py and start_print.py. Scripts are advisory, so a model reads one and may skip a step. Scripts also cannot stop a failure inside a call, and two such failures happened on 2026-10-24. Scripts let a print start while a finished part sat on a bed at 69 °C, and a 0.2 mm slice nearly went to a 0.4 mm machine.

An MCP runs the check inside the tool call, so the refusal is a return value rather than an instruction, and the agent cannot talk past it.

Concern

Skill

Script

This MCP

Agent can skip it

yes

yes

no, the tool refuses

Checks run before hardware

no

no

yes

One interface for any agent

no

no

yes

Per-user config

prose

edit code

one JSON file

The safety checks

Each check returns a named pass or refusal with a measured value, so a blocked run tells the agent exactly what to change.

  • gcode_complete — an EXECUTABLE_BLOCK_END marker exists. A truncated file hangs mid-move.

  • print_time_within_limit — declared duration against max_print_time_s.

  • filament_within_limit — declared volume against max_filament_cm3.

  • nozzle_matches_profile — the gcode's declared nozzle against the profile.

  • nozzle_matches_live_machine — the profile against the printer's actual config.

  • printer_idle — refuses to start over a running job.

  • bed_cool — refuses to start onto a hot bed, where a finished part is sitting.

  • filename_safe — firmware mangles spaces and unicode in gcode names.

  • fits_build_volume — model dimensions against the declared volume.

Profile schema

Full reference in docs/PROFILE.md. The short version:

{
  "printer":   { "host": "192.168.1.200", "moonraker_port": 7125 },
  "machine":   { "nozzle_diameter": 0.4, "build_volume": [220, 220, 245] },
  "slicer":    { "binary": "~/Apps/orca-slicer.AppImage",
                 "machine_profile": "~/.config/OrcaSlicer/machine.json" },
  "materials": { "pla": { "bed_temp": 60, "nozzle_temp": 215,
                          "filament_profiles": ["~/profiles/pla.json"] } },
  "process_profiles": { "fast": "~/profiles/fast-020.json" },
  "safety":    { "max_print_time_s": 28800, "max_bed_temp_to_start": 45 }
}

Gotchas this encodes

Each of these was a real failure on the reference machine, not theory.

  • Metadata sits at the end. OrcaSlicer 2.3.2 writes its config block after the executable block, near EOF. Scanning the first 64 KB of a real slice finds nothing. The parser reads the head and the tail and merges them.

  • The printer ignores ICMP. ping fails while HTTP works. Test reachability over TCP.

  • A static IP on another subnet is invisible to a TCP sweep. If the printer holds 192.168.1.x and the host sits on 192.168.2.x, the host routes the traffic to its ISP gateway and the printer looks offline. A raw ARP scan finds it by MAC while TCP and ICMP do not. The profile's alias_addresses records the host-side fix, which needs root, so the MCP does not apply it.

  • Stock profiles carry Bambu defaults. A 35 °C bed on a PEI plate loses a three-hour PLA print. Set the bed type and the temperature explicitly.

  • Bambu 2.4 3MF files will not load in OrcaSlicer 2.3.2. Strip Metadata/*.config from the archive, or bake the meshes to STL.

  • A long unsupported span fails regardless of speed. A 155 mm ceiling in PLA drapes and cracks. Nozzle and acceleration tuning will not fix it. Flip the part or add supports.

Test

env -u PYTEST_ADDOPTS .venv/bin/python -m pytest tests/ -q

31 tests, no printer needed. The suite runs every safety check against synthetic gcode.

Status

  • 31 tests passing, last run 2026-10-24.

  • Sliced, uploaded and started real prints on an Ender 3 V3 KE, last on 2026-10-24.

  • Refusal path verified live against a running job at 70 °C bed.

Limitations

  • Moonraker/Klipper only. A Marlin printer needs a different transport.

  • Slicing needs an installed OrcaSlicer or a CLI-compatible fork. The checks work without one.

  • measure_model reads STL. 3MF measurement is not implemented.

  • No authentication. Run it on a trusted network.

  • alias_addresses is a note to your own setup script, not something the server applies.

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    A
    maintenance
    Enables interaction with 3D printer management systems through an MCP server, supporting STL file manipulation, slicing, and control of printers like OctoPrint, Klipper, Duet, and more.
    30
    119 npm
    256
    GPL 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to manage multiple 3D printers via MCP, including monitoring temperatures and progress, uploading and slicing models, controlling prints, and viewing cameras.
    7 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to monitor and control Prusa 3D printers over PrusaLink or Prusa Connect, including status and camera checks, file upload and print control, G-code execution, queue and history access, and responding to printer dialogs.
    1
    MIT