Connecting Claude (MCP)

The workbench exposes a remote MCP server with OAuth 2.1 sign-in, so Claude web (and any MCP client) can work on your files with you.

Connect Claude web

  1. In Claude, open Settings → Connectors → Add custom connector.
  2. Enter the server URL: https://cnc.xtract.bot/mcp
  3. Claude registers itself and sends you to this site to sign in (GitHub or GitLab) and approve the connection. You can revoke it any time from Account → Connected apps in the workbench.
  4. Start a chat, enable the connector, and ask e.g. "List my CNC files and analyse the newest one."

Discovery endpoints: https://cnc.xtract.bot/.well-known/oauth-authorization-server and https://cnc.xtract.bot/.well-known/oauth-protected-resource. Tokens are bearer tokens (1 h) with refresh (30 d); all token material is stored hashed.

What the assistant can do

Everything you can do in the UI, plus analysis: list/read/create/edit/move/delete files and folders, analyse and simulate programs, generate G-code from drawings and models, browse/import/publish gallery designs, comment — and, while you have a file open, see your viewport and highlighted region and draw highlights or move the playhead in your view. Moderators additionally get comment-moderation tools.

Tool reference

get_help

Read the manual for this MCP server: concepts (files, kinds, settings, the work coordinate system), the editing workflow, the live 3D view, G-code generation from drawings/models, the svg and model document formats, the gallery, and worked examples. Call this first in a session.

input schema
{
  "type": "object",
  "properties": {
    "section": {
      "type": "string",
      "description": "optional: only return the section whose heading contains this text"
    }
  },
  "additionalProperties": false
}

list_files

List the user's folders and files. Returns { folders: [{id, name, parentId}], files: [{id, kind, name, folderId, bytes, updatedAt, url}], storage: {usedBytes, quotaBytes} } — a flat list; a file's folderId (null = root) says where it sits. Per-file bytes include its saved simulation state. Use the ids with get_file / update_file.

input schema
{
  "type": "object",
  "properties": {
    "q": {
      "type": "string",
      "description": "optional: only files whose name contains this text (case-insensitive)"
    }
  },
  "additionalProperties": false
}

get_file

Read one file: { id, kind, name, folderId, bytes, lineCount, settings, workspace (where the stock is in work coordinates), url, content }. content is the G-code text for gcode files or the JSON document for svg/model files. For long programs pass lines {from,to} (1-based inclusive) to read a numbered window instead. Set includeContent false for metadata only.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id from list_files"
    },
    "includeContent": {
      "type": "boolean",
      "description": "default true"
    },
    "lines": {
      "type": "object",
      "properties": {
        "from": {
          "type": "integer",
          "description": "first line, 1-based"
        },
        "to": {
          "type": "integer",
          "description": "last line, inclusive"
        }
      },
      "required": [
        "from",
        "to"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

create_file

Create a file. kind gcode: content is the G-code text. kind svg / model: content is the JSON document (see get_help §5/§6); omit content for an empty document. settings is deep-merged over the defaults (600×600×200 machine, 120×120×30 stock at (0,0), origin (0,0), Ø6 tool, 0.25 mm resolution, Z0 at the stock top), so {stock: {w: 80}} keeps the other stock fields. Returns { file, analysis, storage }; for gcode the analysis includes the full simulation (removedVolumeCm3, collisions…) — relay its issues, no separate analyze_gcode call needed.

input schema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "file name (≤ 80 chars)"
    },
    "kind": {
      "type": "string",
      "enum": [
        "gcode",
        "svg",
        "model"
      ],
      "description": "File kind: gcode (CNC program text), svg (2D drawing document), model (3D part document)."
    },
    "content": {
      "type": "string",
      "description": "G-code text, or JSON document for svg/model"
    },
    "settings": {
      "type": "object",
      "properties": {
        "machine": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "bed X, mm"
            },
            "y": {
              "type": "number",
              "description": "bed Y, mm"
            },
            "z": {
              "type": "number",
              "description": "Z travel, mm"
            }
          },
          "additionalProperties": false
        },
        "stock": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "stock front-left X on the bed, mm"
            },
            "y": {
              "type": "number",
              "description": "stock front-left Y, mm"
            },
            "w": {
              "type": "number",
              "description": "width along X, mm"
            },
            "d": {
              "type": "number",
              "description": "depth along Y, mm"
            },
            "h": {
              "type": "number",
              "description": "height (thickness), mm"
            }
          },
          "additionalProperties": false
        },
        "origin": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "program X0 offset from the stock's front-left corner, mm"
            },
            "y": {
              "type": "number",
              "description": "program Y0 offset, mm"
            }
          },
          "additionalProperties": false
        },
        "toolDiameter": {
          "type": "number",
          "description": "flat end mill diameter, mm (0.1–100)"
        },
        "resolution": {
          "type": "number",
          "description": "simulation cell size, mm (0.1–5)"
        },
        "preciseEdges": {
          "type": "boolean",
          "description": "render walls where the cutter really passed (sub-cell precision)"
        },
        "zZero": {
          "type": "string",
          "enum": [
            "top",
            "bed"
          ],
          "description": "where program Z0 is: the stock top or the machine bed"
        }
      },
      "additionalProperties": false
    },
    "folderId": {
      "type": "string",
      "description": "destination folder id (omit for root)"
    },
    "sourceFileId": {
      "type": "string",
      "description": "for generated G-code: the svg/model file it came from"
    }
  },
  "required": [
    "name",
    "kind"
  ],
  "additionalProperties": false
}

