Skip to content

Setup reference for agents and manual configuration ​

The README provides the copy-and-paste setup request. Use this reference for exact environment variables, per-printer credentials, manual MCP client configuration, Docker, and troubleshooting. Preserve the user's existing MCP configuration and use the current harness's supported configuration format.

Installation ​

Prerequisites ​

  • Node.js 24, recommended (the package supports Node.js 18 or later, CI tests 18 through 24, and the Docker image uses 24; on end-of-life Node.js 18, npm installs the older @hono/node-server 1.x)
  • npm
  • A slicer (only needed to slice): PrusaSlicer, OrcaSlicer, FULU OrcaSlicer-bambulab, Bambu Studio, Slic3r, or CuraEngine. A file that is already sliced can be uploaded and printed without a slicer on the MCP host. See the slicing guide.
  • Blender with a Blender MCP server (optional): only for the blender_mcp_* tools. See Blender MCP.

Run without installing (npx) ​

The fastest way to get started. No global install is required:

bash
npx -y mcp-3d-printer-server

MCP clients normally launch this command for you over stdio. Set environment variables in the client's MCP configuration or in a .env file in the server's working directory (see Configuration).

Install globally from npm ​

bash
npm install -g mcp-3d-printer-server

The package installs three commands: mcp-3d-printer-server (stdio by default), mcp-3d-printer-server-http (streamable HTTP), and mcp-3d-printer-server-sse (a legacy name that also starts the streamable HTTP runtime).

Install from source ​

bash
git clone https://github.com/DMontgomery40/mcp-3D-printer-server.git
cd mcp-3D-printer-server
npm ci
npm run build
npm link

npm link makes the mcp-3d-printer-server command available globally without publishing to npm. Build from a clean checkout before linking; dist/ is generated and not tracked in Git.


Choose your printer backend ​

PRINTER_TYPE selects the printer adapter. Every printer tool also accepts type, host, port, and api_key arguments, so one server can reach more than one printer; values you omit fall back to the environment.

PRINTER_PORT defaults to 80 for every backend. Set it explicitly when your system listens elsewhere, such as Moonraker on 7125 or Repetier-Server on 3344.

PRINTER_TYPESystemConnectionCredentialsTesting evidence
bambuBambu LabMQTT over TLS on port 8883, FTPS on port 990BAMBU_SERIAL, BAMBU_TOKEN (LAN access code), BAMBU_MODELMost tested. Maintainer hardware testing, shared with the Bambu-only fork. The FTPS fix for #22 is tested against a local FTPS server; confirmation on the reporter's X1C is outstanding.
octoprintOctoPrintHTTP REST API, port 80 on OctoPi; or a URL for HTTPS and reverse-proxy sub-pathsAPI_KEY, sent as X-Api-Key; octoprint_request_api_key gets one through OctoPrint's Application Keys approvalCommunity-reported. Used against a real OctoPrint in #4. Checked with OctoPrint 1.11.8 and 2.1-dev Virtual Printers (npm run test:octoprint-live).
klipperKlipper (Moonraker)HTTP API, usually port 7125Optional API_KEY, sent as X-Api-Key; otherwise Moonraker must trust the MCP hostCommunity-reported. Used with a Creality K1 Max through Moonraker in #16.
prusaPrusaLink / Prusa ConnectHTTP for local PrusaLink, HTTPS for connect.prusa3d.com or port 443API_KEY, sent as X-Api-Key and as a bearer tokenCommunity-reported. PrusaLink 0.8.1 status fixed after #11; Prusa Connect setup discussed in #9.
duetDuetHTTP, port 80None sentUnverified. No hardware reports yet.
repetierRepetier-ServerHTTP, usually port 3344API_KEY, sent as the apikey query parameterUnverified. No hardware reports yet.
crealityCrealityHTTP, port 80API_KEY, sent as a bearer tokenUnverified. No hardware reports yet.

Bambu Lab has the most hardware testing. The other adapters call each system's HTTP API as described in Printer backend setup, and hardware evidence comes from community reports. If a backend misbehaves, please open an issue with the printer, firmware or host software version, and the tool error.


Configuration ​

Create a .env file in the directory where you run the server, or pass environment variables in your MCP client configuration. Configure only the block for your backend.

env
# --- Printer backend (pick one PRINTER_TYPE) ---
PRINTER_TYPE=octoprint            # octoprint, klipper, duet, repetier, bambu, prusa, creality
PRINTER_HOST=192.168.1.100        # Printer or host-software address
PRINTER_PORT=80                   # 7125 for Moonraker, 3344 for Repetier-Server
API_KEY=your_api_key              # OctoPrint, authenticated Moonraker, Repetier, Prusa, Creality

# --- Bambu Lab only ---
# BAMBU_SERIAL=01P00A123456789    # Printer serial number
# BAMBU_TOKEN=your_access_code    # LAN access code from the printer screen
# BAMBU_MODEL=p1s                 # Required for Bambu print operations
# BED_TYPE=textured_plate         # textured_plate, cool_plate, engineering_plate, hot_plate, supertack_plate
# BAMBU_NOZZLE_TYPE=hardened_steel # Only if you replaced the stock nozzle (P1S/P1P/A1 ship stainless_steel)
# NOZZLE_DIAMETER=0.4             # 0.2, 0.4, 0.6, or 0.8 for printing and heating

# --- Print and heating safety (see "Print and heating safety" below) ---
# PRINTER_MAX_NOZZLE_TEMP=300     # Non-Bambu heater ceilings in °C (Bambu uses per-model limits)
# PRINTER_MAX_BED_TEMP=120
# PRINTER_MAX_CHAMBER_TEMP=60

