---
name: fieldcad
description: Design and edit FieldCAD implicit-CAD documents (signed-distance-field modeling with a block graph) through the FieldCAD MCP server — creating primitives, booleans, shells, lattices, and measuring the result.
---

# FieldCAD

FieldCAD is an implicit-modeling (signed-distance-field) CAD system. A design
is a **graph** of nodes (blocks), not a feature tree: primitives and fields
flow through booleans, shells, lattices, and mesh operations to one `output`
node. You edit documents through the FieldCAD MCP tools; every applied edit
is validated, measured, and lands in the document's history, where the user
sees it — in the web editor, or in the desktop app's window (the desktop
server works on the window's one document, id `open`, and its edits stay
unsaved until the user saves).

## Workflow

1. `list_documents` → pick the document (or ask the user which one).
2. `read_graph` → understand what exists. Node ids are opaque and stable —
   never rename ids, only `name` labels.
3. `search_blocks` / `block_doc` → ground yourself before adding ANY node you
   haven't seen the spec for this conversation. Param names and shapes are
   not guessable; the spec is authoritative.
4. `apply_edits` with one atomic batch per coherent change. The response is an
   evaluation report: fix every error `diagnostics` reports (each has a stable
   `code`, a `location` JSON Pointer, and often a `suggestion`), then check
   `measurements` (volume, bbox_size, watertight, components) against the
   user's intent before declaring success.
5. `evaluate` re-measures without changing anything.

## Graph semantics (the parts models get wrong)

- A param value is exactly one of: a **reference** (bare string = another
  node's id, `"id.Port.Prop"` navigates record outputs), a **dimensioned
  value** `{ "value": 18, "unit": "mm" }` (`"unit": ""` = dimensionless), an
  **inline node** `{ "func": "multiply", "params": { … } }` (single-use), an
  **enum member** `{ "enum": "name" }`, free text `{ "text": "…" }`, a bool,
  or a vector `[x, y, z]`. An array of ids `["a", "b"]` is not a value of its
  own: it is only the `items` rows of a `list` node (next point).
- **Lists.** A list input (`union`'s and `intersect`'s `solids`, `cut`'s
  `solids` beside its `primary`, `export_step`'s `part`) takes ONE item — a
  list of one, `"solids": "cavity"` — or a `list` node: inline
  `{ "func": "list", "params": { "items": ["a", "b"] } }`, or its own node
  referenced by id. Never a bare array: `"solids": ["a", "b"]` is no list, and
  the block reports the input as missing. A list's kind is its rows' kind. A
  list wired where one item is taken runs the block once per item. There are
  no lists of lists: a row that is a list adds its items in place.
- Variables are ordinary `var` nodes referenced by id — there is no expression
  syntax; arithmetic is math nodes (`add`, `multiply`, …), inline or named.
- **Prefer inline subnodes** for single-use helpers (a boss region, a math
  step feeding one param) — they keep the notebook short — but nest at most
  **3 levels** deep; anything deeper (or used twice) becomes a named node.
- **Derive, don't copy**: when a value follows from another node's geometry,
  reference its record property (`"hole.StartPoint"`, `"hole.Radius"`) and
  derive offsets with math nodes (e.g. radius = inline `add` of
  `"hole.Radius"` + 1 mm) instead of re-typing the number — a copied number
  silently breaks the design when the source node is edited.
- Always leave the graph with a sensible `output` (set_output) — that is what
  the viewport shows and what exports.
- Prefer minimal edit deltas; use `replace_graph` only for from-scratch
  builds. Keep untouched nodes' ids identical so the user's review diff reads.
- **Notebook order**: the user reads sections top-to-bottom like a document.
  Place variables at the top (a Variables section, or `add_node` with
  `index: 0`), keep producers above their consumers, results last — never
  append a variable to the end.

## Common recipes

- **Hollow part**: solid → `shell` (`in`, `thickness`, direction enum).
- **Cavity**: `cut` { primary: body, solids: cutter } — several cutters
  go in a `list` node.
- **Gyroid infill**: `tpms_cell` (form `Wall`, surface `Gyroid`) wired into
  `lattice_in_solid` (volume, cell, grid `Box`, cell_size, thickness, trim on)
  fills a body with a gyroid cut flush at its surface; for a hollow part, `union`
  it with the body's `shell` (a blend radius also swells the shared outer skin,
  so `intersect` the result with the body).
- **Imported CAD**: `import_step` (asset; `import_parasolid` for a Parasolid file, where the
  kernel is installed) → `cad_body` (a reference such as
  `"<import id>.Bodies.0"`) → `cad_body_to_solid` (tolerance) — then it
  composes with any implicit op.
- **Meshing for export**: `solid_to_mesh` (tolerance in mm) →
  `remesh` / `triangles_to_quads` → `quad_mesh_to_cad_body` →
  `export_step` or `export_parasolid` (its `part` is a list: one CAD body, or a `list` node).

## Units and sanity

Everything is millimeters unless a unit says otherwise. After edits, compare
`measurements.bbox_size` with what the user asked for — a 30 mm part that
measures 300 mm means a unit slipped. `watertight: true` and
`components: 1` are the default expectation for a printable part; say so
explicitly when they don't hold.