update_file

Change a file. Provide content (whole replacement; for svg/model files this is the whole JSON document — or use set_svg_ops for machining ops) or patches (line edits, G-code only), and/or settings (deep-merged partial) and/or name. Patch semantics: fromLine/toLine are 1-based inclusive and refer to the file as it was (patches are applied bottom-up, adjacent patches are fine); replacement is split on newlines into whole lines — do not add a trailing newline; toLine = fromLine-1 inserts before fromLine; an empty replacement deletes the range. The previous version is kept for 24 h (revert_file). The user's open tab reloads and re-simulates; note is shown there as a toast (dropped if no tab is open). Returns { file (with updatedAt — keep it for revert_file's expectedUpdatedAt), analysis, live: {tabsNotified} } — relay the analysis issues.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "content": {
      "type": "string",
      "description": "new full content"
    },
    "patches": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "fromLine": {
            "type": "integer",
            "description": "first line to replace"
          },
          "toLine": {
            "type": "integer",
            "description": "last line to replace (inclusive)"
          },
          "replacement": {
            "type": "string",
            "description": "text to put there; may span lines"
          }
        },
        "required": [
          "fromLine",
          "toLine",
          "replacement"
        ],
        "additionalProperties": false
      },
      "description": "line patches"
    },
    "settings": {
      "type": "object",
      "properties": {
        "machine": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "bed X, mm"
            },
            "y": {
              "type": "number",
              "description": "bed Y, mm"
            },
            "z": {
              "type": "number",
              "description": "Z travel, mm"
            }
          },
          "additionalProperties": false
        },
        "stock": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "stock front-left X on the bed, mm"
            },
            "y": {
              "type": "number",
              "description": "stock front-left Y, mm"
            },
            "w": {
              "type": "number",
              "description": "width along X, mm"
            },
            "d": {
              "type": "number",
              "description": "depth along Y, mm"
            },
            "h": {
              "type": "number",
              "description": "height (thickness), mm"
            }
          },
          "additionalProperties": false
        },
        "origin": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "program X0 offset from the stock's front-left corner, mm"
            },
            "y": {
              "type": "number",
              "description": "program Y0 offset, mm"
            }
          },
          "additionalProperties": false
        },
        "toolDiameter": {
          "type": "number",
          "description": "flat end mill diameter, mm (0.1–100)"
        },
        "resolution": {
          "type": "number",
          "description": "simulation cell size, mm (0.1–5)"
        },
        "preciseEdges": {
          "type": "boolean",
          "description": "render walls where the cutter really passed (sub-cell precision)"
        },
        "zZero": {
          "type": "string",
          "enum": [
            "top",
            "bed"
          ],
          "description": "where program Z0 is: the stock top or the machine bed"
        }
      },
      "additionalProperties": false
    },
    "name": {
      "type": "string",
      "description": "new name"
    },
    "note": {
      "type": "string",
      "description": "one sentence for the user about this change (shown in their workbench)"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

move_file

Move a file to another folder (folderId null/omitted = root) and/or rename it.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "folderId": {
      "type": "string",
      "description": "destination folder id"
    },
    "name": {
      "type": "string",
      "description": "new name"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

delete_file

Permanently delete a file (and its saved simulation state). Requires confirm: true — only after the user asked for the deletion.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "confirm": {
      "type": "boolean",
      "description": "must be true"
    }
  },
  "required": [
    "fileId",
    "confirm"
  ],
  "additionalProperties": false
}