# --- Slicer (only for slice_stl, process_and_print_stl, and Bambu auto-slicing) ---
# SLICER_TYPE=prusaslicer         # prusaslicer, slic3r, orcaslicer, orcaslicer-bambulab, bambustudio, cura
# SLICER_PATH=/path/to/slicer/executable
# SLICER_PROFILE=/path/to/profile # Bambu-compatible slicers: a process profile only
# FILAMENT_PROFILE=/path/to/filament.json
# BAMBU_PROFILES_ROOT=            # Profile tree containing BBL/, if not found from SLICER_PATH
# BAMBU_TEMPLATE_DIR=~/Sync/bambu/templates

# --- Progress, camera, pause and resume (counterpart/1) ---
# PRINTER_CAMERA_SNAPSHOT_URL=http://192.168.1.60/snapshot.jpg  # Override the camera source for any printer
# REPETIER_SLUG=my_printer        # Repetier-Server printer slug, when the server has more than one printer

# --- MCP transport ---
# MCP_TRANSPORT=stdio             # stdio (default) or streamable-http
# MCP_HTTP_HOST=127.0.0.1
# MCP_HTTP_PORT=3000
# MCP_HTTP_PATH=/mcp

# --- Optional standard Blender MCP server ---
# BLENDER_MCP_COMMAND=/full/path/to/uvx
# BLENDER_MCP_ARGS=["mcp-for-blender"]
# BLENDER_MCP_TIMEOUT_MS=120000

# --- Optional Autodesk Fusion (DMontgomery40/fusion360-mcp-server: faust-machines with document binding, pinned; fusion_mcp_setup adds these on macOS) ---
# FUSION_MCP_COMMAND=/full/path/to/uvx
# FUSION_MCP_ARGS=["--from","git+https://github.com/DMontgomery40/fusion360-mcp-server@86a3016a5a5b","fusion360-mcp-server","--mode","socket"]
# FUSION_MCP_TIMEOUT_MS=180000
# FUSION_MCP_HOST=localhost
# FUSION_MCP_PORT=9877

Environment variables reference ​

