{
  "openapi": "3.1.1",
  "info": {
    "title": "Kerfwise G-code Inspector API",
    "version": "1.0.0",
    "summary": "Check G-code: cutting and rapid length, machining time, bounds, tools, warnings, preview.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://kerfwisegcodeinspector.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Check G-code: cutting and rapid length, machining time, bounds, tools, warnings, preview.",
        "description": "Reads a CNC G-code program (GRBL, LinuxCNC, Fanuc-style, most CAM posts) and measures the rapid and cutting path, the machining time with an optional acceleration and cornering model, the X/Y/Z bounds and Z range, statistics per tool and the feeds and spindle speeds used. It warns about missing units, missing feed rates, arcs whose radius does not match, sideways rapids below Z0 and other common mistakes, and draws a top-view toolpath preview.\n\nMethodology:\n- Each line is read like an RS-274/NGC controller: comments in ( ) and after ; are removed, modal state (motion G0–G3 and canned cycles, plane G17/G18/G19, units G20/G21, distance mode G90/G91, arc centre mode G90.1/G91.1, feed mode G93/G94/G95, retract G98/G99, F, S, T) carries over from line to line. Inch programs are converted to mm.\n- Arcs: the centre comes from I/J/K (relative to the start unless G90.1) or from R (negative R = more than 180°). The length is the swept angle × radius, combined with the linear axis for helical arcs. If start and end radius differ by more than 0.002 mm (or 0.1 %), a warning is raised, as many controllers stop there.\n- Canned cycles G81/G82/G83/G73/G85/G89 are expanded into their rapid and feed moves (G83 retracts to R after each peck and rapids back to 0.254 mm above the last depth).\n- Time without acceleration = feed moves ÷ feed + rapids ÷ rapid rate + dwells (G4 P seconds, Fanuc G4 X seconds) + tool changes + program stops. With an acceleration, every move follows a trapezoid profile with GRBL-style look-ahead: corner speed from the junction deviation, arcs limited to √(a × r), full stops at dwells, tool changes, spindle changes and pauses.\n- Bounds cover every move (rapids included); cutting bounds cover the feed moves only. Statistics are split per tool loaded with M6 (or with T alone in programs without M6).\n\nAn estimate, not a machine simulation: controller look-ahead, feed overrides, tool-change routines and macros differ. Always dry-run a program above the material first.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kerfwise_gcode_inspector",
        "x-owner-app": "kerfwise",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KerfwiseGcodeInspectorInput"
              },
              "examples": {
                "example1": {
                  "summary": "Hand-checkable square with one R10 corner arc (G21, G3 with I/J)",
                  "value": {
                    "gcode": "(40 x 40 mm pocket outline with one R10 corner)\nG21 G90 G17\nG0 Z5\nG0 X0 Y0\nS12000 M3\nG1 Z-1 F300\nG1 X40 F1200\nG1 Y30\nG3 X30 Y40 I-10 J0\nG1 X0\nG1 Y0\nG0 Z5\nM5\nM30"
                  }
                },
                "example2": {
                  "summary": "Inch program: G83 peck drilling, two tools, R-format arcs, acceleration 500 mm/s²",
                  "value": {
                    "gcode": "%\nO1001 (INCH DRILLING AND ARCS)\nG20 G90 G17 G40 G80\nT2 M06\nG43 H02 Z1.0\nS6000 M03\nG0 X1.0 Y1.0\nG0 Z0.1\nG98 G83 X1.0 Y1.0 Z-0.75 R0.1 Q0.25 F10.0\nX2.0\nX3.0\nG80\nT1 M06\nG43 H01 Z1.0\nS18000 M03\nG0 X0.5 Y0.5\nG0 Z0.05\nG1 Z-0.125\nG2 X1.5 Y0.5 R0.5 F40.0\nG2 X0.5 Y0.5 R0.5\nG0 Z1.0\nM05\nM30\n%",
                    "acceleration": 500
                  }
                },
                "example3": {
                  "summary": "Kerfwise LinuxCNC program for one sheet (drilling + profiles)",
                  "value": {
                    "gcode": "(Kerfwise CNC - Kerfwise sample sheet - dialect linuxcnc)\nG21 G90 G17 G40 G49 G80 G94 G64 P0.02\n(Kerfwise sample sheet: 1220 x 610 x 18 mm)\n(Z0 = material top, origin bottom-left, 2 parts, profile tool D6 radius compensation in the path)\n(Tool T2: Drill D5 shelf pins, hinge plates, handles, D5, 6000 rpm)\nT2 M6\nG43 H2\nS6000 M3\nG4 P2\n(Drill D5: 4 holes)\nG0 Z15\nG0 X278 Y132\nG0 Z3\nG98 G81 X278 Y132 Z-12 R3 F1200\nX584 Y132\nX584 Y558\nX278 Y558\nG80\nG0 Z15\nM5\n(Tool T1: Compression end mill D6, D6, 18000 rpm)\nT1 M6\nG43 H1\nS18000 M3\nG4 P2\n(Profile A1 4 passes, 3 tabs)\nG0 X318 Y345\nG0 Z3\nG1 Z0 F1500\nG1 Y305 Z-4.575 F3000\nG1 Y95 F6000\nG2 X315 Y92 I-3 J0\nG1 X15\nG2 X12 Y95 I0 J3\nG1 Y595\nG2 X15 Y598 I3 J0\nG1 X315\nG2 X318 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG1 Y305 Z-9.15 F3000\nG1 Y95 F6000\nG2 X315 Y92 I-3 J0\nG1 X15\nG2 X12 Y95 I0 J3\nG1 Y595\nG2 X15 Y598 I3 J0\nG1 X315\nG2 X318 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG1 Y305 Z-13.725 F3000\nG1 Y95 F6000\nG2 X315 Y92 I-3 J0\nG1 X15\nG2 X12 Y95 I0 J3\nG1 Y595\nG2 X15 Y598 I3 J0\nG1 X315\nG2 X318 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG1 Y305 Z-18.3 F3000\nG1 Y95 F6000\nG2 X315 Y92 I-3 J0\nG1 X172\nG1 Z-16 F1500\nG1 X158 F6000\nG1 Z-18.3 F1500\nG1 X15 F6000\nG2 X12 Y95 I0 J3\nG1 Y338\nG1 Z-16 F1500\nG1 Y352 F6000\nG1 Z-18.3 F1500\nG1 Y595 F6000\nG2 X15 Y598 I3 J0\nG1 X158\nG1 Z-16 F1500\nG1 X172 F6000\nG1 Z-18.3 F1500\nG1 X315 F6000\nG2 X318 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG0 Z15\n(Profile A2 4 passes, 3 tabs)\nG0 X624\nG0 Z3\nG1 Z0 F1500\nG1 Y305 Z-4.575 F3000\nG1 Y95 F6000\nG2 X621 Y92 I-3 J0\nG1 X321\nG2 X318 Y95 I0 J3\nG1 Y595\nG2 X321 Y598 I3 J0\nG1 X621\nG2 X624 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG1 Y305 Z-9.15 F3000\nG1 Y95 F6000\nG2 X621 Y92 I-3 J0\nG1 X321\nG2 X318 Y95 I0 J3\nG1 Y595\nG2 X321 Y598 I3 J0\nG1 X621\nG2 X624 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG1 Y305 Z-13.725 F3000\nG1 Y95 F6000\nG2 X621 Y92 I-3 J0\nG1 X321\nG2 X318 Y95 I0 J3\nG1 Y595\nG2 X321 Y598 I3 J0\nG1 X621\nG2 X624 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG1 Y305 Z-18.3 F3000\nG1 Y95 F6000\nG2 X621 Y92 I-3 J0\nG1 X478\nG1 Z-16 F1500\nG1 X464 F6000\nG1 Z-18.3 F1500\nG1 X321 F6000\nG2 X318 Y95 I0 J3\nG1 Y338\nG1 Z-16 F1500\nG1 Y352 F6000\nG1 Z-18.3 F1500\nG1 Y595 F6000\nG2 X321 Y598 I3 J0\nG1 X464\nG1 Z-16 F1500\nG1 X478 F6000\nG1 Z-18.3 F1500\nG1 X621 F6000\nG2 X624 Y595 I0 J-3\nG1 Y345\nG1 Y305\nG1 Y345\nG0 Z15\nM5\nG0 X0 Y0\nM2\n",
                    "startZ": 15
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KerfwiseGcodeInspectorResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "KerfwiseGcodeInspectorInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "gcode": {
            "title": "G-code",
            "description": "Paste the program, or upload a file below.",
            "type": "string",
            "maxLength": 4000000
          },
          "file": {
            "title": "Or: G-code file",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 4096 KiB. Accepts .nc,.ngc,.gcode,.gc,.tap,.cnc,.txt,.nc1.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "rapidRate": {
            "title": "Rapid rate (G0)",
            "type": "number",
            "minimum": 100,
            "maximum": 200000,
            "default": 10000,
            "x-unit": "mm/min"
          },
          "toolChangeSec": {
            "title": "Time per tool change (M6)",
            "type": "number",
            "minimum": 0,
            "maximum": 600,
            "default": 20,
            "x-unit": "s"
          },
          "pauseSec": {
            "title": "Time per program stop (M0/M1)",
            "description": "For example a manual tool change on GRBL.",
            "type": "number",
            "minimum": 0,
            "maximum": 3600,
            "default": 0,
            "x-unit": "s"
          },
          "acceleration": {
            "title": "Acceleration (0 = ignore)",
            "description": "With a value, every move speeds up and slows down (trapezoid profile) and corners are taken at the GRBL junction-deviation speed.",
            "type": "number",
            "minimum": 0,
            "maximum": 50000,
            "default": 0,
            "x-unit": "mm/s²"
          },
          "junctionDeviation": {
            "title": "Junction deviation",
            "type": "number",
            "minimum": 0.001,
            "maximum": 1,
            "default": 0.01,
            "x-unit": "mm"
          },
          "defaultUnits": {
            "title": "Units when the program sets none",
            "type": "string",
            "enum": [
              "mm",
              "inch"
            ],
            "default": "mm"
          },
          "startX": {
            "title": "Start X",
            "type": "number",
            "minimum": -100000,
            "maximum": 100000,
            "default": 0,
            "x-unit": "mm"
          },
          "startY": {
            "title": "Start Y",
            "type": "number",
            "minimum": -100000,
            "maximum": 100000,
            "default": 0,
            "x-unit": "mm"
          },
          "startZ": {
            "title": "Start Z",
            "type": "number",
            "minimum": -100000,
            "maximum": 100000,
            "default": 0,
            "x-unit": "mm"
          }
        },
        "required": []
      },
      "KerfwiseGcodeInspectorResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "lines",
              "lengths",
              "time",
              "counts",
              "bounds",
              "tools",
              "warnings"
            ],
            "properties": {
              "lines": {
                "type": "integer"
              },
              "blocks": {
                "type": "integer"
              },
              "declaredUnits": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "unitsUsed": {
                "type": "string"
              },
              "dialectHints": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "lengths": {
                "type": "object",
                "properties": {
                  "rapid": {
                    "type": "number"
                  },
                  "feed": {
                    "type": "number"
                  },
                  "arc": {
                    "type": "number"
                  },
                  "total": {
                    "type": "number"
                  }
                }
              },
              "time": {
                "type": "object",
                "properties": {
                  "totalSec": {
                    "type": "number"
                  },
                  "feedSec": {
                    "type": "number"
                  },
                  "rapidSec": {
                    "type": "number"
                  },
                  "dwellSec": {
                    "type": "number"
                  },
                  "toolChangeSec": {
                    "type": "number"
                  },
                  "pauseSec": {
                    "type": "number"
                  },
                  "noAccelSec": {
                    "type": "number"
                  },
                  "accelModel": {
                    "type": "boolean"
                  }
                }
              },
              "counts": {
                "type": "object",
                "properties": {
                  "rapid": {
                    "type": "integer"
                  },
                  "linear": {
                    "type": "integer"
                  },
                  "arcs": {
                    "type": "integer"
                  },
                  "drillCycles": {
                    "type": "integer"
                  },
                  "toolChanges": {
                    "type": "integer"
                  },
                  "pauses": {
                    "type": "integer"
                  },
                  "dwells": {
                    "type": "integer"
                  },
                  "comments": {
                    "type": "integer"
                  }
                }
              },
              "bounds": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "x0": {
                    "type": "number"
                  },
                  "y0": {
                    "type": "number"
                  },
                  "z0": {
                    "type": "number"
                  },
                  "x1": {
                    "type": "number"
                  },
                  "y1": {
                    "type": "number"
                  },
                  "z1": {
                    "type": "number"
                  }
                }
              },
              "cuttingBounds": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "x0": {
                    "type": "number"
                  },
                  "y0": {
                    "type": "number"
                  },
                  "z0": {
                    "type": "number"
                  },
                  "x1": {
                    "type": "number"
                  },
                  "y1": {
                    "type": "number"
                  },
                  "z1": {
                    "type": "number"
                  }
                }
              },
              "zRange": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "min": {
                    "type": "number"
                  },
                  "max": {
                    "type": "number"
                  }
                }
              },
              "feeds": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "spindleSpeeds": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "tools": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "tool": {
                      "type": "string"
                    },
                    "loads": {
                      "type": "integer"
                    },
                    "feedLength": {
                      "type": "number"
                    },
                    "rapidLength": {
                      "type": "number"
                    },
                    "feedTimeSec": {
                      "type": "number"
                    },
                    "moves": {
                      "type": "integer"
                    },
                    "arcs": {
                      "type": "integer"
                    },
                    "maxFeed": {
                      "type": "number"
                    },
                    "minZ": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "maxSpindle": {
                      "type": "number"
                    }
                  }
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "level": {
                      "type": "string"
                    },
                    "line": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "count": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              },
              "endsProperly": {
                "type": "boolean"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}