create_folder

Create a folder (parentId omitted = root).

input schema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "folder name"
    },
    "parentId": {
      "type": "string",
      "description": "parent folder id"
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}

get_storage_usage

Storage usage for the user: bytes used (file bodies plus saved simulation states) and the 50 MB quota. Check before creating large files.

input schema
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

analyze_gcode

Parse and (by default) simulate a program: fileId, or raw gcode plus optional settings (a dry run before saving). Returns { lineCount, moveCount, rapidCount, cutCount, arcCount, rapidTravel, cutTravel, bbox (work coords of cutting moves), feeds, estimatedSeconds, minZ, warnings (parser), simulation: { resolution, removedVolumeCm3, remainingVolumeCm3, pieces, largestPiece {w,d,h,x,y}, rapidCollisions [{moveIndex, line}], bedCollisions [{moveIndex, line}] }, issues: [plain-language problems] }. create_file / update_file / the generators already return this for the saved file; use analyze_gcode for raw text or to re-check later. Always relay issues.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "analyse this file"
    },
    "gcode": {
      "type": "string",
      "description": "or analyse this program text"
    },
    "settings": {
      "type": "object",
      "properties": {
        "machine": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "bed X, mm"
            },
            "y": {
              "type": "number",
              "description": "bed Y, mm"
            },
            "z": {
              "type": "number",
              "description": "Z travel, mm"
            }
          },
          "additionalProperties": false
        },
        "stock": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "stock front-left X on the bed, mm"
            },
            "y": {
              "type": "number",
              "description": "stock front-left Y, mm"
            },
            "w": {
              "type": "number",
              "description": "width along X, mm"
            },
            "d": {
              "type": "number",
              "description": "depth along Y, mm"
            },
            "h": {
              "type": "number",
              "description": "height (thickness), mm"
            }
          },
          "additionalProperties": false
        },
        "origin": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "program X0 offset from the stock's front-left corner, mm"
            },
            "y": {
              "type": "number",
              "description": "program Y0 offset, mm"
            }
          },
          "additionalProperties": false
        },
        "toolDiameter": {
          "type": "number",
          "description": "flat end mill diameter, mm (0.1–100)"
        },
        "resolution": {
          "type": "number",
          "description": "simulation cell size, mm (0.1–5)"
        },
        "preciseEdges": {
          "type": "boolean",
          "description": "render walls where the cutter really passed (sub-cell precision)"
        },
        "zZero": {
          "type": "string",
          "enum": [
            "top",
            "bed"
          ],
          "description": "where program Z0 is: the stock top or the machine bed"
        }
      },
      "additionalProperties": false
    },
    "simulate": {
      "type": "boolean",
      "description": "run the material simulation (default true; set false for a quick parse of huge files)"
    }
  },
  "additionalProperties": false
}

get_live_view

What the user sees right now for a file: { connected (boolean), connections (open tabs), viewport {position, target}, userHighlights [{id, label, min [x,y,z], max [x,y,z]}] (regions THEY marked, work coordinates, oldest first) plus a top-level userNote string (their free-text note, null if none), agentHighlights (yours), playhead {moveIndex, total, playing}, selection {fromLine, toLine, text} (lines they clicked in the listing), stats {pieces, largestPiece, removedVolumeCm3, rapidCollisions [{moveIndex, line}], …}, program {moveCount, warnings} }. Use it when the user says 'this', 'here' or 'what I marked'. If connected is false no tab is open (the file still exists; highlights show when it is opened). Use get_open_files to find which file is open.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

