repair_hints
Analyze error messages from build123d and return specific repair hints for common issues like wrong Location syntax, missing .part, and degenerate booleans.
Instructions
Given an error message from execute(), return targeted fix suggestions for common build123d mistakes: wrong Location syntax, missing .part, CadQuery idioms, blocked imports, degenerate boolean results, fillet edge selection, and more. Pass the full error string from execute() or last_error().
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| error_text | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- The core handler function. Takes an error_text string, matches it against patterns in _HINTS, and returns a JSON string of fix suggestions.
def repair_hints(error_text: str) -> str: matches = [] for patterns, hint in _HINTS: if any(re.search(p, error_text) for p in patterns): matches.append(hint) if not matches: matches.append( "No specific hint matched. Call last_error() for the exact line and " "exception, then check: (1) shapes are non-None before boolean ops, " "(2) `from build123d import *` is in scope, " "(3) Location uses a tuple argument." ) return json.dumps({"hints": matches}, indent=2) - src/build123d_mcp/server.py:148-152 (registration)MCP tool registration using the @mcp.tool() decorator. Delegates to the handler function in src/build123d_mcp/tools/repair_hints.py.
@mcp.tool() def repair_hints(error_text: str) -> str: """Given an error message from execute(), return targeted fix suggestions for common build123d mistakes: wrong Location syntax, missing .part, CadQuery idioms, blocked imports, degenerate boolean results, fillet edge selection, and more. Pass the full error string from execute() or last_error().""" from build123d_mcp.tools.repair_hints import repair_hints as _repair_hints return _repair_hints(error_text) - A list of (pattern_list, hint_text) tuples defining all the error patterns and their associated fix hints. Used by the handler to match error messages.
_HINTS: list[tuple[list[str], str]] = [ ( [r"NoneType.*has no attribute", r"AttributeError.*None"], "Shape is None. If you used BuildPart context manager, access the result with " "`.part` (e.g. `result = bp.part`). In algebra mode, assign directly: " "`result = Box(10,10,10) - Cylinder(3,12)`.", ), ( [r"None context requested", r"No context.*requested"], "build123d algebra mode requires no context manager — create shapes directly " "and assign to `result` or call `show()`. Remove `with BuildPart()` wrappers " "if you're using operator-based construction.", ), ( [r"cq\.", r"Workplane", r"CadQuery"], "CadQuery syntax detected. build123d uses a different API: " "`Box(w,h,d)` not `cq.Workplane().box(w,h,d)`. " "Replace `.translate()` with `.move(Location((x,y,z)))`, " "`.rotate()` with `.rotate(Axis.Z, angle)`, " "and `.union()`/`.cut()` with `+`/`-` operators.", ), ( [r"TypeError.*Location", r"Location.*argument"], "Location syntax: pass a tuple — `Location((x, y, z))` not `Location(x, y, z)`. " "For combined translation + rotation: `Location((x,y,z), (rx,ry,rz))`.", ), ( [r"[Ff]illet.*edge", r"[Ee]dge.*fillet", r"ValueError.*edges.*fillet"], "Fillet edge selection: edges must be non-tangent and the radius must be smaller " "than the adjacent wall thickness. Select edges with " "`shape.edges().filter_by(Axis.Z)` or index them with `shape.edges()[0]`. " "Avoid `shape.edges()` (all edges) on complex shapes — pick specific ones.", ), ( [r"NameError.*\b(Box|Cylinder|Sphere|Cone|Torus|Extrude|BuildPart|" r"BuildSketch|Align|Axis|Location|Plane|Vector|Color|Compound|Shell|" r"Fillet|Chamfer|extrude|loft|sweep)\b"], "build123d name not in scope. Add `from build123d import *` at the top of " "the execute() call. If it was imported in a previous call, re-run that import " "or include it in this snippet.", ), ( [r"ImportError", r"SecurityError", r"not allowed.*import", r"import.*not allowed"], "Import blocked. Allowed modules: build123d, bd_warehouse, math, numpy, decimal, " "fractions, statistics, numbers, random, collections, itertools, functools, copy, " "operator, struct, typing, abc, dataclasses, enum, re, string, textwrap, pprint, " "json, base64, hashlib, io, warnings, contextlib. " "Remove os, sys, pathlib, subprocess, socket, and network imports. " "Start the server with --allow-all-imports to disable this check entirely.", ), ( [r"Constraint failed", r"AssertionError"], "Constraint failed — a dimension is physically impossible. Common causes: " "fillet radius larger than the adjacent face, hole diameter larger than " "the wall, or zero/negative dimensions. Check all numeric parameters.", ), ( [r"empty.*[Ss]hape", r"[Ss]hape.*empty", r"degenerate", r"no.*solid"], "Degenerate or empty shape after boolean operation. The cutter probably doesn't " "overlap the base, or the result has zero volume. Verify positions with " "measure(bounding_box) on both shapes before the boolean.", ), ( [r"ExecutionTimeout"], "Execution timed out. Likely causes: very high-resolution mesh " "(lower angular_deflection), deeply nested boolean operations, or an " "infinite loop. Simplify the geometry or break it into smaller steps.", ), ( [r"\.part\b"], "If you see an error referencing `.part`: in BuildPart context manager usage " "you must explicitly read `context.part` to get the Shape. " "In algebra mode (recommended) you don't need `.part` at all.", ), ]