Printer Control Tools
Every printer tool accepts host, port, type, and api_key, and Bambu printers also use bambu_serial and bambu_token. Omitted values fall back to PRINTER_HOST, PRINTER_PORT, PRINTER_TYPE, API_KEY, BAMBU_SERIAL, and BAMBU_TOKEN. Keep keys in the server configuration rather than passing them in calls.
What each call does depends on the backend; see printer backend setup for the exact API routes.
Every print start and positive heating command, on every backend, goes through a safety gate: the server inspects the exact G-code it will start, checks each heater target against hardware and material ceilings, and checks that the printer is ready. It does not ask for confirmation or for a bed check: the client's own print action is the approval. Turning a heater off (temperature: 0), pause_print, and cancel_print are never gated. resume_print reheats and moves the printer, so it only resumes a job this server inspected and started. It asks a person first only when the printer cannot hand the file back for re-reading.
get_printer_status
Read the printer's current status. The response is the backend's own status data: Bambu returns temperatures, job state, progress, layers, time remaining, and AMS data; OctoPrint returns printer state and temperatures; Klipper returns the Moonraker host state only.
{
"type": "klipper",
"host": "192.168.1.50",
"port": "7125"
}list_printer_files
List the files stored on the printer or its host software. On Bambu printers it lists the cache/, timelapse/, and logs/ directories.
{
"type": "octoprint",
"host": "192.168.1.100"
}upload_gcode
Upload G-code to the printer, and optionally start it. Pass gcode_path for a local file, or gcode with the content (or a local path). filename defaults to the basename of gcode_path; when printing, use a plain filename so the started file is exactly the uploaded one.
{
"type": "klipper",
"host": "192.168.1.50",
"port": "7125",
"gcode_path": "/path/to/benchy.gcode",
"print": true,
"material": "PETG"
}- Without
print, the file is only uploaded and nothing is inspected or started. - With
print: true, the exact uploaded bytes are inspected first (everySandRheater target, tool changes, and hardware and material ceilings), and the printer's state is checked before the job starts. - Printing needs a declared material: slicer metadata in the file (
; filament_type = PLA) or thematerialargument, which must not contradict the file. - On Bambu printers, files go to
cache/over FTPS, and printing also needs the model (bambu_modelorBAMBU_MODEL). Automatic printing after upload supports.gcodeonly; print.3mfprojects withprint_3mf. - The result's
jobNameis the remote file name.counterpart_print_progressreports the same name asjob.namewhile that job runs.
start_print
Start a G-code file that is already stored on the printer. The server downloads that exact file, inspects it, checks the printer's state, and then starts a uniquely named checked copy. This works on Bambu Lab, OctoPrint, Klipper (Moonraker), and Duet. Repetier, Prusa, and Creality refuse, because their adapters have no verified download route; use upload_gcode with print: true instead.
{
"type": "octoprint",
"host": "192.168.1.100",
"filename": "benchy.gcode",
"material": "PLA"
}Pass material when the file has no filament_type metadata. On Bambu printers it also needs the model and supports .gcode files only; bare filenames are looked up in cache/. The result's jobName is the checked copy's name (checked-<id>-<file>), which is the name the printer reports for the job.
cancel_print
Cancel the current print job. Cancelling is never gated and also cancels checked prints still waiting to start, including a pending resume_print. A cancelled job cannot be resumed; use pause_print to stop temporarily. Bambu stops go over the connection that printed (connection_mode, connection_type, default BAMBU_DEFAULT_CONNECTION_MODE/_TYPE): LAN MQTT, or FULU's BambuNetwork session, which reports the printer's err_code when it refuses.
{
"type": "prusa",
"host": "192.168.1.120"
}pause_print
Pause the current print. Pausing is never gated. Routes: OctoPrint POST /api/job ({"command":"pause","action":"pause"}), Moonraker POST /printer/print/pause, PrusaLink PUT /api/v1/job/<id>/pause, Duet M25, Repetier @pause, Creality printers that run Moonraker, and Bambu {"print":{"command":"pause"}} over LAN MQTT or FULU's BambuNetwork session (the connection that printed, as for cancel_print).
{
"type": "klipper",
"host": "192.168.1.50",
"port": "7125"
}connect_printer
OctoPrint only. Connects OctoPrint to the printer when it is running but not connected: counterpart_print_progress reports octoprint.disconnected then. It uses OctoPrint's own saved serial port, baud rate and printer profile unless serial_port, baudrate or printer_profile is given, and waits up to wait_ms (default 20000) for the connection to settle. With power_on: true it first switches the printer on through the PSU Control plugin when that plugin reports the power off. If OctoPrint is already connected, nothing is sent. The result has action (none, connected, connecting or failed), a message, and OctoPrint's connection (state, port, baudrate, ports, powerOn).
{ "type": "octoprint", "host": "octopi.local", "power_on": true }octoprint_request_api_key
Gets an OctoPrint API key through OctoPrint's Application Keys approval, so nobody copies a key by hand. Call it with OctoPrint's host and port, and an app name for OctoPrint's prompt. The result is pending, with a request_id and approve_url (OctoPrint's own approval page; every signed-in OctoPrint page also shows the prompt). Call again with request_id and wait_ms (up to 120000) to wait for the answer. approved returns api_key and env: {"API_KEY": …}. The other statuses are denied, expired, unsupported (no Application Keys plugin: copy a key from OctoPrint's settings), unreachable and rate-limited (OctoPrint allows 3 requests a minute, 5 in 10 minutes and 10 an hour). OctoPrint drops a request that isn't polled every 5 seconds, so this server keeps polling between calls, for up to 10 minutes.
{ "host": "octopi.local", "port": "80", "app": "Counterpart" }resume_print
Resume a paused print. Resume reheats and moves the printer, so it is gated like a print start. It resumes only the job this server instance inspected and started, and after a fresh report that the same job is paused. OctoPrint, Klipper and Duet re-download the file and compare its SHA-256 with the inspected bytes, and resume without a prompt. PrusaLink, Repetier and Creality have no download route here, so the job is matched by name only and a person confirms through MCP elicitation first. The printer is read again just before the resume is sent. Cancelling the job, or a report that it ended, retires it, so a later job that reuses the name is never resumed. A job started at the printer, from another app, or before the server restarted is refused; resume it at the printer. Duet's download route needs the Duet Software Framework, so a standalone board's resume is refused. The result's bytesVerified says whether the bytes were re-read. OctoPrint drops a resume sent while it is still pausing, so the server waits for a settled pause (job.stage "Pausing" means not yet). Routes: OctoPrint {"command":"pause","action":"resume"}, Moonraker POST /printer/print/resume, PrusaLink PUT /api/v1/job/<id>/resume, Duet M24, and Repetier continueJob.
Bambu resumes as bambu-printer-mcp does: the paused job must report the unique name this server started it under (subtask_name), the fresh-state check is repeated, and its bytes are re-read over FTPS (the uploaded 3MF, else the cache/<name>_plate_<n>.gcode a P1S extracts) and compared. A file this server uploaded that cannot be re-read refuses the resume; only a file FULU's plug-in placed that cannot be re-read asks a person. The resume goes over the connection that printed.
counterpart_printer_info
Describe the printer in the shared counterpart/1 shape: the server's name and version, the printer's kind, host and any model, serial or name it reports, and which features work. Capabilities are detected from the firmware, never assumed. For example, camera is http-snapshot only when the firmware names a snapshot URL or the conventional one answers with a JPEG.
{
"contract": "counterpart/1",
"server": { "name": "mcp-3d-printer-server", "version": "1.2.21" },
"printer": { "kind": "klipper", "host": "192.168.1.50", "name": "voron" },
"capabilities": { "progress": true, "layers": true, "camera": "http-snapshot", "filaments": "single",
"pause": true, "resume": true, "cancel": true, "print3mf": false, "gcode": true }
}counterpart_print_progress
Read the printer's state and current job in one shape for every firmware. state is one of idle, preparing, printing, paused, finished, failed, cancelled, offline, or busy. job carries the file name (the same as the upload's jobName), progress (0 to 100), elapsedSec, remainingSec, layer and totalLayers where the firmware reports them, and stage for transitional states. problems lists firmware errors and messages. Optional fields are omitted, never null. A printer that does not answer at all is offline. See progress, camera, pause and resume for each firmware's requests and mapping.
{
"contract": "counterpart/1",
"observedAt": "2026-11-01T12:00:00.000Z",
"state": "printing",
"job": { "name": "checked-4f2a-bracket.gcode", "progress": 25, "elapsedSec": 1200, "remainingSec": 3600, "layer": 12, "totalLayers": 80 },
"temps": { "nozzle": { "actual": 215, "target": 215 }, "bed": { "actual": 60, "target": 60 } },
"problems": []
}camera_snapshot
Capture one JPEG frame and return it as base64, with the same arguments and result as bambu-printer-mcp's camera_snapshot plus capturedAt. Pass save_path to also write the file, and timeout_ms (default 8000).
- OctoPrint: the Classic Webcam snapshot URL on 1.9 and later (
plugins.classicwebcam.snapshot),webcam.snapshotUrlon 1.8, thenhttp://<host>/webcam/?action=snapshot. - Moonraker: the first enabled webcam's
snapshot_urlfrom/server/webcams/list. - Repetier: the printer's webcam
staticUrl. - PrusaLink and Duet have no local snapshot API.
PRINTER_CAMERA_SNAPSHOT_URLoverrides the source for any printer.
Relative URLs and loopback hosts in the firmware's settings are read as the printer itself. The API key is sent only to the printer's own API address, and only a JPEG is accepted.
{ "status": "success", "format": "image/jpeg", "sizeBytes": 78719, "base64": "/9j/4AAQ…", "capturedAt": "2026-11-01T12:00:00.000Z", "source": "moonraker-webcam" }get_printer_filaments
Report the loaded filament as one external slot, {"slots": [{"id": "ext", "material", "color", "name", "source", "profile"}]}. Moonraker printers with Spoolman report the active spool's material, #RRGGBB colour and name (source: "spoolman"); other non-Bambu printers report source: "unknown".
Bambu printers report the live AMS from the printer's MQTT report, with bambu-printer-mcp's fields unchanged: trays (one per AMS tray, empty ones included: absolute slot = AMS id × 4 + tray, loaded, tray_type, tray_color, tray_info_idx, remain_percent, the resolved Bambu Studio/Orca filament profile resolved_profile_path with profile_resolution and match_confidence, and display_name), summary, recommended and load_filaments_all. Profiles resolve by tray_info_idx in the slicer's BBL/filament folder (BAMBU_PROFILES_ROOT, else the SLICER_PATH installation), preferring the bambu_model and nozzle_diameter preset. slots lists every loaded tray and the external spool ("254", and "254"/"255" on dual-nozzle printers) with source: "printer". Pass a tray's slot to print_3mf as ams_slots.
Each slot also carries profile: the absolute path of the installed slicer filament profile for its material, which slice_stl takes as load_filaments. It is resolved in the slicer slice_stl uses by default (SLICER_TYPE, SLICER_PATH), for the printer in SLICER_PROFILE. OrcaSlicer searches its bundled profiles/<vendor>/filament, the OrcaSlicer/system copies and the user's OrcaSlicer/user/<id>/filament presets, reading filament_type and compatible_printers through inherits. PrusaSlicer and Slic3r search the data folder's filament/*.ini presets. The material must equal the profile's filament_type exactly (case aside): a PLA spool never gets a PLA-CF profile. Profiles that list other printers are skipped; one that lists this printer comes first, then profiles for every printer, user presets before bundled ones. With no reported material, profile is the filament slice_stl loads by default (FILAMENT_PROFILE), when that file exists. When nothing resolves, profile is omitted.
set_printer_temperature
Set a target temperature for a printer component. Use bed or extruder; Bambu also accepts nozzle, tool, and tool0. OctoPrint nozzle targets are sent to tool0.
{
"type": "klipper",
"host": "192.168.1.50",
"port": "7125",
"component": "extruder",
"temperature": 215,
"material": "PLA"
}temperaturemust be a finite number of 0 or more.0switches the heater off and is never gated.- Positive targets are checked before connecting against independent hardware ceilings and the material's ceiling, and need a ready printer.
- Positive nozzle heating needs
material(for example PLA, PETG, or ABS), including for spools without RFID. - On Bambu printers, positive heating also needs
bambu_model(orBAMBU_MODEL), and nozzle heating usesnozzle_diameter(0.2, 0.4, 0.6, or 0.8; defaultNOZZLE_DIAMETERor 0.4). Both are checked against the live printer.