highlight_region

Draw labelled translucent boxes in the user's 3D view for a file (work coordinates, mm; Z0 = stock top when zZero is top). Each call REPLACES your previous boxes for that file (≤ 20). min/max are normalised per axis and thin boxes render at least 0.5 mm thick, so a locate_moves match's box works directly. focusCamera true flies their camera to the first box. clear true removes all your boxes. Returns { shown, tabsNotified } — tabsNotified 0 means no tab is open and the boxes will show when the file is opened.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "boxes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "min": {
            "type": "array",
            "items": {
              "type": "number",
              "description": "mm"
            },
            "description": "[x, y, z] lower corner"
          },
          "max": {
            "type": "array",
            "items": {
              "type": "number",
              "description": "mm"
            },
            "description": "[x, y, z] upper corner"
          },
          "label": {
            "type": "string",
            "description": "short label shown above the box"
          },
          "color": {
            "type": "string",
            "description": "CSS colour, e.g. #f59e0b"
          }
        },
        "required": [
          "min",
          "max"
        ],
        "additionalProperties": false
      },
      "description": "boxes to draw (≤ 20)"
    },
    "focusCamera": {
      "type": "boolean",
      "description": "fly the camera to the first box"
    },
    "clear": {
      "type": "boolean",
      "description": "remove existing highlights instead"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

set_playhead

Move the user's simulation to moveIndex so they see the material right after that move has been applied ('move N / total' in the workbench; 0 = untouched stock). Playback pauses if it was running. Use locate_moves to turn a line number or a region into a moveIndex. Returns { moveIndex, tabsNotified }.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "moveIndex": {
      "type": "integer",
      "description": "move index to show"
    }
  },
  "required": [
    "fileId",
    "moveIndex"
  ],
  "additionalProperties": false
}

send_note

Show a one-sentence message in the user's workbench (a toast) for a file they have open, e.g. 'Added a retract before the rapid at move 412'. Dropped if no tab is open (check tabsNotified). For edits prefer update_file's note.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "text": {
      "type": "string",
      "description": "≤ 200 characters"
    }
  },
  "required": [
    "fileId",
    "text"
  ],
  "additionalProperties": false
}

generate_gcode_from_svg

