Turn STEP, DXF and SVG files into CNC milling and laser G-code for GRBL, LinuxCNC, Mach3 and more.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Optional. Without it everything works except the G-code download. Get a free personal token from https://www.step2gcode.com/api/mcp/trial (no account), buy credits for it on step2gcode.com.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Check whether the user finished the browser upload. Returns the file_id once ready. Free. NEXT STEP - do not generate yet. Ask the user: 1. Which material? Offer the presets (wood, hardwood, plywood, acrylic, aluminum, steel, brass). If theirs is not among them, pick the closest preset AND set spindle_speed, feed_rate, plunge_rate and step_down yourself from known cutting data for that material and cutter. 2. Which cutter diameters do they actually own? Recommend one from that list: it must fit the smallest inner radius of the part, and a smaller cutter means longer machining time. If you cannot judge the geometry yet, say so and suggest generating once to see the dimensions. 3. Is the part cut all the way through? Then recommend holding_tabs so it does not come loose on the last pass.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http step2gcode https://www.step2gcode.com/api/mcp
Headers
X-Api-Token (optional, not in the config above): Optional. Without it everything works except the G-code download. Get a free personal token from https://www.step2gcode.com/api/mcp/trial (no account), buy credits for it on step2gcode.com.Check whether the user finished the browser upload. Returns the file_id once ready. Free. NEXT STEP - do not generate yet. Ask the user: 1. Which material? Offer the presets (wood, hardwood, plywood, acrylic, aluminum, steel, brass). If theirs is not among them, pick the closest preset AND set spindle_speed, feed_rate, plunge_rate and step_down yourself from known cutting data for that material and cutter. 2. Which cutter diameters do they actually own? Recommend one from that list: it must fit the smallest inner radius of the part, and a smaller cutter means longer machining time. If you cannot judge the geometry yet, say so and suggest generating once to see the dimensions. 3. Is the part cut all the way through? Then recommend holding_tabs so it does not come loose on the last pass.
| Argument | Type | Description |
|---|---|---|
| upload_id* | string |
{
"type": "object",
"required": [
"upload_id"
],
"properties": {
"upload_id": {
"type": "string"
}
}
}Turn an uploaded STL or 3MF mesh back into a STEP solid with exact surfaces - planes, cylinders, cones and fillets instead of triangles, intersected into real edges. Free; only the download of the finished file is charged. Use this when the user has a mesh and needs CAD geometry: a hole becomes a cylinder with a diameter again, a chamfer a cone with an angle, so the part can be dimensioned, modified and machined. Typical case: the CAD source is gone and the mesh is all that is left. The answer states the largest measured deviation between the built solid and the mesh, in millimetres - always pass that number on to the user, it is what tells them whether the result is accurate enough for their part. Parts made of freeform surfaces and raw 3D scans are rejected with a reason instead of being badly reconstructed. Upload the mesh the same way as any other file (curl to /api/upload, upload_file_content or create_upload_link), then pass the file_id here. The resulting STEP file can go straight into generate_gcode afterwards.
| Argument | Type | Description |
|---|---|---|
| file_id* | string | file_id of an uploaded .stl or .3mf mesh |
{
"type": "object",
"required": [
"file_id"
],
"properties": {
"file_id": {
"type": "string",
"description": "file_id of an uploaded .stl or .3mf mesh"
}
}
}Create a short-lived link the user opens in their browser to upload a CAD file (STEP, DXF or SVG). Returns the link and an upload_id. Poll check_upload with the upload_id until it reports a file_id, then call generate_gcode. Free. Before using this, check whether you can run shell commands. If you can, do not send the user to the browser: compress the file locally and pass it to upload_file_content (the compression is detected by itself). That works inside sandboxes without network access, which is the common case - a direct curl upload fails there with a proxy error. The response of this tool spells out all three routes in order.
No arguments.
{
"type": "object",
"properties": {}
}Start G-code generation for an uploaded file and wait for the result. Most jobs finish within the wait, so the answer usually already carries the part dimensions and hole diameters - no polling needed. Only if it says the job is still running, call get_job_status once after a few seconds. Free - only the download is charged. BEFORE CALLING THIS, ask the user about the settings you cannot infer from the file. Two of them change the result substantially and are wrong by default more often than not: - material: drives spindle speed, feed rates and depth per pass. Aluminium cut with wood settings breaks cutters. - holding_tabs: without them a cut-out part comes loose on the last pass and can be thrown off the machine. Recommend them for any part cut all the way through. Also worth asking when the geometry suggests it: pockets and holes (STEP files carry that depth information), and whether the job is milling or laser work. IMAGES (PNG, JPG, WebP ...) are always laser raster engraving. Ask for the engraving width in mm, photo or logo (image_mode), the controller (GRBL or Marlin) and its S maximum (laser_max_s), and tell the user to test power and speed on a scrap piece first.
| Argument | Type | Description |
|---|---|---|
| depth | number | Cutting depth in mm; omit to take it from the model geometry (STEP) |
| holes | boolean | Drill or mill the holes found in the geometry |
| laser | boolean | Laser job instead of milling |
| file_id* | string | file_id from check_upload or upload_file_content |
| pockets | boolean | Mill pockets found in the geometry (STEP) |
Show credits left on the access token and how many models are already paid for. Free.
No arguments.
{
"type": "object",
"properties": {}
}Return the download link for the finished G-code. THIS is the billed step: one credit per model not paid for yet. Re-downloading the same job or a model already paid for is free. Called without confirm, this tool charges nothing and only reports what the download would cost. Show that to the user, wait for their answer, and only then call again with confirm=true. Never set confirm=true on your own initiative - it spends the user's money.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | |
| confirm | boolean | Set to true only after the user has explicitly agreed to spend the credit. |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string"
},
"confirm": {
"type": "boolean",
"description": "Set to true only after the user has explicitly agreed to spend the credit."
}
}
}Check a generation job. While it runs you get the percentage done, how long it has been running and how much time is left before the generator gives up on its own (180 s). When it fails you get the generator's own reason - pass that on to the user instead of retrying blindly. This is the ONLY correct way to wait for a job; calling generate_gcode again never helps. Free.
| Argument | Type | Description |
|---|---|---|
| job_id* | string |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string"
}
}
}Return the download link for a STEP file built by convert_mesh_to_step. THIS is the billed step: one credit per model not paid for yet. The DXF contour that comes with a 2.5D part belongs to the same part and costs nothing extra. Called without confirm, this tool charges nothing and only reports what the download would cost. Show that to the user, wait for their answer, and only then call again with confirm=true. Never set confirm=true on your own initiative - it spends the user's money.
| Argument | Type | Description |
|---|---|---|
| confirm | boolean | Set to true only after the user has explicitly agreed to spend the credit. |
| step_id* | string | step_id from convert_mesh_to_step |
{
"type": "object",
"required": [
"step_id"
],
"properties": {
"confirm": {
"type": "boolean",
"description": "Set to true only after the user has explicitly agreed to spend the credit."
},
"step_id": {
"type": "string",
"description": "step_id from convert_mesh_to_step"
}
}
}Show the finished tool path as an image (top view and isometric view, cutting moves and rapids in different colours) so the user can sanity-check the job before paying. Free.
| Argument | Type | Description |
|---|---|---|
| job_id* | string |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string"
}
}
}Upload a CAD file by writing its content out yourself. Use this only when a direct curl upload to /api/upload is not possible - that route costs you no output at all. If you must use this tool, ALWAYS compress first and pass the output as content: (xz -9e -c model.step 2>/dev/null || gzip -9c model.step) | base64 | tr -d '\n' xz writes about a quarter less than gzip; the command falls back to gzip where xz is missing (macOS). xz, bzip2 and gzip are recognised by themselves - no extra argument. Do not paste the raw file. You have to write out every character you pass here, and a typical STEP solid takes you three minutes raw against twenty seconds compressed - the user watches that happen. Compression shrinks it about fivefold, which brings files up to roughly 80 KB within reach. NO SHELL AVAILABLE? Then do not use this tool for anything above about 10 KB - you would have to type the whole file out and the user waits minutes for it. Call create_upload_link instead: the user drops the file in a browser and it is done in five seconds. Same if the file is still too large after compressing. Free. NEXT STEP - do not generate yet. Ask the user: 1. Which material? Offer the presets (wood, hardwood, plywood, acrylic, aluminum, steel, brass). If theirs is not among them, pick the closest preset AND set spindle_speed, feed_rate, plunge_rate and step_down yourself from known cutting data for that material and cutter. 2. Which cutter diameters do they actually own? Recommend one from that list: it must fit the smallest inner radius of the part, and a smaller cutter means longer machining time. If you cannot judge the geometry yet, say so and suggest generating once to see the dimensions. 3. Is the part cut all the way through? Then recommend holding_tabs so it does not come loose on the last pass.
| Argument | Type | Description |
|---|---|---|
| content* | string | Output of: (xz -9e -c model.step 2>/dev/null || gzip -9c model.step) | base64 | tr -d '\n' - the compressed file, base64 encoded (xz, bzip2 or gzip, detected by itself). |
| filename* |
We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 13, 2026.
| material | string | Workpiece material. Sets spindle speed, feed rates and depth per pass. Ask the user - the default assumes softwood. |
| origin_z | string | Z zero on the stock surface (top) or on the spoilboard. Default top. |
| feed_rate | number | Horizontal feed in mm/min. Same rule: only for materials outside the presets. |
| origin_xy | string | Workpiece origin in XY. Default TL. |
| step_down | number | Depth per pass in mm. Rule of thumb: up to 1x cutter diameter in softwood, far less in metal. |
| controller | string | Laser G-code dialect. GRBL (default) or Marlin (3D printers with a laser module, MPCNC/LowRider and other Marlin machines). |
| image_mode | string | Images only. Line by line (raster): dither for photos (default), threshold for logos and text, grayscale for power per dot. Along lines (vector, much faster): outline traces the edges of dark areas, centerline follows the middle of strokes (line art, handwriting), edges turns a photo into a line drawing. |
| ramp_entry | boolean | Enter the material on a ramp instead of plunging straight down |
| laser_max_s | number | Highest S value of the machine: $30 on GRBL (usually 1000), 255 on most Marlin machines. Ask the user - a wrong value burns at the wrong power. |
| laser_power | number | Laser power in % (laser jobs). For images the default is 30 %. |
| laser_speed | number | Laser speed in mm/min (laser jobs). For images the default is 2000. |
| plunge_rate | number | Plunge feed in mm/min, for materials outside the presets. |
| bit_diameter | number | Cutter diameter in mm, e.g. 6. Leave it out and step2gcode picks the largest whole millimetre that still fits through the smallest hole in the part. |
| holding_tabs | boolean | Leave small bridges so a part cut all the way through stays in the stock. Strongly recommended for through cuts. |
| spindle_speed | number | Spindle speed in rpm. Only set this when the material is not covered by the presets - otherwise the preset value is used. |
| image_width_mm | number | Images only: engraving width in mm; the height follows from the aspect ratio. Default 100. |
| inner_contours | boolean | Cut inner contours and cut-outs |
{
"type": "object",
"required": [
"file_id"
],
"properties": {
"depth": {
"type": "number",
"description": "Cutting depth in mm; omit to take it from the model geometry (STEP)"
},
"holes": {
"type": "boolean",
"description": "Drill or mill the holes found in the geometry"
},
"laser": {
"type": "boolean",
"description": "Laser job instead of milling"
},
"file_id": {
"type": "string",
"description": "file_id from check_upload or upload_file_content"
},
"pockets": {
"type": "boolean",
"description": "Mill pockets found in the geometry (STEP)"
},
"material": {
"enum": [
"wood",
"hardwood",
"plywood",
"acrylic",
"aluminum",
"steel",
"brass"
],
"type": "string",
"description": "Workpiece material. Sets spindle speed, feed rates and depth per pass. Ask the user - the default assumes softwood."
},
"origin_z": {
"enum": [
"top",
"spoilboard"
],
"type": "string",
"description": "Z zero on the stock surface (top) or on the spoilboard. Default top."
},
"feed_rate": {
"type": "number",
"description": "Horizontal feed in mm/min. Same rule: only for materials outside the presets."
},
"origin_xy": {
"enum": [
"TL",
"TR",
"BL",
"BR",
"C"
],
"type": "string",
"description": "Workpiece origin in XY. Default TL."
},
"step_down": {
"type": "number",
"description": "Depth per pass in mm. Rule of thumb: up to 1x cutter diameter in softwood, far less in metal."
},
"controller": {
"enum": [
"grbl",
"marlin"
],
"type": "string",
"description": "Laser G-code dialect. GRBL (default) or Marlin (3D printers with a laser module, MPCNC/LowRider and other Marlin machines)."
},
"image_mode": {
"enum": [
"dither",
"threshold",
"grayscale",
"outline",
"centerline",
"edges"
],
"type": "string",
"description": "Images only. Line by line (raster): dither for photos (default), threshold for logos and text, grayscale for power per dot. Along lines (vector, much faster): outline traces the edges of dark areas, centerline follows the middle of strokes (line art, handwriting), edges turns a photo into a line drawing."
},
"ramp_entry": {
"type": "boolean",
"description": "Enter the material on a ramp instead of plunging straight down"
},
"laser_max_s": {
"type": "number",
"description": "Highest S value of the machine: $30 on GRBL (usually 1000), 255 on most Marlin machines. Ask the user - a wrong value burns at the wrong power."
},
"laser_power": {
"type": "number",
"description": "Laser power in % (laser jobs). For images the default is 30 %."
},
"laser_speed": {
"type": "number",
"description": "Laser speed in mm/min (laser jobs). For images the default is 2000."
},
"plunge_rate": {
"type": "number",
"description": "Plunge feed in mm/min, for materials outside the presets."
},
"bit_diameter": {
"type": "number",
"description": "Cutter diameter in mm, e.g. 6. Leave it out and step2gcode picks the largest whole millimetre that still fits through the smallest hole in the part."
},
"holding_tabs": {
"type": "boolean",
"description": "Leave small bridges so a part cut all the way through stays in the stock. Strongly recommended for through cuts."
},
"spindle_speed": {
"type": "number",
"description": "Spindle speed in rpm. Only set this when the material is not covered by the presets - otherwise the preset value is used."
},
"image_width_mm": {
"type": "number",
"description": "Images only: engraving width in mm; the height follows from the aspect ratio. Default 100."
},
"inner_contours": {
"type": "boolean",
"description": "Cut inner contours and cut-outs"
}
}
}| string |
| File name including extension, e.g. bracket.step |
{
"type": "object",
"required": [
"filename",
"content"
],
"properties": {
"content": {
"type": "string",
"description": "Output of: (xz -9e -c model.step 2>/dev/null || gzip -9c model.step) | base64 | tr -d '\\n' - the compressed file, base64 encoded (xz, bzip2 or gzip, detected by itself)."
},
"filename": {
"type": "string",
"description": "File name including extension, e.g. bracket.step"
}
}
}