VariableDefaultRequiredDescription
PRINTER_TYPEoctoprintYesPrinter adapter: octoprint, klipper, duet, repetier, bambu, prusa, or creality
PRINTER_HOSTlocalhostYesPrinter or host-software address. Prusa also accepts connect.prusa3d.com or a full https:// URL
PRINTER_PORT80Depends on backendHTTP port for non-Bambu backends. Set 7125 for Moonraker and 3344 for Repetier-Server
API_KEYOctoPrint, authenticated Moonraker, Repetier, Prusa, CrealityBackend API key or token. Duet does not send one
BAMBU_SERIALBambuPrinter serial number
BAMBU_TOKENBambuLAN access code shown on the printer
BAMBU_MODELBambu printing and Bambu slicingPrinter model: p1s, p1p, x1c, x1e, a1, a1mini, h2d. Required for print_3mf, start_print, upload with print: true, positive Bambu heating, and Bambu-compatible slicing. Slicing also accepts p2s, h2s, and h2c when the installed slicer has that preset. If omitted and the MCP client supports elicitation, the server asks. It selects the slicer machine preset and is checked against the live printer before printing.
BED_TYPEtextured_plateNoBambu bed plate: textured_plate, cool_plate, engineering_plate, hot_plate, supertack_plate. print_3mf requires it to match the sliced plate's bed metadata
NOZZLE_DIAMETER0.4NoNozzle diameter in mm. Selects the Bambu machine preset; printing and heating accept 0.2, 0.4, 0.6, or 0.8 and check it against the live printer
BAMBU_NOZZLE_TYPEmodel preset's stock nozzleNoInstalled nozzle for slicing: stainless_steel, hardened_steel, tungsten_carbide, or brass. Set it if you upgraded the nozzle; printing requires the job's nozzle type to match the printer's report
BAMBU_DISPATCH_CHECK_MS15000NoHow long to watch the printer's reports after a print command to confirm it started or was refused (0 to 60000; 0 skips the check)
PRINT_CONFIRMATION_TIMEOUT_MS600000 (10 minutes)NoHow long a person has to answer a server prompt (the Bambu printer-model prompt, or a resume the printer cannot re-read), from 1000 to 3600000. When it runs out, nothing is sent and the error says so
PRINTER_MAX_NOZZLE_TEMP300NoNozzle ceiling in °C for non-Bambu printers. Only the server environment can change it; an invalid value refuses printing and positive heating
PRINTER_MAX_BED_TEMP120NoBed ceiling in °C for non-Bambu printers
PRINTER_MAX_CHAMBER_TEMP60NoChamber ceiling in °C for non-Bambu printers
SLICER_TYPEbambustudio when PRINTER_TYPE=bambu, otherwise prusaslicerNoprusaslicer, slic3r, orcaslicer, orcaslicer-bambulab (FULU), bambustudio, or cura. See slicer aliases
SLICER_PATHCommon install paths for FULU/Orca and Bambu Studio; none for other slicersTo sliceSlicer executable. Per-call slicer_path requires MCP_ALLOW_EXECUTABLE_ARG=1
SLICER_PROFILENoBambu-compatible slicers: one process profile JSON; the machine preset always comes from the model and nozzle. Generic OrcaSlicer: machine.json;process.json, optionally |filament.json. See the slicing guide
FILAMENT_PROFILENoFilament profile path(s) loaded with --load-filaments, ;-separated in slot order; PrusaSlicer and Slic3r take one .ini preset, loaded with a second --load. Alias: SLICER_FILAMENT_PROFILE
SLICER_TIMEOUT_MS600000NoSlicer process deadline in milliseconds
SLICER_STALL_SECONDS20NoOn macOS, a Bambu-compatible slicer that uses no CPU and writes nothing this long is stopped as waiting on a dialog (slicer-waiting-on-dialog); 0 turns it off
BAMBU_PROFILES_ROOTfound from SLICER_PATHNoProfile tree containing BBL/ for the active Bambu-compatible slicer. Set it for AppImages or non-standard installs. It is never replaced by another installation's tree
BAMBU_SLICER_PROFILE_DIRSthe slicers' user system/BBL directoriesNoDirectories (separated with your OS path delimiter) searched for custom process and filament parents, never for machine presets. Set it empty to disable the search
BAMBU_TEMPLATE_DIR~/Sync/bambu/templatesNoLocal template registry for slice_with_template, list_templates, save_template, and get_slice_settings
BAMBU_TEMPLATE_3MF_PATHNoDefault template 3MF whose slicer settings are reused as the process profile. A 3MF that carries its own process settings slices with them instead
FULU_ORCA_PATHNoFULU OrcaSlicer-bambulab executable when SLICER_PATH is unset. Aliases: ORCASLICER_BAMBULAB_PATH, ORCA_SLICER_BAMBULAB_PATH
FULU_ORCA_PLUGIN_DIRNoDirectory with FULU's runtime payload, such as OrcaSlicer.app/Contents/MacOS. Alias: ORCASLICER_BAMBULAB_PLUGIN_DIR
PJARCZAK_MAC_RUNTIME_DIR~/Library/Application Support/OrcaSlicer/macos-bridge/runtimeNoInstalled FULU macOS runtime directory
FULU_BAMBU_BRIDGE_COMMANDFULU bridge toolsTrusted command that starts the FULU BambuNetwork bridge host. See the FULU guide
BAMBU_DEFAULT_CONNECTION_MODElan_mqtt_ftpsNoBambu print and control route: lan_mqtt_ftps (direct LAN MQTT/FTPS) or bambu_network (FULU's BambuNetwork bridge). counterpart_print_route changes it. See FULU's route
BAMBU_DEFAULT_CONNECTION_TYPEcloudNoFor bambu_network: lan (the plug-in's authenticated local session) or cloud (Bambu Cloud)
BAMBU_NETWORK_BRIDGE_COMMANDFULU_BAMBU_BRIDGE_COMMAND, then the discovered FULU hostFor bambu_networkTrusted FULU bridge host command for the print route, bambu-printer-mcp's name for it
BAMBU_NETWORK_BRIDGE_IMAGEFor bambu_networkInstead of a command: a Docker image holding FULU's x86-64 Linux host, which the server runs with its own agent config folder mounted (print snapshots are written there). With BAMBU_NETWORK_BRIDGE_DOCKER, BAMBU_NETWORK_BRIDGE_DOCKER_HOST and the optional Colima profile settings. See FULU in Docker
BAMBU_NETWORK_CONFIG_DIR~/.config/mcp-3d-printer-server/bambu-networkNoFULU agent config folder; holds control-trace.log and the pinned certificates
BAMBU_NETWORK_COUNTRY_CODE, BAMBU_NETWORK_USER_INFO, BAMBU_NETWORK_CERT_DIR, PJARCZAK_BAMBU_PLUGIN_DIRUS, none, <config>/cert, noneNoRegion, optional cloud net.change_user JSON, certificate folder, and the networking plug-in folder for the bridge host
MCP_ALLOW_EXECUTABLE_ARGdisabledNoAccept per-call executable selectors (slicer_path, bridge_command, and bridge probe paths). Leave off for normal use; see executable settings
MCP_ALLOW_BRIDGE_COMMAND_ARGdisabledNoLegacy opt-in for bridge commands and bridge-derived probe paths only
PRINTER_CAMERA_SNAPSHOT_URLNoJPEG snapshot URL used by camera_snapshot for any printer, instead of the firmware's own webcam settings. Use it for PrusaLink and Duet, which have no local snapshot API
REPETIER_SLUGthe only printerRepetier-Server with several printersPrinter slug the progress, camera, pause and resume tools address. Without it, those tools need a server with exactly one printer
TEMP_DIRprivate folder under the system temporary directoryNoIntermediate files. Each server instance gets its own folder unless you set this
MCP_TRANSPORTstdioNostdio or streamable-http
MCP_HTTP_HOST127.0.0.1NoHTTP bind address (HTTP transport only)
MCP_HTTP_PORT3000NoHTTP port. The legacy -http and -sse commands also read HTTP_PORT and SSE_PORT
MCP_HTTP_PATH/mcpNoHTTP endpoint path
MCP_HTTP_STATEFULtrueNoStateful HTTP sessions
MCP_HTTP_JSON_RESPONSEtrueNoReturn JSON responses instead of event streams where possible
MCP_HTTP_ALLOWED_ORIGINSNoComma-separated allowed browser origins. Requests with any other Origin header are rejected; requests without one are accepted
BLENDER_MCP_COMMANDNoTrusted executable for a standard stdio Blender MCP server, such as the full path to uvx
BLENDER_MCP_ARGS[]NoJSON array of arguments, such as ["mcp-for-blender"]; no shell parsing
BLENDER_MCP_TIMEOUT_MS120000NoConnection, discovery, and call deadline, 100 to 300000 ms
BLENDER_MCP_BRIDGE_COMMANDNoLegacy trusted shell command for blender_mcp_edit_model; separate from the standard MCP integration
FUSION_MCP_COMMANDNoTrusted executable for the Fusion MCP server: the full path to uvx. See the Fusion guide
FUSION_MCP_ARGSthe pinned server in socket modeNoJSON array of arguments; no shell parsing. The default is ["--from","git+https://github.com/DMontgomery40/fusion360-mcp-server@86a3016a5a5b","fusion360-mcp-server","--mode","socket"]
FUSION_MCP_TIMEOUT_MS180000NoConnection, discovery, and call deadline, 100 to 600000 ms
FUSION_MCP_HOSTlocalhostNoWhere the Fusion360MCP add-in listens, passed through to the Fusion server
FUSION_MCP_PORT9877NoThe add-in's port. Blender's MCP addon holds the add-in's own default, 9876, so Fusion uses 9877. The Fusion server and the add-in both read it; Fusion, a GUI app, needs it in its own environment (see the Fusion guide)

The Blender MCP server also reads its own BLENDER_HOST and BLENDER_PORT (default localhost:9876) if the addon listens elsewhere. This server passes only BLENDER_* settings to it, never printer credentials. See the Blender guide. The Fusion MCP server likewise receives only FUSION_* settings, and runs on FUSION_MCP_PORT=9877 so it does not collide with Blender on 9876. See the Fusion guide.


Every print start and positive heating command goes through the same gate on every backend. Heater-off (a target of 0) and cancel_print are never gated, and cancelling also cancels checked prints that are still waiting to start.

  • No confirmation prompt and no bed check. Print starts and positive heating do not ask a person: the client's own print action is the approval, and the printer runs its own checks. A printer that reports a finished job (Moonraker complete or cancelled, PrusaLink FINISHED or STOPPED, Creality finished, Bambu FINISH) starts the next print like an idle one, so clear the plate first. Only a resume that the printer cannot re-read asks a person (see progress, camera, pause and resume).
  • File inspection. The server inspects a private copy of the exact G-code or 3MF plate it will start: every S and R heater target, tool changes, and RepRapFirmware and Klipper heater commands. Klipper macro parameters that name a heater are checked; macro bodies stored on the printer cannot be.
  • Ceilings. Nozzle, bed, and chamber targets have independent ceilings: 300, 120, and 60 °C by default on non-Bambu printers (PRINTER_MAX_NOZZLE_TEMP, PRINTER_MAX_BED_TEMP, PRINTER_MAX_CHAMBER_TEMP), and per-model hardware limits on Bambu printers. Material ceilings also apply, such as 260 °C for PLA. Only the server environment can change a ceiling; tool arguments and G-code never raise one.
  • Declared material. Printing and positive nozzle heating need a material: slicer metadata in the G-code (; filament_type = PETG) or the material argument, which must not contradict the file. A declaration cannot prove which spool is physically loaded.
  • Printer state. Printing, paused, errored, offline, or unreadable printers are refused. On Bambu printers, a fresh MQTT report must match the model, serial, nozzle, and loaded filament, when the job is checked, again just before upload, and again before dispatch.
  • Files already on the printer. start_print downloads the file, inspects it, and starts a uniquely named checked copy on Bambu Lab, OctoPrint, Klipper (Moonraker), and Duet. Repetier, Prusa, and Creality refuse remote starts because no verified download route exists; upload the local G-code with print: true.
  • Expected temperatures. process_and_print_stl refuses before uploading when extruder_temp or bed_temp differs from the sliced job's highest target.

Known gaps: Klipper macro bodies and RepRapFirmware stored tool temperatures (used when a job selects a tool with T0 without setting a target first) live on the printer and cannot be inspected. Activation-only M568 and M144 commands are refused for that reason.

Evidence: the gate is covered by unit tests and MCP-level tests with mocked MQTT and FTPS boundaries and loopback HTTP printer APIs. On one real P1S, the Bambu path read fresh status, refused a job sliced for the wrong nozzle type, asked for confirmation (an earlier release, before the prompt was removed), and uploaded the checked file. The firmware then refused the unsigned start command because Developer Mode was off. Acceptance at a mocked boundary does not prove firmware behavior, and the other printer systems have not been exercised on real hardware in this release.


MCP client configuration ​

Add the server to your MCP client (Claude Desktop, Claude Code, Cursor, Codex, or any MCP-compatible client). JSON-based clients use an mcpServers entry with the command, arguments, and environment. This OctoPrint example shows the shape:

json
{
  "mcpServers": {
    "3dprint": {
      "command": "npx",
      "args": ["-y", "mcp-3d-printer-server"],
      "env": {
        "PRINTER_TYPE": "octoprint",
        "PRINTER_HOST": "192.168.1.100",
        "PRINTER_PORT": "80",
        "API_KEY": "your_octoprint_api_key"
      }
    }
  }
}

Replace the env block with the values for your backend:

Backendenv values
OctoPrintPRINTER_TYPE=octoprint, PRINTER_HOST, PRINTER_PORT (80 on OctoPi, 5000 for octoprint serve), API_KEY
Klipper (Moonraker)PRINTER_TYPE=klipper, PRINTER_HOST, PRINTER_PORT=7125, API_KEY if required
DuetPRINTER_TYPE=duet, PRINTER_HOST, PRINTER_PORT if not 80
Repetier-ServerPRINTER_TYPE=repetier, PRINTER_HOST, PRINTER_PORT=3344, API_KEY
Bambu LabPRINTER_TYPE=bambu, PRINTER_HOST, BAMBU_SERIAL, BAMBU_TOKEN, BAMBU_MODEL
PrusaLinkPRINTER_TYPE=prusa, PRINTER_HOST (printer IP), API_KEY
Prusa ConnectPRINTER_TYPE=prusa, PRINTER_HOST=connect.prusa3d.com, PRINTER_PORT=443, API_KEY
CrealityPRINTER_TYPE=creality, PRINTER_HOST, PRINTER_PORT if not 80, API_KEY

A Bambu Lab example with FULU OrcaSlicer-bambulab slicing:

json
{
  "mcpServers": {
    "3dprint": {
      "command": "npx",
      "args": ["-y", "mcp-3d-printer-server"],
      "env": {
        "PRINTER_TYPE": "bambu",
        "PRINTER_HOST": "192.168.1.100",
        "BAMBU_SERIAL": "01P00A123456789",
        "BAMBU_TOKEN": "your_lan_access_code",
        "BAMBU_MODEL": "p1s",
        "SLICER_TYPE": "orcaslicer-bambulab",
        "SLICER_PATH": "/Applications/OrcaSlicer.app/Contents/MacOS/OrcaSlicer"
      }
    }
  }
}

Codex uses TOML instead of JSON. Add a [mcp_servers.<name>] table to config.toml, or manage servers with codex mcp:

toml
[mcp_servers.3dprint]
command = "npx"
args = ["-y", "mcp-3d-printer-server"]
env = { PRINTER_TYPE = "klipper", PRINTER_HOST = "192.168.1.50", PRINTER_PORT = "7125" }

Claude Code can add the server from the command line:

bash
claude mcp add 3dprint -e PRINTER_TYPE=klipper -e PRINTER_HOST=192.168.1.50 -e PRINTER_PORT=7125 -- npx -y mcp-3d-printer-server

Where the configuration lives depends on your client and OS:

Client / scopemacOS / Linux locationWindows location
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json (macOS)%APPDATA%\Claude\claude_desktop_config.json
Claude Code (project-shared MCP).mcp.json in the project root.mcp.json in the project root
Claude Code (user/local MCP)~/.claude.json; see Claude Code MCP configuration%USERPROFILE%\.claude.json
Cursor (project).cursor/mcp.json in the project root.cursor\mcp.json in the project root
Cursor (global)~/.cursor/mcp.json%USERPROFILE%\.cursor\mcp.json
Codex (user; CLI, IDE extension, app)~/.codex/config.toml; see Codex MCP configuration%USERPROFILE%\.codex\config.toml
Codex (project, trusted projects only).codex/config.toml in the project root.codex\config.toml in the project root

Restart or reload your client after editing its configuration. For Codex, run /mcp or codex mcp list to confirm the server loaded.

Executable settings stay in server configuration ​

Slicer paths, the FULU bridge command, and Blender MCP commands select which local program this server launches. By default they are read only from the server's environment. A per-call slicer_path, bridge_command, or bridge probe path returns an error that names the opt-in flag unless MCP_ALLOW_EXECUTABLE_ARG=1 is set. Accepting those arguments would let anything steering the model, such as a downloaded model's description or 3MF metadata, choose a program to run. Set the flag only for trusted, interactive diagnostics. The standard Blender MCP command and arguments are always server configuration.

Optional code mode ​

Use the harness's built-in code mode or equivalent when available. Otherwise, an agent may suggest a compatible, maintained integration as an optional way to discover and compose tools; check that integration's current setup instructions first. Direct MCP use is fully supported; code mode is not required. See code execution with MCP for the approach.


Printer backend setup ​

OctoPrint ​

  1. Set PRINTER_TYPE=octoprint and PRINTER_HOST. OctoPi serves on port 80; a directly run octoprint serve usually listens on 5000 (PRINTER_PORT).
  2. Get a key. Either let the person approve this server in OctoPrint (no copying): call octoprint_request_api_key, have them click Allow in OctoPrint's web page (or open the returned approve_url), and call it again with the request_id to get the key for API_KEY. Or create an application key yourself under Settings → Application Keys.

PRINTER_HOST may be a full URL for an OctoPrint behind HTTPS or a reverse proxy at a sub-path, for example https://octopi.example.com or http://pi.lan/octoprint/ (OctoPrint's reverse-proxy setup with X-Script-Name). A URL keeps its own port. HTTPS certificates are verified, so OctoPi's self-signed certificate on port 443 is refused; use http:// on the local network instead. A URL with a user name or password is refused: the key goes in API_KEY.

The adapter sends the key as X-Api-Key. It calls /api/printer for status (state and temperatures, not job progress), /api/files?recursive=true to list files, /api/files/local to upload (optionally selecting and printing), /api/files/local/<file> to start a file, /downloads/files/local/<file> to download a stored file for inspection before start_print, /api/job to cancel, and /api/printer/bed or /api/printer/tool (nozzle targets go to tool0) for temperature targets. The file list keeps each file's name, path, type, size, date, OctoPrint's estimated print time and its last print, and each folder's children, so a large library stays small (#4).

Not connected. OctoPrint can be running while it isn't connected to the printer (state Closed, or Offline after error). counterpart_print_progress then reports the problem octoprint.disconnected instead of a bare offline, and a print is refused with the same words. connect_printer connects it (POST /api/connection {"command":"connect"}) with OctoPrint's own saved port, baud rate and printer profile, or the ones given. With PSU Control installed and the printer switched off, the problem says so, and connect_printer with power_on: true switches it on first (POST /api/plugin/psucontrol {"command":"turnPSUOn"}).

Prusa printers and binary G-code. OctoPrint streams plain-text G-code, and this server inspects the exact text before printing. Prusa's MK4, MK4S, MINI, XL and CORE One profiles turn on binary G-code (.bgcode) once it is enabled in PrusaSlicer's preferences, and PrusaSlicer then writes binary bytes even into a .gcode file. slice_stl with PrusaSlicer adds --no-binary-gcode when the loaded config has binary_gcode = 1, and stops with an explanation if a slice still comes out binary. Prusa recommends plain G-code with OctoPrint too (Prusa help).

Checked against OctoPrint 1.11.8 and the 2.1 development build (octoprint/octoprint:canary, past 2.0) in Docker with OctoPrint's Virtual Printer, directly and behind an nginx sub-path proxy (npm run test:octoprint-live): the approval flow, disconnect and connect_printer, upload and print, progress, the file list, and cancel. PSU Control is checked against a fake OctoPrint only. No physical printer was used.

Klipper (Moonraker) ​

  1. Make sure Moonraker is reachable from the machine running the MCP server, usually on port 7125.
  2. If Moonraker requires authentication, set API_KEY to its API key. The adapter sends X-Api-Key on status, file, upload, start, cancel, and heater requests. Leave it empty only when Moonraker trusts the MCP host. See Moonraker authentication.
  3. Set PRINTER_TYPE=klipper, PRINTER_HOST, and PRINTER_PORT=7125.

get_printer_status comes from /printer/info, which reports the Klipper host state (for example ready) but not job progress or temperatures. For job progress, layers and temperatures (#12), use counterpart_print_progress; see progress, camera, pause and resume. The safety gate reads /printer/objects/query?webhooks&print_stats to refuse a printer that is printing, paused, or in error. Other calls: /server/files/list, /server/files/upload, /server/files/gcodes/<file> (download for inspection before start_print), /printer/print/start, /printer/print/cancel, and SET_HEATER_TEMPERATURE through /printer/gcode/script for bed and extruder.

Duet ​

Set PRINTER_TYPE=duet, PRINTER_HOST, and PRINTER_PORT if not 80. The adapter targets the Duet Software Framework (DuetWebServer) REST API and sends no password. It calls GET /machine/status, GET /machine/directory/0:/gcodes to list files, GET and PUT /machine/file/<path> to download and upload, and POST /machine/code with a raw-text G-code body (M32 to start, M0 to cancel, M140/M104 for temperatures). The routes follow DuetSoftwareFramework's MachineController; printing and file routes do not support standalone RepRapFirmware. The progress, pause and resume tools also speak the rr_model and rr_gcode routes that standalone boards and DSF 3.5 or later answer, falling back to /machine/status and /machine/code. They have not been verified against Duet hardware; please report which firmware and host mode you tested.

Repetier ​

Set PRINTER_TYPE=repetier, PRINTER_HOST, PRINTER_PORT (Repetier-Server usually uses 3344), and API_KEY from Repetier-Server. The adapter sends the key as an apikey query parameter to /printer/api/ with actions such as listPrinter, getPrinterInfo, ls, startJob, stopJob, setBedTemp, and setExtruderTemp. The print and file routes do not add a printer slug to the path, and they have not been verified against a Repetier-Server install. The progress, camera, pause and resume tools address /printer/api/<slug>, using REPETIER_SLUG or the server's only printer. It has no verified download route, so start_print of a file already on the server is refused; upload the local G-code with print: true.

Bambu Lab ​

Bambu Lab printers use local MQTT (port 8883, TLS) for status and commands and FTPS (port 990, implicit TLS) for uploads. You need the printer's IP address, serial number, LAN access code, and model.

Enable LAN Only Mode and Developer Mode ​

Enable LAN Only Mode and, on firmware with authorization controls, Developer Mode so third-party clients can send commands. Older firmware may expose the LAN interfaces without a separate Developer Mode toggle. Follow the model-specific LAN setup and Developer Mode instructions; firmware and menu names vary.

  1. On the printer's touchscreen, open Settings, then the Network (WLAN) page. It shows the network name, IP address, the LAN Only Mode toggle, and the access code.

P1S network settings screen showing WLAN, LAN Only Mode, IP address, and Access Code

  1. Turn LAN Only Mode on. This enables the local MQTT and FTPS services the server uses. It also disconnects the printer from Bambu's cloud, so the Bambu Handy app stops working while it is on; slicers can still connect over LAN.
  2. On firmware that provides it, turn Developer Mode on in the same LAN settings.
  3. Note the Access Code. It is your BAMBU_TOKEN. Refreshing it disconnects clients that use the old code.

P1S network settings showing the Access Code field

Find the serial number ​

The serial number is on a sticker on the back or underside of the printer, and on the touchscreen under Settings > Device Info. P1 series serials usually begin with 01P, X1 series with 01X, and A1 series with 01A. Bambu Studio also shows it under Device > Device Management.

P1S device info screen showing model name, serial number, AMS serial, and printing time

Find the access code ​

  • P1 series (P1P, P1S): Settings > Network / WLAN. The access code is at the bottom of the screen.
  • X1 series (X1C, X1E): Settings > Network. Enable LAN Only Mode (and Developer Mode where offered) to see the code.
  • A1 and A1 mini: In the Bambu Handy app, connect to the printer and open its Settings > Network page.

Menu names vary by model and firmware. The access code is not your Bambu account password. If a LAN or Developer Mode setting is missing, check the instructions for your exact model and firmware.

Set the model ​

Set BAMBU_MODEL to one of p1s, p1p, x1c, x1e, a1, a1mini, or h2d. Print tools refuse to guess: without a model they ask through MCP elicitation when the client supports it, or return an error. G-code for the wrong model can crash the bed into the nozzle.

How the server talks to Bambu printers ​

  • MQTT (port 8883, TLS): Commands and status reports use the printer's own MQTT broker with username bblp and the access code. Status requests a full report (pushall) and returns temperatures, job state, progress, layers, time remaining, and raw AMS data. The implementation follows community documentation such as OpenBambuAPI.
  • FTPS (port 990, implicit TLS): Uploads use basic-ftp directly with TLS 1.2 on both channels and TLS session reuse, which the printer requires. This addresses the "Premature close" failures in #22 and avoids the double-path upload bug in bambu-js. File listing still uses bambu-js helpers. The printer uses a self-signed certificate, so the connection assumes a trusted local network.
  • Printing: print_3mf uploads a sliced .3mf to cache/ and sends a project_file command with the plate's G-code path, MD5 and AMS mapping (ams_slots or ams_mapping), under a unique checked-… name that the printer reports back. start_print starts a plain .gcode file already on the printer.
  • Pause, resume and stop: pause_print, resume_print and cancel_print send pause, resume and stop over the connection that printed. Resume only continues the job this server started, after re-reading its bytes over FTPS.
  • Authorization firmware without Developer Mode: direct LAN commands are refused (HMS 0500-0500-0001-0007). Set BAMBU_DEFAULT_CONNECTION_MODE=bambu_network and BAMBU_DEFAULT_CONNECTION_TYPE=lan to print and control through FULU's authenticated BambuNetwork session instead; see the FULU guide. Status and the safety checks still read the printer's LAN MQTT reports.
  • PrusaLink (local): Set PRINTER_TYPE=prusa, PRINTER_HOST to the printer's address, and API_KEY to the PrusaLink API key. Local hosts use HTTP unless you give an https:// host or port 443.
  • Prusa Connect (cloud): Set PRINTER_HOST=connect.prusa3d.com with PRINTER_PORT=443, or PRINTER_HOST=https://connect.prusa3d.com. The server normalizes both forms to HTTPS. Cloud access through this adapter has not been confirmed on hardware; see #9.

The adapter sends the key as both X-Api-Key and Authorization: Bearer. If a PrusaLink install only accepts username/password (HTTP digest) authentication, this adapter cannot authenticate yet. Requests try newer routes first and fall back on 404, 405, or 501: status tries /api/v1/status, then /api/v1/printer, then /api/printer (the PrusaLink 0.8.1 fix for #11); files try /api/v1/storage, then /api/files, then /api/files/local. start_print of a file already on the printer is refused because no verified download route exists; upload the local G-code with print: true.

Creality ​

Set PRINTER_TYPE=creality, PRINTER_HOST, PRINTER_PORT if not 80, and API_KEY to a bearer token. The adapter calls /api/device/status, /api/storage/list, /api/storage/info, /api/storage/upload, /api/job/start, /api/job/cancel, and /api/printer/temperature over HTTP with Authorization: Bearer <API_KEY>. None of these routes has been verified against Creality Cloud or stock Creality firmware. start_print of a file already on the printer is refused; upload the local G-code with print: true.

If your Creality printer runs Klipper with Moonraker reachable on the network, as with the K1 Max in #16, use PRINTER_TYPE=klipper instead. With PRINTER_TYPE=creality, the progress, camera, filament, pause and resume tools probe Moonraker on port 7125, then 4408, and use it when it answers.

Progress, camera, pause and resume ​

counterpart_printer_info, counterpart_print_progress, camera_snapshot, get_printer_filaments, pause_print, and resume_print follow the counterpart/1 contract that bambu-printer-mcp also implements, so one client can treat both servers alike. Each declares an outputSchema and returns structuredContent plus the same JSON as text. A tool error carries text only, because MCP clients validate any structured content against the output schema.

FirmwareProgress requestsState and jobCameraPause / resumeChecked against
OctoPrintGET /api/job, GET /api/printer (temperatures; 409 when not operational). After a job ends, the file's prints.lastOperational is idle, or finished at 100%, or cancelled when the selected file's last print failed with no progress (OctoPrint records a cancel that way). Printing, Paused, Error, Offline and the transitional states (job.stage) map directly. completion is 0 to 100; times are secondsplugins.classicwebcam.snapshot (1.9+), webcam.snapshotUrl (1.8, and 1.9+ compatibility), /webcam/?action=snapshotPOST /api/job {"command":"pause","action":"pause"|"resume"}OctoPrint 1.11.2 and 1.8.7 virtual printers; job API
Klipper (Moonraker)GET /printer/objects/query?print_stats&virtual_sdcard&display_status&extruder&heater_bed&webhooks, plus estimated_time from /server/files/metadataprint_stats.state; webhooks.state other than ready is busy. Progress is display_status.progress (else virtual_sdcard.progress) ×100; remaining is the slicer estimate minus print_duration; layers come from SET_PRINT_STATS_INFOFirst enabled webcam's snapshot_url from /server/webcams/listPOST /printer/print/pause and /resume, waiting up to 60 s for the macroKlipper and Moonraker 1.5.0 on simulavr (mainsail-crew virtual-klipper-printer), with Spoolman 0.27.1; Moonraker printer API, Klipper status reference
PrusaLinkGET /api/v1/status, and GET /api/v1/job for the long file name. Older firmware: the OctoPrint-compatible /api/jobPRINTING, PAUSED, FINISHED, STOPPED (cancelled), ERROR (failed), ATTENTION (paused, with a problem), IDLE/READY, BUSYNonePUT /api/v1/job/<id>/pause and /resumePrusaLink OpenAPI only
Duet (RRF 3)GET /rr_model?key=state&flags=d99v and key=job (standalone and DSF 3.5+), else GET /machine/statusstate.status; an idle board's job.lastFileName with lastFileCancelled/lastFileAborted gives cancelled, failed or finished. Progress is filePosition / file.size; remaining is timesLeft.slicer, else .file. The verbose v flag is required: f drops job.fileNoneM25 and M24 through rr_gcode, else POST /machine/codeRRF HTTP requests, object model only
Repetier-ServerGET /printer/api/<slug>?a=listPrinteronline, job and paused; progress is done; elapsed is printedTimeComp, remaining printTime - printedTimeComp. These fields and @pause are returned or accepted by current servers but are not in the API manualgetPrinterConfig webcam staticUrlsend {"cmd":"@pause"}; continueJobDocs only
CrealityMoonraker on port 7125, then 4408, when it answers; otherwise /api/device/statusAs Klipper, or the device status's recognized state wordsAs Klipper, or noneOnly through MoonrakerNot verified; the ports come from community K1 setups, not Creality documentation
Bambu LabThe MQTT report (get_printer_status raw)gcode_state; a user cancel (print_error 0300400C) is cancelled; HMS codes as AAAA_BBBB_CCCC_DDDDNone here; use bambu-printer-mcpMQTT {"print":{"command":"pause"|"resume"}}, or FULU's BambuNetwork session (the connection that printed)Offline mapping tests; in-process MQTT/FTPS fakes and a fake FULU host

OctoPrint, Klipper and Duet (DSF) re-download the job for resume_print and compare its SHA-256 with the inspected bytes, then resume without a prompt. PrusaLink, Repetier and Creality match it by name only, so a person confirms through MCP elicitation first. A standalone Duet board has no download route here, so its resume is refused.

Bambu resume_print re-reads the uploaded 3MF over FTPS (else the plate G-code a P1S extracts from it) and compares its SHA-256; a file this server uploaded that cannot be re-read refuses the resume, and only a file FULU's plug-in placed asks a person.

get_printer_filaments on Bambu reports the AMS as bambu-printer-mcp does (trays, summary, recommended, load_filaments_all) plus a slot for every loaded tray and the external spool; profiles resolve by each tray's tray_info_idx in the slicer's BBL/filament folder. On other printers it reports one external slot. The 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. On Moonraker it reads the active spool with GET /server/spoolman/spool_id and POST /server/spoolman/proxy (/v1/spool/<id>); without Spoolman, and on every other firmware, the slot's source is unknown.


Blender MCP (optional) ​

Blender MCP lets your agent model or refit parts in Blender, for example refitting a downloaded model before slicing, and export a verified STL. Printer tools work without it. The Blender guide covers setup, units, and a worked phone-case example.

  1. Install Blender, uv, and the mcp-for-blender Blender addon (uvx mcp-for-blender install-addon, or install its addon.py in Blender's add-on preferences). Enable the addon and keep Blender open: the addon does not run in background mode.
  2. Set BLENDER_MCP_COMMAND to the full path of uvx and BLENDER_MCP_ARGS to ["mcp-for-blender"]. The project was formerly published as blender-mcp; uvx blender-mcp still works as a compatibility wrapper.
  3. Ask your agent to call blender_mcp_status with {"connect": true}. That initializes the MCP server and lists its tools by name; it does not prove the addon inside Blender is connected. A get_scene_info call through blender_mcp_call checks the addon.

Blender and this server must see the same local file paths. See the Blender tool reference for blender_mcp_status, blender_mcp_call, blender_mcp_export_stl, and blender_mcp_edit_model.


Running with Docker ​

The repository includes a Dockerfile (Node.js 24 on Alpine, running as a non-root user) and a docker-compose.yml.

  1. Copy .env.example to .env and fill in your backend's values.
  2. Build the image:
bash
docker build -t mcp-3d-printer-server .
  1. Run it over stdio for an MCP client, with host networking so it can reach printers on your LAN:
bash
docker run --rm -i --network host --env-file .env mcp-3d-printer-server

Docker's --env-file reads each value literally, including any trailing # comment. Remove inline comments (such as the one after SLICER_TYPE in .env.example) or pass values with -e instead. Host networking behaves this way on Linux; on Docker Desktop, check that the container can reach your printer's address.

To serve streamable HTTP instead, add -e MCP_TRANSPORT=streamable-http -e MCP_HTTP_HOST=0.0.0.0 and set MCP_HTTP_ALLOWED_ORIGINS for any browser clients. scripts/docker-run.sh builds and runs the image with the variables from .env.

The image does not include a slicer, and a slicer installed on the host generally cannot run inside the container. To slice in Docker, install a Linux build of your slicer in a derived image and set SLICER_PATH to its path inside the container. Printing files that are already sliced needs no slicer.


Streamable HTTP transport ​

stdio is the default and what most local MCP clients use. For a long-running service, start the HTTP transport:

bash
MCP_TRANSPORT=streamable-http MCP_HTTP_PORT=3000 npx -y mcp-3d-printer-server

The endpoint is http://127.0.0.1:3000/mcp by default. The server binds to 127.0.0.1 unless you set MCP_HTTP_HOST. It has no built-in authentication, so keep it on a trusted network and restrict browser origins with MCP_HTTP_ALLOWED_ORIGINS.


Troubleshooting ​

  • The server starts but printer calls fail: check PRINTER_TYPE, PRINTER_HOST, and PRINTER_PORT first. The port defaults to 80 for every backend.
  • Klipper returns 401 or 403: Moonraker is enforcing authorization. Add the MCP host to trusted_clients.
  • Bambu uploads fail with "Premature close": update to 1.2.9 or later, which negotiates the TLS session reuse the printer requires.
  • Bambu print tools ask for or reject the model: set BAMBU_MODEL to your exact model. Do not substitute a similar model.
  • "No confirmation was received within 10 minutes": nobody answered a resume prompt in time, so nothing was sent. Ask again when you are at the printer, or raise PRINT_CONFIRMATION_TIMEOUT_MS.
  • "The printer rejected its LAN access code" (Bambu, MQTT "Not authorized" or FTPS 530): the access code changes when LAN mode is toggled and after some resets. Read the current code on the printer under Settings > Network (WLAN) and update BAMBU_TOKEN.
  • "Hardware safety confirmation was declined or cancelled": a person or the MCP client declined a resume prompt for a job the printer cannot re-read. Some clients decline prompts automatically, for example in a non-interactive permission mode. Resume the job at the printer instead.
  • "Printer nozzle 0 information is unreliable; refresh the nozzle information on the printer" (Bambu): a nozzle with an identity chip reports its stored information as unreliable; refresh it on the printer. Nozzles without a chip, such as stock H2D nozzles (sn: N/A), pass this check. Stderr records them once per printer; check that their configured diameter and type match the installed hotends.
  • A resume is refused because the client cannot ask for confirmation: PrusaLink, Repetier and Creality resumes need a person to confirm through MCP elicitation. Use a client that supports it, or resume the job at the printer.
  • A print is refused for a missing material: the G-code has no ; filament_type line. Pass material (for example PLA) to the print or heating tool.
  • "The printer rejected the print command (HMS 0500-0500-0001-0007)": Bambu firmware 01.08.05 and later only accept third-party LAN control with LAN Only Mode and Developer Mode on (Settings > WLAN on the printer). LAN Only Mode turns off cloud and Bambu Handy remote access while it is on. Or start the uploaded file from Bambu Studio or the printer's screen.
  • "Printer nozzle 0 type is unknown or does not match the job": the file was sliced for a different nozzle material than the printer reports (a stock P1S preset assumes stainless steel). Slice again with nozzle_type or set BAMBU_NOZZLE_TYPE.
  • print_3mf rejects the bed type: pass the bed_type the plate was sliced for, or slice again for the plate you have installed.
  • A Bambu slice stops before the slicer runs: the exact <model> <nozzle> nozzle preset is missing from the selected installation, or SLICER_PROFILE lists a machine preset. See Bambu-compatible slicing.
  • A per-call slicer_path or bridge_command is rejected: configure the executable in the server environment instead. See executable settings.
  • The MCP client lists no tools: restart or reload the client after changing its configuration, and check the client's MCP logs for the server's startup error.

Released under the GPL-2.0 license. An independent open-source project, not affiliated with any printer manufacturer or host software.