Generate a G-code program from an svg file's shapes (per-shape op: pocket / profile-outside / profile-inside / engrave; defaultOp applies to shapes without one) and save it as a gcode file (new, or update outputFileId) with the drawing's settings and sourceFileId set. Coordinates: program X = drawing x, program Y = document height − drawing y (the document box), so the document's bottom-left corner is X0 Y0. Defaults: feed 800, plunge 250 mm/min, stepdown 2 mm, stepover 0.45 × tool, safeZ 5. Shapes are machined in document order. Generated programs start 'G21 G90 G17' + 'G0 Z<safeZ>' and end 'G0 Z<safeZ>' / 'G0 X0 Y0' / 'M2' — no spindle command (add 'M3 S…' with a patch if the machine needs it). save false = dry run (returns the program text, nothing written). Returns { file, ops, warnings, cutLengthMm, moveCount, analysis }.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "svg file id"
    },
    "outputName": {
      "type": "string",
      "description": "name for the G-code file (default '<drawing> toolpaths')"
    },
    "outputFileId": {
      "type": "string",
      "description": "update this existing gcode file instead of creating one"
    },
    "outputFolderId": {
      "type": "string",
      "description": "folder for a new file"
    },
    "onlyObjectIds": {
      "type": "array",
      "items": {
        "type": "string",
        "description": "object id"
      },
      "description": "machine only these objects (and their children)"
    },
    "defaultOp": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "pocket",
            "profile-outside",
            "profile-inside",
            "engrave"
          ]
        },
        "depth": {
          "type": "number",
          "description": "mm below Z0"
        },
        "stepdown": {
          "type": "number",
          "description": "mm per pass"
        },
        "feed": {
          "type": "number",
          "description": "mm/min"
        },
        "plunge": {
          "type": "number",
          "description": "mm/min"
        },
        "stepover": {
          "type": "number",
          "description": "fraction of tool diameter (pockets)"
        }
      },
      "additionalProperties": false
    },
    "cam": {
      "type": "object",
      "properties": {
        "safeZ": {
          "type": "number",
          "description": "retract height above Z0, mm (default 5)"
        },
        "feed": {
          "type": "number",
          "description": "default feed, mm/min (800)"
        },
        "plunge": {
          "type": "number",
          "description": "default plunge, mm/min (250)"
        },
        "stepdown": {
          "type": "number",
          "description": "default stepdown, mm (2)"
        },
        "stepover": {
          "type": "number",
          "description": "default stepover fraction (0.45)"
        }
      },
      "additionalProperties": false
    },
    "save": {
      "type": "boolean",
      "description": "false = dry run: return the program without saving (default true)"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

generate_gcode_from_model

Generate waterline-roughing G-code from a model file: for each distinct top height (highest first) pocket the region whose final surface is at or below it, within the stock footprint. Frame: model X/Y are program X/Y (not re-centred), model Z0 is the part's bottom sitting on the bed, stockTop is the stock top in model Z (default stock.h) — anything above the part's top is faced off first. clearOutsideTo: omitted or null = leave the stock around the part untouched (safe default); pass a model Z (e.g. 0 = the bed) to clear the surrounding stock down to it — at 0 the part is cut free on the last pass and there are no holding tabs, so warn the user. Saves with the model file's settings and sourceFileId set. Defaults: stepdown 2 mm, feed 800, plunge 250, stepover 0.45, safeZ 5; same header/footer as the svg generator (no spindle command). save false = dry run. Returns { file, ops, warnings, cutLengthMm, moveCount, analysis }.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "model file id"
    },
    "outputName": {
      "type": "string",
      "description": "name for the G-code file"
    },
    "outputFileId": {
      "type": "string",
      "description": "update this existing gcode file"
    },
    "outputFolderId": {
      "type": "string",
      "description": "folder for a new file"
    },
    "stockTop": {
      "type": "number",
      "description": "stock top in model Z (default stock.h)"
    },
    "clearOutsideTo": {
      "type": [
        "number",
        "null"
      ],
      "description": "model Z to clear the surrounding stock down to (0 = bed, cuts the part free); omitted or null = leave the surrounding stock"
    },
    "stepdown": {
      "type": "number",
      "description": "max depth per pass, mm (default 2)"
    },
    "cam": {
      "type": "object",
      "properties": {
        "safeZ": {
          "type": "number",
          "description": "retract height, mm (5)"
        },
        "feed": {
          "type": "number",
          "description": "mm/min (800)"
        },
        "plunge": {
          "type": "number",
          "description": "mm/min (250)"
        },
        "stepover": {
          "type": "number",
          "description": "fraction of tool diameter (0.45)"
        }
      },
      "additionalProperties": false
    },
    "save": {
      "type": "boolean",
      "description": "false = dry run: return the program without saving (default true)"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

import_svg_markup

Create an svg file from raw <svg> markup (paths, rects, circles → ellipses, ellipses, polygons/polylines, lines, groups, transforms). Scale: a width/height with units (mm, cm, in, px at 96 dpi) plus a viewBox sets it; with no width or a unitless one, 1 user unit = 1 mm. Filled shapes default to fill #3b82f6 (fill is cosmetic — machining follows op). stock.w/d default to the drawing's width/height (stock.h stays 30 mm — pass settings.stock.h for the material; a partial settings.stock keeps the drawing-derived w/d). Shapes are imported at the top level, groups become groups. Returns { file, document } with object ids so you can assign ops with set_svg_ops.

input schema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "file name"
    },
    "svg": {
      "type": "string",
      "description": "the SVG markup"
    },
    "folderId": {
      "type": "string",
      "description": "folder id"
    },
    "settings": {
      "type": "object",
      "properties": {
        "machine": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "bed X, mm"
            },
            "y": {
              "type": "number",
              "description": "bed Y, mm"
            },
            "z": {
              "type": "number",
              "description": "Z travel, mm"
            }
          },
          "additionalProperties": false
        },
        "stock": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "stock front-left X on the bed, mm"
            },
            "y": {
              "type": "number",
              "description": "stock front-left Y, mm"
            },
            "w": {
              "type": "number",
              "description": "width along X, mm"
            },
            "d": {
              "type": "number",
              "description": "depth along Y, mm"
            },
            "h": {
              "type": "number",
              "description": "height (thickness), mm"
            }
          },
          "additionalProperties": false
        },
        "origin": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number",
              "description": "program X0 offset from the stock's front-left corner, mm"
            },
            "y": {
              "type": "number",
              "description": "program Y0 offset, mm"
            }
          },
          "additionalProperties": false
        },
        "toolDiameter": {
          "type": "number",
          "description": "flat end mill diameter, mm (0.1–100)"
        },
        "resolution": {
          "type": "number",
          "description": "simulation cell size, mm (0.1–5)"
        },
        "preciseEdges": {
          "type": "boolean",
          "description": "render walls where the cutter really passed (sub-cell precision)"
        },
        "zZero": {
          "type": "string",
          "enum": [
            "top",
            "bed"
          ],
          "description": "where program Z0 is: the stock top or the machine bed"
        }
      },
      "additionalProperties": false
    }
  },
  "required": [
    "name",
    "svg"
  ],
  "additionalProperties": false
}

