Via the powers of CircuitPython, and Adafruit WipperSnapper, I have gifted my CNC a new set of features...
V1 avoids altering the CNC firmware or machine at all, because that would be less accessible (or reversible)!
It supports the existing software control system (wifi) by adding an MCP server with safety baked-in (motion rules + human approvals) along with skills for CNC probing, tool setting, roughing and visual feedback. Utilises a directly attached or MQTT based feedback system for the tool-setter and probe.
- An old Snapmaker 2.0 A350 3-in-1 3D printer / CNC / Laser (with Rotary extension), which relies on a touchscreen android device (handles the WiFi API etc) driving an STM32 compatible machine controller running a marlin fork. Utilises their Luban software for sending whole Jobs or single commands.
- A cheap-as-chips tool-setter, with optional blowing post (unintegrated currently as needs an Air Line).
(Looks an orangey gold in the image above, with the large central silver circular testing pad)
Β - Standard $50 touch probe tool, mines the one with "USB-C" connector for non-usb things.
For the Touch Sensor events to be received by the MCP server, use WiFi, or GPIO, or a USB-GPIO device:
- A microcontroller or Single Board Computer compatible with Adafruit Blinka, for CircuitPython support on the PC / SBC. I used anΒ Adafruit KB2040 (same chip as Pico) running U2-IF, connected to the laptop.
Β Β Or - A Network-Enabled device capable of sending the touch sensor events to an MQTT topic, like a WiFi enabled board supported by Adafruit WipperSnapper. I used the Adafruit FunHouse for my prototype.
The MCP Readme contains further instructions for how to setup Blinka and the python virtual environment, or the MQTT option, although hopefully the Preferences -> MCP page should be mostly self explanatory.
Or this:
I started small, with the tool height detector (tool-setter?) as it has a relatively large safety margin for error if you plunge down too far. An initial move to the area at top gantry height, then gentle descending march.
After getting that running along with some initial safety "laws of motion", the probe tool was next. This quickly led to overconfidence, soon paid for in a later accident during which it didn't raise Z to top position before a large horizontal move (stock and probe tip danced - wood won - old tip now has a slight kink).
Short video of the setup here (age restricted to avoid people who don't RTFM from injuring themselves)
After about 50-odd PRs, and a reasonable last test: calculating the true center-point after safely probing a workpiece remotely from the park last night, beer in hand...(DoΒ Not do what I do)...I can confidently say the safety rules codified in the solution are pretty good, stopping an agent from mistakes a few times that day.Β
Never leave a running or connected machine unattended!
https://github.com/tyeth/Luban/releases/latest/ (v4.15.2-mcp-alpha.8)
[Mac Users need to unblock it first or re-sign it with a local certificate to avoid warnings]
Safety First!
My aim was to ensure the Skills offered to agents allowed easy use of the api, and safety guidance (along with machine params expected motion rules etc).
However anyone using AI knows they love to ignore advice and required steps from Skills and sessions, meaning an MCP server should codify any safety rules required, otherwise Agents will attempt to find a shortcut or ignore rules.
To that end, I felt it necessary to not allow the MCP server to connect to the CNC machine (use the Connect button / API in Luban software).
Human intervention / sign-off is therefore required to enable agentic / remote use.
On top of that I added a second gate / verification factor, that a webpage must be opened by a human (local machine / network* only) to verify the intended GCode moves and then approve the job and feed back a 2FA code to the Agent.
Β To speed life up I've allowed the agent to poll for approval instead of pasting the code, and I've experimented with asking an agent to approve jobs for me which I've rules around - it allowed the one approval but not for future jobs/moves, each requiring explicit consent+approval.
Testing with low intelligence/effort models
I started with a Sonnet model using the skills and MCP server, pretty effective, although at times I used Opus or Fable for new untested mcp tool calls. Only one probe tip was harmed during the making of this software!
Most recently for the surface mapping and center finding probing operations I've been trying GPT5.6-Sol on Light thinking, which has still been very effective, only once misremembering a preferred coordinate from the start of a very long conversation.
Remote MCP Access
I particularly appreciated how the OpenAI tunnel-client worked (don't forget Developer Mode in user settings), running locally to forward the MCP, and then once setup then the Web sessions could access my mcp server too. ChatGPT also allowed easily updating the skills installed by just asking for the latest PR copy to be utilised.
MCP Oauth - Having originally used the Luban MCP on my host laptop, and not wanting to reload the MCP server after each change, I was going via a script and the loopback (127.0.0.1) network interface which works fine.Β Alas, once I'd shifted the MCP to another machine life became more complex for Claude (wanting loopback only - fixed with `ssh -L 40889:127.0.0.1:40889 [email protected]`, then wanting OAuth). I added a minimal registration pathway to the MCP server so now we ignore it and claude shuts up.
Luban MCP tools β v4.15.2-mcp-alpha.8
Terse reference for the 65 MCP tools in v4.15.2-mcp-alpha.8.
Start a fresh session with get_stored_state. Read the CNC skills and MCP setup guide for operating rules. Staged jobs require operator approval through the confirmation page; direct tools follow their documented guards.
Status and connection
get_machine_profileβ Machine profile: build volume, per-toolhead work ranges, and kinematics (which element moves per axis).get_positionβ The machine POSITION OF RECORD: the judged machine-frame position with itsreliability.get_connection_statusβ Report whether Luban is connected to a machine, and over which channel.recover_machine_connectionβ Recover stopped HTTP heartbeat polling using only the machine session already held by Luban.get_mcp_diagnosticsβ Timing evidence for slow or aborted procedures.
G-code jobs
validate_gcodeβ Statically inspect G-code: motion extents, feeds, spindle commands and warnings.submit_gcode_jobβ Stage a G-code job for human confirmation.start_gcode_jobβ Start an approved job: uploads the file to the machine through the same prepare/start path as "Start on Luban" (door interlock applies) and starts it.get_job_timingβ Where a job's time went, computed from its event log.get_gcode_job_statusβ Report a job state, ending reason, events, results, and live machine progress.stop_gcode_jobβ Stop a running job.convert_thread_milling_gcodeβ Offline conversion of Machining Doctor thread-milling G-code to explicit Snapmaker G0/G1 moves.
Transport and direct motion
restore_work_frameβ Put the controller back in the WORK workspace (G90thenG54on its own line).homeβ MACHINE home (G28): drives every axis to its limit switches - this is NOT the work origin; moving to work X0 Y0 is the separate goto_work_origin operation.goto_work_originβ STAGE a move to the WORK origin: one XY move to work X0 Y0 at the CURRENT Z.move_and_captureβ ONE vision-driven XY reposition: Z-GATED FIRST (operator ruling 2026-09-21), then move, settle-verify, and capture a position-stamped frame.move_zβ Request absolute Z motion, executed on the direct path so positions PERSIST.traverse_xyβ Law-2 TRANSPORT: an absolute XY move, or an ordered series.
Camera and vision
visual_servoβ Use the stored camera calibration to move toward a target pixel and capture a new frame.list_camerasβ List available capture sources: the configured snapshot URL, or DirectShow video devices found by ffmpeg.capture_frameβ Capture one frame from the workshop camera (configstore: mcpCameraUrl for an HTTP snapshot source, else ffmpeg with mcpCameraDevice/mcpFfmpegPath).get_frameβ Return a frame that was captured earlier, by frame_id.preview_camerasβ Show what each attached camera SEES, one frame per camera, so the right one can be identified before it is selected.select_cameraβ Choose which camera every capture uses from here on.set_tool_regionβ Update the expectedToolRegion that every capture reports: the fractional box where the endmill images (fixed camera-to-spindle geometry).track_featureβ Template-match a patch between two cached frames.query_firmware_configurationβ Read the configuration currently used by the firmware with M503 S (not necessarily EEPROM values).query_firmware_positionβ Ask the firmware directly for its position report (M114) and return the RAW controller response alongside the heartbeat-derived view.
Camera model
set_camera_calibrationβ Persist a pixel-to-machine calibration, keyed by the machine Y it was derived at (the SM2 platform travels in Y, so a mapping is only valid at that Y).get_camera_calibrationβ Fetch calibrations: by id, nearest to a machine Y (within tolerance), or all.delete_camera_calibrationβ Delete one stored calibration by id.get_camera_modelβ The current camera model and whether it may still be used.set_camera_modelβ Store a camera model solved from a bootstrap frame set (scripts/camera_bootstrap.py).plan_view_poseβ Where must the TOOLHEAD go to see this machine point? Read-only.camera_bootstrapβ Stage the camera pre-configuration for human approval.verify_camera_modelβ Check the stored camera model against a target whose machine coordinates are known, at the CURRENT toolhead position, and record the residual.
Landmarks
set_landmarkβ Persist a named scene landmark (tool height checker, rotary span, tool post...) with its machine-coordinate XY extent and a description.set_probe_geometryβ Store the jig/tool constants a rotary probe_program may reference as the "axis" namespace.delete_landmarkβ Delete one stored landmark by id or name.get_stored_stateβ Everything already known about this machine and bed in one read-only call.
Probe feed
get_probe_feed_statusβ State of the external probe sensor feed (transport mqtt or gpio).connect_probe_feedβ Connect (or reconnect) to the probe sensor feed using the current settings.disconnect_probe_feedβ Disconnect from the probe sensor feed and stop reconnecting.clear_overtravel_alarmβ Clear the latched safety alarm (overtravel OR crash - a contact sensor firing during motion that expected no contact) that is blocking all motion.
Tool setter and tool change
set_tool_setter_configβ Store the tool setter reference: its centre in MACHINE coordinates, the machine Z at which a known bit triggers it, that bit's length, and the longest bit in use.get_tool_setter_configβ The stored tool setter reference (centre, trigger Z, bit lengths, tool-change park position) and the last two measurements.run_tool_setterβ Stage a tool height measurement against the tool setter for human confirmation.goto_tool_change_positionβ Stage the move to the operator-set tool-change park position.apply_tool_length_offsetβ After a tool change: shift the CURRENT work origin Z by the measured length difference between the last two tool setter measurements.
Touch-probe procedures
probe_pointβ Stage a touch-probe point measurement for human confirmation.probe_vectorβ Stage a touch-probe march along an ARBITRARY direction for human confirmation.probe_sequenceβ Stage a whole measurement CIRCUIT as ONE operator approval.probe_circleβ Stage an N-point circle measurement of a roughly-round vertical feature for human confirmation.probe_surface_pathβ Stage a TOP-SURFACE FLATNESS scan along a straight line for human confirmation.probe_surface_gridβ Stage a TOP-SURFACE HEIGHT MAP for human confirmation.survey_bedβ Stage a whole-bed camera survey for human confirmation.probe_stock_outlineβ Stage a probe circuit to find a block top, outline, and centre from an estimated location.probe_wall_followβ Stage ONE approved procedure that follows a VERTICAL wall.probe_cornerβ Stage ONE approved procedure that measures the RADIUS of an internal corner between two walls earlier marches have fitted.probe_trace_perimeterβ Stage ONE approved procedure that traces the whole INTERNAL PERIMETER of a pocket of UNKNOWN shape from one point known to be inside it (operator spec 2026-09-22).probe_programβ Stage a COMPOSITE probing program for ONE human approval.
CAM probing programs
run_probing_gcodeβ Stage a CAM probing program for one operator approval and return an inspection report.get_inspection_reportβ Render the inspection report of a finished (or aborted) run_probing_gcode job in another format.
Workspaces and origins
set_workspace_originβ Stage a HUMAN-GATED origin write to an explicitly named G54..G59.3 workspace.select_workspaceβ Stage a HUMAN-GATED selection of an existing G54..G59.3 workspace without changing any stored origin.
What's next?
Digital Read Out (D.R.O.) showing the current X/Y/Z Coordinates
Having a circuitpython code capable device it occurred to me that I could add a Digital Read Out (x/y/z) to my machine relatively easily, getting the MCP server to push latest numbers to an I2C display like this one:
Inspection jobs from FreeCAD / Fusion / CAM software
I've got the first newly written post-processor for Fusion going, and it's successfully generated a probing job, verified as compatible with the MCP, so by the end of this week we should be sat up at the big boys table...
This page (Bringing new conveniences to an old CNC (v1 - unmodified machine)) was last updated on September 28, 2026.
Text editor powered by tinymce.