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
- In Claude, open Settings → Connectors → Add custom connector.
- Enter the server URL:
https://cnc.xtract.bot/mcp - 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.
- 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
}