locate_moves

Map between source lines, move indexes and geometry of a gcode file. A move is one motion segment: every G0/G1 line (including modal lines with no G word) and every G2/G3 arc is exactly one move; comment, F-only, G21/G90 and M/S/T lines produce none. Query by lines (1-based), moveIndexes (1-based), a work-space box (3D intersection with the move's bbox; a get_live_view highlight can be passed as-is), deepest N cutting moves (lowest minZ first, ties in program order — use zAtOrBelow to get every move on that floor), or zAtOrBelow. Several filters intersect; kind defaults to any. Each match: { moveIndex, line, code, rapid, feed, from, to, minZ, box {min, max}, bbox } — pass box straight into highlight_region and moveIndex into set_playhead.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "gcode file id"
    },
    "lines": {
      "type": "array",
      "items": {
        "type": "integer",
        "description": "source line, 1-based"
      },
      "description": "lines to look up"
    },
    "moveIndexes": {
      "type": "array",
      "items": {
        "type": "integer",
        "description": "move index, 1-based"
      },
      "description": "move indexes to look up"
    },
    "box": {
      "type": "object",
      "properties": {
        "min": {
          "type": "array",
          "items": {
            "type": "number",
            "description": "mm"
          },
          "description": "[x,y,z]"
        },
        "max": {
          "type": "array",
          "items": {
            "type": "number",
            "description": "mm"
          },
          "description": "[x,y,z]"
        }
      },
      "required": [
        "min",
        "max"
      ],
      "additionalProperties": true,
      "description": "work-space box; extra fields (id, label) are ignored"
    },
    "deepest": {
      "type": "integer",
      "description": "return the N deepest cutting moves"
    },
    "zAtOrBelow": {
      "type": "number",
      "description": "cutting moves reaching this Z or lower"
    },
    "kind": {
      "type": "string",
      "enum": [
        "rapid",
        "cut",
        "any"
      ]
    },
    "limit": {
      "type": "integer",
      "description": "max matches (default 50, max 200)"
    }
  },
  "required": [
    "fileId"
  ],
  "additionalProperties": false
}

get_open_files

Which of the user's files currently have a workbench tab connected (checked live), so 'the file I have open' resolves without a name. Returns { open: [{id, name, kind, connections, playhead {moveIndex, total, playing} | null, url}], recentlyOpened: [ids] }.

input schema
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

revert_file

Restore the CONTENT a file had before its most recent content change (one level; kept 24 h; settings and name are not touched). The most recent change may be the user's own workbench save rather than your edit — pass expectedUpdatedAt (the file.updatedAt your update_file returned) and the revert is refused if the file changed since. The open tab reloads. Returns { file, analysis, revertedFrom: {updatedAt} }.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "confirm": {
      "type": "boolean",
      "description": "must be true"
    },
    "expectedUpdatedAt": {
      "type": "string",
      "description": "file.updatedAt from your last update_file; refuse if it no longer matches"
    }
  },
  "required": [
    "fileId",
    "confirm"
  ],
  "additionalProperties": false
}

rename_folder

Rename one of the user's folders (names must be unique among siblings, ≤ 80 characters).

input schema
{
  "type": "object",
  "properties": {
    "folderId": {
      "type": "string",
      "description": "folder id"
    },
    "name": {
      "type": "string",
      "description": "new name"
    }
  },
  "required": [
    "folderId",
    "name"
  ],
  "additionalProperties": false
}

delete_folder

Delete an empty folder (move or delete its files and sub-folders first).

input schema
{
  "type": "object",
  "properties": {
    "folderId": {
      "type": "string",
      "description": "folder id"
    }
  },
  "required": [
    "folderId"
  ],
  "additionalProperties": false
}

set_svg_ops

Assign, change or remove machining operations on objects of an svg file without re-sending the document: ops: [{objectId, op}] where op is {type: pocket | profile-outside | profile-inside | engrave, depth (mm below the stock top), stepdown, feed, plunge, stepover?} or null to remove. A group's op applies to children without their own. Returns the updated document summary and the list of shapes that now have ops.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "svg file id"
    },
    "ops": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "objectId": {
            "type": "string",
            "description": "object id from the document"
          },
          "op": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "pocket",
                  "profile-outside",
                  "profile-inside",
                  "engrave"
                ]
              },
              "depth": {
                "type": "number",
                "description": "mm below the stock top (positive)"
              },
              "stepdown": {
                "type": "number",
                "description": "mm per pass"
              },
              "feed": {
                "type": "number",
                "description": "mm/min"
              },
              "plunge": {
                "type": "number",
                "description": "mm/min"
              },
              "stepover": {
                "type": "number",
                "description": "fraction of tool diameter (pockets)"
              }
            },
            "required": [
              "type",
              "depth"
            ],
            "description": "the operation, or null to remove it"
          }
        },
        "required": [
          "objectId",
          "op"
        ],
        "additionalProperties": false
      },
      "description": "operations to set"
    }
  },
  "required": [
    "fileId",
    "ops"
  ],
  "additionalProperties": false
}

vote_design

Toggle the user's upvote on a published design. Returns { upvotes, voted }.

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}

delete_publication

Delete one of the user's publications from the gallery permanently (their own file is untouched). Requires confirm: true.

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    },
    "confirm": {
      "type": "boolean",
      "description": "must be true"
    }
  },
  "required": [
    "id",
    "confirm"
  ],
  "additionalProperties": false
}

update_publication

Edit the title and/or description of one of the user's published designs (the frozen file copy is unchanged).

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    },
    "title": {
      "type": "string",
      "description": "new title (≤ 120)"
    },
    "description": {
      "type": "string",
      "description": "new description (≤ 2000)"
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}

explore_public_designs

Browse the public gallery. sort: top (most upvoted, all-time) | new | imported | discussed; kind filters by file kind; q searches title/description. 24 per page. Each item has an absolute url to give the user.

input schema
{
  "type": "object",
  "properties": {
    "sort": {
      "type": "string",
      "enum": [
        "top",
        "new",
        "imported",
        "discussed"
      ]
    },
    "kind": {
      "type": "string",
      "enum": [
        "all",
        "gcode",
        "svg",
        "model"
      ]
    },
    "q": {
      "type": "string",
      "description": "search text"
    },
    "page": {
      "type": "integer",
      "description": "1-based page"
    }
  },
  "additionalProperties": false
}

get_public_design

One published design: title, author, settings, stats, counts; includeContent true adds the G-code / document.

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    },
    "includeContent": {
      "type": "boolean",
      "description": "include the body"
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}

import_public_design

Copy a published design into the user's files (folderId omitted = root). Returns the new file.

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    },
    "folderId": {
      "type": "string",
      "description": "destination folder"
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}

publish_file

Publish one of the user's files to the public gallery as a frozen copy (title ≤ 120 chars, description ≤ 2000). Only when the user asks to share it — then publish directly with a sensible title/description (from the file name, settings and analysis) and tell them both are editable with update_publication; propose first only when publishing was not explicitly requested. Returns the publication with an absolute url.

input schema
{
  "type": "object",
  "properties": {
    "fileId": {
      "type": "string",
      "description": "file id"
    },
    "title": {
      "type": "string",
      "description": "title"
    },
    "description": {
      "type": "string",
      "description": "description"
    }
  },
  "required": [
    "fileId",
    "title"
  ],
  "additionalProperties": false
}

list_my_publications

The user's published designs with their status and counts.

input schema
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

set_publication_status

Set one of the user's publications to published or unlisted (unlisted hides it from the gallery but keeps the link working).

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    },
    "status": {
      "type": "string",
      "enum": [
        "published",
        "unlisted"
      ]
    }
  },
  "required": [
    "id",
    "status"
  ],
  "additionalProperties": false
}

list_comments

Visible comments on a published design (newest last).

input schema
{
  "type": "object",
  "properties": {
    "publicationId": {
      "type": "string",
      "description": "publication id"
    }
  },
  "required": [
    "publicationId"
  ],
  "additionalProperties": false
}

post_comment

Post a comment on a published design as the user (≤ 4000 chars). Only when the user asks.

input schema
{
  "type": "object",
  "properties": {
    "publicationId": {
      "type": "string",
      "description": "publication id"
    },
    "body": {
      "type": "string",
      "description": "comment text"
    }
  },
  "required": [
    "publicationId",
    "body"
  ],
  "additionalProperties": false
}

admin_moderation_queue moderators

Moderators: the raw comment list without the policy. filter: flagged (default) | hidden | recent | all. Each item: { id, body, userName, userId, publicationId, publicationTitle, status (visible|hidden|deleted), flagged, flagReason, createdAt }. For a full moderation pass prefer admin_auto_moderate.

input schema
{
  "type": "object",
  "properties": {
    "filter": {
      "type": "string",
      "enum": [
        "flagged",
        "hidden",
        "recent",
        "all"
      ]
    },
    "limit": {
      "type": "integer",
      "description": "max items (default 100)"
    }
  },
  "additionalProperties": false
}

admin_auto_moderate moderators

Moderators: READ-ONLY briefing for a moderation pass — the moderation policy plus the pending comments (flagged first, then recent; each with commentId, author, design, flag reason, body). Nothing changes until you call admin_moderate_comments. Decide per comment: hide (reversible, comment kept but invisible), delete (permanent), restore (make a hidden one visible), clear_flag (dismiss the report, keep visible) or leave it. Present your decisions to the moderator before applying unless they told you to act autonomously; then apply them in one admin_moderate_comments call and summarise.

input schema
{
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "description": "max comments to review (default 50)"
    }
  },
  "additionalProperties": false
}

admin_moderate_comments moderators

Moderators: apply decisions to comments. action hide (keeps it, invisible), delete, restore (make visible again), clear_flag (dismiss a flag). note is recorded with the decision.

input schema
{
  "type": "object",
  "properties": {
    "decisions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "commentId": {
            "type": "string",
            "description": "comment id"
          },
          "action": {
            "type": "string",
            "enum": [
              "hide",
              "delete",
              "restore",
              "clear_flag"
            ]
          },
          "note": {
            "type": "string",
            "description": "reason, ≤ 500 chars"
          }
        },
        "required": [
          "commentId",
          "action"
        ],
        "additionalProperties": false
      },
      "description": "decisions"
    }
  },
  "required": [
    "decisions"
  ],
  "additionalProperties": false
}

admin_set_publication_status moderators

Moderators: set a design to published | unlisted | removed (removed hides it everywhere; give a reason).

input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "publication id"
    },
    "status": {
      "type": "string",
      "enum": [
        "published",
        "unlisted",
        "removed"
      ]
    },
    "reason": {
      "type": "string",
      "description": "shown to the owner"
    }
  },
  "required": [
    "id",
    "status"
  ],
  "additionalProperties": false
}

admin_ban_user moderators

Moderators: suspend (banned true) or reinstate an account. A banned user cannot sign in or use MCP tokens.

input schema
{
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "description": "user id"
    },
    "banned": {
      "type": "boolean",
      "description": "true to suspend"
    }
  },
  "required": [
    "userId",
    "banned"
  ],
  "additionalProperties": false
}