# Agent tools

Every tool Rack Maker's MCP server gives your agent - what it does, what it may change, its inputs and its answer.

Source: https://rack-maker.cbnsndwch.dev/docs/agents/tools

## How the tools work

**Drafts by name.** Tools name a draft as `<namespace>/<slug>@<rev>`, for example `ana/nas-box@1`: your handle, the device's id, and the revision number. `list_my_drafts` lists yours, and `create_draft` answers with the new one's name.

**Working copies.** Each command (`setBody`, `addPort`, and so on) changes a working copy of the draft that the server keeps for your agent between calls. They are the same commands the modeler runs in your browser.

**Saving.** `save` writes the working copy to the draft. While you have the draft open in a browser, you rarely need it: your agent's changes show there at once, and the browser saves them to the draft as it saves your own edits. With no browser open, the changes wait in the working copy until `save`. If the draft changed in the browser since the agent last looked, `save` refuses unless it is told to overwrite; `discard_changes` drops the agent's copy instead. See [Live sync](https://rack-maker.cbnsndwch.dev/docs/modeler/live-sync).

**All or nothing.** `apply_commands` runs several commands in one call, in order. If any one is refused, nothing changes.

**Access** says what a tool may do:

- **reads only**: it changes nothing;
- **makes changes**: it changes your working copy, the draft, or what is kept with it (a datasheet);
- **makes changes that can drop or replace earlier work**: it can't be undone from the agent (dropping a working copy, publishing);
- **reaches beyond Rack Maker**: it fetches a URL on the internet.

Your agent's client sees the same, as each tool's hints.

Every tool acts as you, with your permissions and limits. See [Using AI agents](https://rack-maker.cbnsndwch.dev/docs/agents).

The server offers 65 tools. This list is made from their definitions each time the site is built, so it always matches what your agent sees.

## Drafts

Find, start, save and drop the drafts the agent works on.

### `list_my_drafts`

**List my device drafts.** Your devices and parts in the modeler that are drafts or waiting for review, with their draft ids and links.

**Access:** reads only.

**Input:** none.

**Answers:** `{ drafts: [{ draft, name, status, updatedAt, workingCopy, url }] }`: each draft or submission, whether a working copy is kept for it, and its link in the modeler.

### `get_draft`

**Get a draft.** A draft's spec (your working copy, if you have one), each face's size and flat area in mm, the images traced on its faces (sizes and corners only), and the checklist of what is still missing before publishing.

**Access:** reads only.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** `{ draft, status, unsaved, changedElsewhere, url, kind, shape, envelope, mounting, spec, faces, traces, checklist }`: `faces` gives each face's size and flat area in mm, `traces` the images traced on it (sizes and corners only), `checklist` what is done and what is still missing, `openIssues` the user's corrections, and `detections` how many detections from the user's photos are pending, accepted and rejected, with a nudge.

### `create_draft`

**Create a draft.** Start a device: \{ name, maker? } for a new one (a plain 150 × 150 × 40 mm box until you set its body); a part: \{ name, kind: "part", category } (a 200 × 150 × 3 mm plate until you set its shape: a shelf, tray, bracket, panel…); or \{ from: "\<namespace>/\<slug>@\<rev>" } for a new revision of a published one (yours) or a copy of someone else's, crediting them.

**Access:** makes changes.

| Input                 | Type                                                                  | Description                                                                                                     |
| :-------------------- | :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `name` (optional)     | `string`                                                              | the name, e.g. "UCG-Ultra" or "10-inch 1U shelf"                                                                |
| `maker` (optional)    | `string`                                                              | the maker, e.g. "Ubiquiti"                                                                                      |
| `kind` (optional)     | `"device" \| "part"`                                                  | device (default) or part                                                                                        |
| `category` (optional) | `"shelf" \| "tray" \| "mount" \| "panel" \| "accessory" \| "printed"` | a part's catalog category: shelf, tray, mount, panel, accessory, printed (default shelf)                        |
| `from` (optional)     | `string`                                                              | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** `{ draft, url, next }`: the new draft's id, its link, and what to do next.

### `save`

**Save the draft.** Write your working copy to the draft, so it shows in the modeler in the browser and in the catalog as a draft. Refuses if the draft changed in the browser meanwhile, unless overwrite is true.

**Access:** makes changes.

| Input                  | Type      | Description                                                                                                     |
| :--------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                | `string`  | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `overwrite` (optional) | `boolean` | –                                                                                                               |

**Answers:** `{ draft, saved, url }`, or `saved: false` with a note when there was nothing to save.

### `discard_changes`

**Discard the working copy.** Drop your unsaved working copy (and the traced corners); the draft stays as last saved.

**Access:** makes changes that can drop or replace earlier work.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** `{ draft, discarded: true }`.

## Modeler commands

Each is one command of the modeler, the same the browser runs, applied to the draft's working copy. Every one takes the `draft` it works on, and answers the same way.

### `apply_commands`

**Apply several commands.** Run several modeler commands on a draft in one go, in order: \[\{ type: "setBody", w: 150, … }, \{ type: "addPort", port: \{ … } }]. All or nothing: if one is refused, nothing changes. Each command takes the same fields as its own tool. Detections too: \{ type: "acceptDetection", id: "d3" } (with label, at, size, rotate, itemId, note to correct it), \{ type: "rejectDetection", id, note }, \{ type: "updateDetection", id, label }.

**Access:** makes changes.

| Input      | Type       | Description                                                                                                     |
| :--------- | :--------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`    | `string`   | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `commands` | `object[]` | the commands, each \{ type, ...fields }                                                                         |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setIdentity`

Name the device, its maker, and the link to its product or spec-sheet page.

**Access:** makes changes.

| Input                  | Type     | Description                                                                                                     |
| :--------------------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `name` (optional)      | `string` | the device name, without the maker: "UCG-Ultra"                                                                 |
| `maker` (optional)     | `string` | the maker: "Ubiquiti"; "" clears it                                                                             |
| `datasheet` (optional) | `string` | a link to the maker's product or spec-sheet page; "" clears it                                                  |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setKind`

Say what you are modelling: a device (has ports, stands on a surface or in a rack) or a part that holds or mounts things (a shelf, tray, bracket, panel, plate, spacer), with its catalog category. A part is published as that category; its mounting (setMount) and decks (addDeck) are what make it useful in a rack.

**Access:** makes changes.

| Input                 | Type                                                                  | Description                                                                                                                                                               |
| :-------------------- | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `draft`               | `string`                                                              | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                           |
| `kind`                | `"device" \| "part"`                                                  | device (has ports; stands on a surface) or part (a shelf, bracket, tray, panel…)                                                                                          |
| `category` (optional) | `"shelf" \| "tray" \| "mount" \| "panel" \| "accessory" \| "printed"` | a part's catalog category: shelf, tray, mount, panel, accessory, printed (mount: brackets and ears; printed: 3D-printed parts; accessory: spacers, plates, anything else) |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setShape`

Choose the body's shape (default: box). Pick the simplest that matches the silhouette:
\- box: a rectangular case with rounded upright corners (radius) and bevelled or rounded top and bottom edges (most switches, routers, NUCs).
\- rounded: a rounded rectangle seen from above whose corners are big (up to half the width: a stadium) or whose bottom edge differs from the top (bottomEdge): a Mac mini, many mini PCs. Measure the corner radius from a photo from above (the corner's arc against a ruler), or estimate from the straight part of each side.
\- cylinder: round seen from above (diameter): hubs, smart speakers, round workstations; a puck is a short cylinder with big round edges (edgeSize). Its round side is four faces, one quarter each (front, right, rear, left); u runs around the side from the face's left edge (measure along the surface with a tape, or from the angle: u = angle in radians × radius). Things there are drawn on the surface, not cut; photos can't be traced on the round side.
\- tapered: a box whose top is smaller on some sides (top: \{ front, rear, left, right } insets, measured horizontally): a front bezel sloping back, a NAS with a slanted front. The slanted faces are flat: their v runs up the slope.
\- stack: the box plus blocks (a raised section on top, rack ears, a handle). Ports go on the box's faces only; the model's size grows to take in the blocks.
\- extrusion: a 2D outline (with holes) on the front, right or top face, run straight through: brackets (profile angle or zee), channels, plates (plane top), panels (plane front), shelves (an angle seen from the side). Give a profile (\{ kind, w, h, t }) or the outline's corners; the body's size becomes the outline's box and the length. Ports and zones go on the two outline faces only.

**Access:** makes changes.

| Input                   | Type                                                                      | Description                                                                                                                                                                                                                                                          |
| :---------------------- | :------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                 | `string`                                                                  | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                                                                                      |
| `shape`                 | `"box" \| "rounded" \| "cylinder" \| "tapered" \| "stack" \| "extrusion"` | box, rounded, cylinder, tapered, stack, extrusion                                                                                                                                                                                                                    |
| `bottomEdge` (optional) | `object \| null`                                                          | rounded, cylinder, tapered: the bottom edge when it isn't the top's (null: the same)                                                                                                                                                                                 |
| `bottomEdge.size`       | `number`                                                                  | how far the chamfer or round reaches in (mm)                                                                                                                                                                                                                         |
| `bottomEdge.style`      | `"bevel" \| "round"`                                                      | bevel (a chamfer) or round                                                                                                                                                                                                                                           |
| `top` (optional)        | `object`                                                                  | tapered: how much smaller the top is than the bottom on each side (mm, measured horizontally)                                                                                                                                                                        |
| `top.front` (optional)  | `number`                                                                  | front (mm)                                                                                                                                                                                                                                                           |
| `top.rear` (optional)   | `number`                                                                  | rear (mm)                                                                                                                                                                                                                                                            |
| `top.left` (optional)   | `number`                                                                  | left (mm)                                                                                                                                                                                                                                                            |
| `top.right` (optional)  | `number`                                                                  | right (mm)                                                                                                                                                                                                                                                           |
| `diameter` (optional)   | `number`                                                                  | cylinder: its diameter (sets w and d) (mm)                                                                                                                                                                                                                           |
| `blocks` (optional)     | `object[]`                                                                | stack: every block added to the box (replaces the list)                                                                                                                                                                                                              |
| `plane` (optional)      | `"front" \| "right" \| "top"`                                             | extrusion: the face the outline is drawn on, seen from outside; it runs straight through to the opposite face (front: a profile seen from the front, run to the rear; right: seen from the right side, run across the width; top: a plate seen from above, run down) |
| `outline` (optional)    | `([number, number])[]`                                                    | extrusion: the outline on that face                                                                                                                                                                                                                                  |
| `holes` (optional)      | `(([number, number])[])[]`                                                | extrusion: holes right through it (mounting holes, cut-outs)                                                                                                                                                                                                         |
| `profile` (optional)    | `object`                                                                  | extrusion: a standard profile instead of an outline                                                                                                                                                                                                                  |
| `profile.kind`          | `"plate" \| "angle" \| "channel" \| "zee"`                                | plate (a rectangle), angle (an L: legs along the bottom and up the left), channel (a U), zee (a Z)                                                                                                                                                                   |
| `profile.w`             | `number`                                                                  | its width on that face (mm)                                                                                                                                                                                                                                          |
| `profile.h`             | `number`                                                                  | its height on that face (mm)                                                                                                                                                                                                                                         |
| `profile.t`             | `number`                                                                  | the material thickness (mm)                                                                                                                                                                                                                                          |
| `length` (optional)     | `number`                                                                  | extrusion: how far it runs through (the thickness of a plate) (mm)                                                                                                                                                                                                   |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setMount`

How it mounts: on rack rails (standard 10in, 19in or deskpi-tl1, its height in U, and the flange: the depth from its front to where the ears sit against the rails, usually the faceplate thickness, 2–3 mm), or null to stand on a flat surface. Anything that mounts in a rack (a shelf, a blanking panel, a 1U device with ears) needs this.

**Access:** makes changes.

| Input                     | Type                               | Description                                                                                                     |
| :------------------------ | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                   | `string`                           | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `rails`                   | `object \| null`                   | the rails it mounts on; null: it stands on a flat surface (a shelf, a desk)                                     |
| `rails.standard`          | `"10in" \| "19in" \| "deskpi-tl1"` | 10in and 19in: EIA-310 rack rails; deskpi-tl1: the DeskPi RackMate TL1 rails                                    |
| `rails.units`             | `number`                           | its height on the rails in rack units (1U = 44.45 mm); 0.5 steps                                                |
| `rails.flange` (optional) | `number`                           | from the front of the model to the face that sits against the rails (the back of the ears); default 3 (mm)      |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `addDeck`

Offer a deck: a flat surface devices stand on (a shelf socket). at is its front-left corner in model coordinates (x from the left of the whole model, y from its front, z the height of the surface), size its usable \[width, depth]; capacity and maxLoadG are optional limits. A shelf or tray needs one to hold anything.

**Access:** makes changes.

| Input                      | Type                       | Description                                                                                                                                               |
| :------------------------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                    | `string`                   | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                           |
| `deck`                     | `object`                   | –                                                                                                                                                         |
| `deck.id` (optional)       | `string`                   | optional; made from the label ("deck")                                                                                                                    |
| `deck.label` (optional)    | `string`                   | what the device calls it: "LAN 1", "USB-C (PD in)"                                                                                                        |
| `deck.at`                  | `[number, number, number]` | its front-left corner \[x, y, z] mm in model coordinates (from the left-front-bottom of the whole model); z is the height of the surface devices stand on |
| `deck.size`                | `[number, number]`         | \[width, depth] mm of the flat area devices may use                                                                                                       |
| `deck.capacity` (optional) | `number`                   | how many devices it takes (default: any that fit)                                                                                                         |
| `deck.maxLoadG` (optional) | `number`                   | the most it carries, grams                                                                                                                                |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `updateDeck`

Change a deck's fields; null clears one.

**Access:** makes changes.

| Input                       | Type                               | Description                                                                                                                                               |
| :-------------------------- | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                     | `string`                           | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                           |
| `id`                        | `string`                           | the id of the item (as the add command or get\_draft reported it)                                                                                         |
| `patch`                     | `object`                           | –                                                                                                                                                         |
| `patch.label` (optional)    | `string \| null`                   | what the device calls it: "LAN 1", "USB-C (PD in)"                                                                                                        |
| `patch.at` (optional)       | `[number, number, number] \| null` | its front-left corner \[x, y, z] mm in model coordinates (from the left-front-bottom of the whole model); z is the height of the surface devices stand on |
| `patch.size` (optional)     | `[number, number] \| null`         | \[width, depth] mm of the flat area devices may use                                                                                                       |
| `patch.capacity` (optional) | `number \| null`                   | how many devices it takes (default: any that fit)                                                                                                         |
| `patch.maxLoadG` (optional) | `number \| null`                   | the most it carries, grams                                                                                                                                |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `removeDeck`

Remove a deck.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the id of the item (as the add command or get\_draft reported it)                                               |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setBody`

Set the body: w (left to right), d (front to rear), h (height without feet), radius of the vertical edges, and the bevel or round along the top and bottom edges. All mm. Measure the outside of the case at its widest. On a cylinder w and d are the diameter; on an extrusion the outline stretches with the size.

**Access:** makes changes.

| Input                  | Type                 | Description                                                                                                     |
| :--------------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                | `string`             | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `w` (optional)         | `number`             | width, left to right (mm)                                                                                       |
| `d` (optional)         | `number`             | depth, front to rear (mm)                                                                                       |
| `h` (optional)         | `number`             | height of the body, without feet (mm)                                                                           |
| `radius` (optional)    | `number`             | radius of the four vertical edges; 0: square (mm)                                                               |
| `edgeSize` (optional)  | `number`             | the bevel or round along the top and bottom edges; 0: sharp (mm)                                                |
| `edgeStyle` (optional) | `"bevel" \| "round"` | bevel (a chamfer) or round                                                                                      |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setFeet`

Set the feet as pegs (setBase \{ kind: "pegs" } by its older name; a base made of solids goes): height (0: none), diameter, inset from the edges for the default four, or their centres \[x, y] in model coordinates on the bottom, and round or square.

**Access:** makes changes.

| Input              | Type                           | Description                                                                                                                      |
| :----------------- | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| `draft`            | `string`                       | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                  |
| `h` (optional)     | `number`                       | foot height; 0: no feet (mm)                                                                                                     |
| `d` (optional)     | `number`                       | foot diameter (mm)                                                                                                               |
| `inset` (optional) | `number`                       | foot centres from the side and front/rear edges (the default four feet) (mm)                                                     |
| `at` (optional)    | `([number, number])[] \| null` | foot centres \[x, y] mm on the bottom in model coordinates (x from the left side, y from the front); null: four near the corners |
| `color` (optional) | `string`                       | a colour like "#2a2d33"                                                                                                          |
| `shape` (optional) | `"round" \| "square"`          | round (default) or square pegs (d is the side)                                                                                   |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setBase`

Set the base: what the device stands on. Pick the kind from a photo from the side at table level and one from below:
\- pegs: separate feet near the corners. Measure their height, diameter (or side) and inset, or give each centre (at). layout "corners-and-middle" for six.
\- plinth: one base set in from the body's edges (the body seems to float over a smaller, often darker block). Measure its footprint (size, or inset from the body's edges), height and outline (rect with radius, ellipse, stadium, path), and how it meets the body: step, chamfer, cove (a concave curve flaring out into the body: measure its radius, see the server's instructions on curves) or round (convex). tuck: the body's bottom edge curving in to meet it. pad: a rubber pad or ring on its bottom.
\- rails: parallel runners (NAS units, some switches): count, width, height, inset from the sides, endInset from the ends, along d (front to rear) or w.
\- none: a flat bottom. custom: keep the height and build the base from solids (addSolid, z \< 0).
A plinth and rails are made as solids (role "base"), which list\_solids shows and updateSolid edits; setBase again replaces them. h is always the height from the table to the body's bottom.

**Access:** makes changes.

| Input                       | Type                                                                        | Description                                                                                                                                                                                                                                 |
| :-------------------------- | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `draft`                     | `string`                                                                    | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                                                             |
| `kind`                      | `"pegs" \| "plinth" \| "rails" \| "none" \| "custom"`                       | pegs (round or square feet), plinth (one inset base under the body), rails (parallel runners), none (flat bottom), custom (keep the height, build the base from solids yourself)                                                            |
| `h` (optional)              | `number`                                                                    | the height under the body: from the table to the bottom of the body (mm)                                                                                                                                                                    |
| `d` (optional)              | `number`                                                                    | pegs: a peg's diameter (square: its side) (mm)                                                                                                                                                                                              |
| `inset` (optional)          | `number \| [number, number]`                                                | pegs: from each peg's centre to the sides and the front or rear (mm); plinth: from the body's edges to the plinth's, \[side, front/rear] or one number, instead of its size; rails: from the body's sides to the outer runners' outer edges |
| `at` (optional)             | `([number, number])[] \| null`                                              | pegs: their centres \[x, y] mm in model coordinates, any number; null: four near the corners                                                                                                                                                |
| `shape` (optional)          | `"round" \| "square"`                                                       | pegs: round (default) or square                                                                                                                                                                                                             |
| `layout` (optional)         | `"corners" \| "corners-and-middle"`                                         | pegs: four near the corners, or six (the middles of the sides too)                                                                                                                                                                          |
| `color` (optional)          | `string`                                                                    | a colour like "#2a2d33"                                                                                                                                                                                                                     |
| `outline` (optional)        | `"rect" \| "ellipse" \| "stadium" \| "path"`                                | plinth: its outline seen from below: rect (with radius), ellipse (a circle if square), stadium, path                                                                                                                                        |
| `size` (optional)           | `[number, number]`                                                          | plinth: its footprint \[width, depth] mm, where it stands on the table                                                                                                                                                                      |
| `centre` (optional)         | `[number, number]`                                                          | plinth: its centre \[x, y] mm in body coordinates (default: the body's centre)                                                                                                                                                              |
| `radius` (optional)         | `number`                                                                    | plinth (rect): its corners' radius (mm)                                                                                                                                                                                                     |
| `points` (optional)         | `number[][]`                                                                | plinth (path): its outline about its centre, \[x, y] as seen from above                                                                                                                                                                     |
| `transition` (optional)     | `"step" \| "chamfer" \| "cove" \| "round"`                                  | plinth: how it meets the body: step (a plain right angle), chamfer (a straight bevel), cove (a concave curve: the plinth flares out into the body), round (a convex round on the plinth's top edge)                                         |
| `transitionSize` (optional) | `number`                                                                    | plinth: the cove's or round's radius, the chamfer's leg (mm)                                                                                                                                                                                |
| `tuck` (optional)           | `number`                                                                    | plinth: the body's bottom edge rounded in to meet the plinth (a tucked underside), its radius; 0 takes it off (mm)                                                                                                                          |
| `pad` (optional)            | `object \| null`                                                            | plinth: a rubber pad or ring on its bottom; null removes it                                                                                                                                                                                 |
| `pad.inset`                 | `number`                                                                    | from the plinth's edge (mm)                                                                                                                                                                                                                 |
| `pad.thickness`             | `number`                                                                    | its thickness (part of h) (mm)                                                                                                                                                                                                              |
| `pad.ring` (optional)       | `number`                                                                    | a ring this wide instead of a full pad (mm)                                                                                                                                                                                                 |
| `pad.color` (optional)      | `string`                                                                    | a colour like "#2a2d33"                                                                                                                                                                                                                     |
| `finish` (optional)         | `object`                                                                    | default: the body's                                                                                                                                                                                                                         |
| `finish.color`              | `string`                                                                    | a colour like "#2a2d33"                                                                                                                                                                                                                     |
| `finish.preset`             | `"matte-plastic" \| "brushed-metal" \| "perforated-grille" \| "metal-foam"` | matte-plastic, brushed-metal, perforated-grille, metal-foam                                                                                                                                                                                 |
| `count` (optional)          | `number`                                                                    | rails: how many runners (2 to 8)                                                                                                                                                                                                            |
| `width` (optional)          | `number`                                                                    | rails: each runner's width (mm)                                                                                                                                                                                                             |
| `endInset` (optional)       | `number`                                                                    | rails: from the front and rear (or the sides, across) to their ends (mm)                                                                                                                                                                    |
| `along` (optional)          | `"d" \| "w"`                                                                | rails: d (front to rear, default) or w (side to side)                                                                                                                                                                                       |
| `round` (optional)          | `number`                                                                    | rails: their bottom edges rounded, radius (mm)                                                                                                                                                                                              |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `addSolid`

Add a solid: geometry the shapes and presets don't have (a boss, a knob, a raised panel, a handle, a dome, a hood), or a cut (op "cut": a groove, a recess, a vent, taken out of the flat face it runs into). Kinds: extrude (a profile run straight; draft; rims at either end: round, cove, chamfer, flare), box, cylinder, revolve (an \[r, a] profile turned round the axis), loft (from one profile to another). Profiles: rect, circle, ellipse, stadium, or a path whose \[u, v, bulge] points make arcs. Place it in body coordinates (at, axis), or onFace \{ face, at: \[u, v], depth? } like a port: the profile as seen on that face, running out of it (a cut: depth into it). A cut must be a straight extrude, box or cylinder square to one flat face, and not go through. Ports can't go where a solid covers a face or over a cut. Check it with render\_faces \{ section: "side" } and the faces.

**Access:** makes changes.

| Input                     | Type                                                          | Description                                                                                                                                                                                                                             |
| :------------------------ | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                   | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                                                         |
| `solid`                   | `object`                                                      | a solid: extrude, box, cylinder, revolve or loft                                                                                                                                                                                        |
| `onFace` (optional)       | `object`                                                      | place it on a face instead of giving at and axis: its profile is drawn as seen on that face (from outside), and it runs out of the face (a cut: `depth` into it). The easiest way to put a groove, a recess, a vent or a boss on a face |
| `onFace.face`             | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | front, rear, left, right, top or bottom                                                                                                                                                                                                 |
| `onFace.at`               | `[number, number]`                                            | where the profile's origin goes on that face: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)                                                                                                        |
| `onFace.depth` (optional) | `number`                                                      | a cut: how deep into the face (sets its length) (mm)                                                                                                                                                                                    |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `updateSolid`

Change a solid's fields (any but id; null clears one): move it (at), resize it, change its profile, rims, draft, finish or op.

**Access:** makes changes.

| Input                      | Type                                                                        | Description                                                                                                                                                                      |
| :------------------------- | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                    | `string`                                                                    | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                  |
| `id`                       | `string`                                                                    | the id of the item (as the add command or get\_draft reported it)                                                                                                                |
| `patch`                    | `object`                                                                    | –                                                                                                                                                                                |
| `patch.label` (optional)   | `string \| null`                                                            | –                                                                                                                                                                                |
| `patch.op` (optional)      | `"add" \| "cut" \| null`                                                    | –                                                                                                                                                                                |
| `patch.at` (optional)      | `[number, number, number] \| null`                                          | –                                                                                                                                                                                |
| `patch.axis` (optional)    | `"z" \| "-z" \| "y" \| "-y" \| "x" \| "-x" \| null`                         | –                                                                                                                                                                                |
| `patch.rotate` (optional)  | `number \| null`                                                            | –                                                                                                                                                                                |
| `patch.finish` (optional)  | `object \| null`                                                            | –                                                                                                                                                                                |
| `patch.finish.color`       | `string`                                                                    | a colour like "#2a2d33"                                                                                                                                                          |
| `patch.finish.preset`      | `"matte-plastic" \| "brushed-metal" \| "perforated-grille" \| "metal-foam"` | –                                                                                                                                                                                |
| `patch.profile` (optional) | `any \| null`                                                               | –                                                                                                                                                                                |
| `patch.h` (optional)       | `number \| null`                                                            | –                                                                                                                                                                                |
| `patch.draft` (optional)   | `number \| null`                                                            | –                                                                                                                                                                                |
| `patch.top` (optional)     | `object \| null`                                                            | a rim on one end                                                                                                                                                                 |
| `patch.top.kind`           | `"round" \| "cove" \| "chamfer" \| "flare"`                                 | round: the edge rounded off (convex); cove: a concave curve flaring OUT by size into what it meets (the curved transition under a body); chamfer: a bevel in; flare: a bevel out |
| `patch.top.size`           | `number`                                                                    | the radius (round, cove) or the bevel's leg (chamfer, flare) (mm)                                                                                                                |
| `patch.bottom` (optional)  | `object \| null`                                                            | a rim on one end                                                                                                                                                                 |
| `patch.bottom.kind`        | `"round" \| "cove" \| "chamfer" \| "flare"`                                 | round: the edge rounded off (convex); cove: a concave curve flaring OUT by size into what it meets (the curved transition under a body); chamfer: a bevel in; flare: a bevel out |
| `patch.bottom.size`        | `number`                                                                    | the radius (round, cove) or the bevel's leg (chamfer, flare) (mm)                                                                                                                |
| `patch.size` (optional)    | `[number, number, number] \| null`                                          | –                                                                                                                                                                                |
| `patch.radius` (optional)  | `number \| null`                                                            | –                                                                                                                                                                                |
| `patch.d` (optional)       | `number \| null`                                                            | –                                                                                                                                                                                |
| `patch.angle` (optional)   | `number \| null`                                                            | –                                                                                                                                                                                |
| `patch.from` (optional)    | `object \| null`                                                            | a 2D outline, centred on its origin (a path: where its points say): rect (with corner radius r), circle, ellipse, stadium, or path                                               |
| `patch.to` (optional)      | `object \| null`                                                            | a 2D outline, centred on its origin (a path: where its points say): rect (with corner radius r), circle, ellipse, stadium, or path                                               |
| `patch.shift` (optional)   | `[number, number] \| null`                                                  | –                                                                                                                                                                                |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `removeSolid`

Remove a solid.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the id of the item (as the add command or get\_draft reported it)                                               |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `addPort`

Add a port opening (or a row of identical ones). Positions are \[u, v] mm on the face (see the server instructions: seen from outside, from the bottom-left corner, centre of the item). The reply gives its id.

**Access:** makes changes.

| Input                    | Type                                                                                                                                                                                             | Description                                                                                                                                  |
| :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                  | `string`                                                                                                                                                                                         | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                              |
| `port`                   | `object`                                                                                                                                                                                         | –                                                                                                                                            |
| `port.id` (optional)     | `string`                                                                                                                                                                                         | optional; made from the kind: "usb-c", "usb-c-2"                                                                                             |
| `port.kind`              | `"usb-c" \| "thunderbolt" \| "usb-a" \| "rj45" \| "hdmi" \| "displayport" \| "sfp" \| "qsfp" \| "dc-barrel" \| "audio-3.5" \| "ac-c8" \| "ac-c14" \| "sd-card" \| "power-button" \| "lock-slot"` | one of: usb-c, thunderbolt, usb-a, rj45, hdmi, displayport, sfp, qsfp, dc-barrel, audio-3.5, ac-c8, ac-c14, sd-card, power-button, lock-slot |
| `port.label` (optional)  | `string`                                                                                                                                                                                         | what the device calls it: "LAN 1", "USB-C (PD in)"                                                                                           |
| `port.face`              | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"`                                                                                                                                    | front, rear, left, right, top or bottom                                                                                                      |
| `port.at`                | `[number, number]`                                                                                                                                                                               | the centre of the opening (the first one, for a row): \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)     |
| `port.rotate` (optional) | `0 \| 90 \| 180 \| 270`                                                                                                                                                                          | degrees counter-clockwise seen from outside: 90 stands a port on end                                                                         |
| `port.count` (optional)  | `number`                                                                                                                                                                                         | a row of this many identical ports                                                                                                           |
| `port.pitch` (optional)  | `number`                                                                                                                                                                                         | centre-to-centre distance in a row (mm)                                                                                                      |
| `port.along` (optional)  | `"u" \| "v"`                                                                                                                                                                                     | a row runs along u (to the right, default) or v (upwards)                                                                                    |
| `port.size` (optional)   | `[number, number]`                                                                                                                                                                               | \[width, height] mm of the opening, before rotation, when it isn't the standard size                                                         |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `movePort`

Move a port. Positions are \[u, v] mm on the face (see the server instructions: seen from outside, from the bottom-left corner, centre of the item).

**Access:** makes changes.

| Input   | Type               | Description                                                                                                     |
| :------ | :----------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string`           | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string`           | the id of the item (as the add command or get\_draft reported it)                                               |
| `at`    | `[number, number]` | the new centre: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)              |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `updatePort`

Change any of a port's fields (kind, label, face, at, rotate, count, pitch, along, size); null clears one.

**Access:** makes changes.

| Input                     | Type                                                                                                                                                                                                     | Description                                                                                                                                  |
| :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                   | `string`                                                                                                                                                                                                 | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                              |
| `id`                      | `string`                                                                                                                                                                                                 | the id of the item (as the add command or get\_draft reported it)                                                                            |
| `patch`                   | `object`                                                                                                                                                                                                 | –                                                                                                                                            |
| `patch.kind` (optional)   | `"usb-c" \| "thunderbolt" \| "usb-a" \| "rj45" \| "hdmi" \| "displayport" \| "sfp" \| "qsfp" \| "dc-barrel" \| "audio-3.5" \| "ac-c8" \| "ac-c14" \| "sd-card" \| "power-button" \| "lock-slot" \| null` | one of: usb-c, thunderbolt, usb-a, rj45, hdmi, displayport, sfp, qsfp, dc-barrel, audio-3.5, ac-c8, ac-c14, sd-card, power-button, lock-slot |
| `patch.label` (optional)  | `string \| null`                                                                                                                                                                                         | what the device calls it: "LAN 1", "USB-C (PD in)"                                                                                           |
| `patch.face` (optional)   | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom" \| null`                                                                                                                                    | front, rear, left, right, top or bottom                                                                                                      |
| `patch.at` (optional)     | `[number, number] \| null`                                                                                                                                                                               | the centre of the opening (the first one, for a row): \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)     |
| `patch.rotate` (optional) | `0 \| 90 \| 180 \| 270 \| null`                                                                                                                                                                          | degrees counter-clockwise seen from outside: 90 stands a port on end                                                                         |
| `patch.count` (optional)  | `number \| null`                                                                                                                                                                                         | a row of this many identical ports                                                                                                           |
| `patch.pitch` (optional)  | `number \| null`                                                                                                                                                                                         | centre-to-centre distance in a row (mm)                                                                                                      |
| `patch.along` (optional)  | `"u" \| "v" \| null`                                                                                                                                                                                     | a row runs along u (to the right, default) or v (upwards)                                                                                    |
| `patch.size` (optional)   | `[number, number] \| null`                                                                                                                                                                               | \[width, height] mm of the opening, before rotation, when it isn't the standard size                                                         |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `removePort`

Remove a port.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the id of the item (as the add command or get\_draft reported it)                                               |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `addZone`

Add a zone: an area of a face with its own finish (a vent grille, a metal-foam front), flush or sunk in. Ports inside it are cut into it. Positions are \[u, v] mm on the face (see the server instructions: seen from outside, from the bottom-left corner, centre of the item).

**Access:** makes changes.

| Input                    | Type                                                                        | Description                                                                                                     |
| :----------------------- | :-------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                  | `string`                                                                    | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `zone`                   | `object`                                                                    | –                                                                                                               |
| `zone.id` (optional)     | `string`                                                                    | optional; made from the label                                                                                   |
| `zone.label` (optional)  | `string`                                                                    | what the device calls it: "LAN 1", "USB-C (PD in)"                                                              |
| `zone.face`              | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"`               | front, rear, left, right, top or bottom                                                                         |
| `zone.at`                | `[number, number]`                                                          | the centre of the zone: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)      |
| `zone.size`              | `[number, number]`                                                          | \[width, height] mm                                                                                             |
| `zone.radius` (optional) | `number`                                                                    | corner radius (mm)                                                                                              |
| `zone.depth` (optional)  | `number`                                                                    | how far it is sunk into the face; 0 or absent: flush (mm)                                                       |
| `zone.finish`            | `object`                                                                    | –                                                                                                               |
| `zone.finish.color`      | `string`                                                                    | a colour like "#2a2d33"                                                                                         |
| `zone.finish.preset`     | `"matte-plastic" \| "brushed-metal" \| "perforated-grille" \| "metal-foam"` | matte-plastic, brushed-metal, perforated-grille, metal-foam                                                     |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `moveZone`

Move a zone. Positions are \[u, v] mm on the face (see the server instructions: seen from outside, from the bottom-left corner, centre of the item).

**Access:** makes changes.

| Input   | Type               | Description                                                                                                     |
| :------ | :----------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string`           | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string`           | the id of the item (as the add command or get\_draft reported it)                                               |
| `at`    | `[number, number]` | the new centre: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)              |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `updateZone`

Change a zone's fields; null clears one.

**Access:** makes changes.

| Input                     | Type                                                                        | Description                                                                                                     |
| :------------------------ | :-------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                   | `string`                                                                    | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`                      | `string`                                                                    | the id of the item (as the add command or get\_draft reported it)                                               |
| `patch`                   | `object`                                                                    | –                                                                                                               |
| `patch.label` (optional)  | `string \| null`                                                            | what the device calls it: "LAN 1", "USB-C (PD in)"                                                              |
| `patch.face` (optional)   | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom" \| null`       | front, rear, left, right, top or bottom                                                                         |
| `patch.at` (optional)     | `[number, number] \| null`                                                  | the centre of the zone: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)      |
| `patch.size` (optional)   | `[number, number] \| null`                                                  | \[width, height] mm                                                                                             |
| `patch.radius` (optional) | `number \| null`                                                            | corner radius (mm)                                                                                              |
| `patch.depth` (optional)  | `number \| null`                                                            | how far it is sunk into the face; 0 or absent: flush (mm)                                                       |
| `patch.finish` (optional) | `object \| null`                                                            | –                                                                                                               |
| `patch.finish.color`      | `string`                                                                    | a colour like "#2a2d33"                                                                                         |
| `patch.finish.preset`     | `"matte-plastic" \| "brushed-metal" \| "perforated-grille" \| "metal-foam"` | matte-plastic, brushed-metal, perforated-grille, metal-foam                                                     |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `removeZone`

Remove a zone.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the id of the item (as the add command or get\_draft reported it)                                               |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `addFeature`

Add an LED or a small button. Positions are \[u, v] mm on the face (see the server instructions: seen from outside, from the bottom-left corner, centre of the item).

**Access:** makes changes.

| Input                      | Type                                                          | Description                                                                                                     |
| :------------------------- | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                    | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `feature`                  | `object`                                                      | –                                                                                                               |
| `feature.id` (optional)    | `string`                                                      | optional; made from the kind                                                                                    |
| `feature.kind`             | `"led" \| "button"`                                           | led or button                                                                                                   |
| `feature.label` (optional) | `string`                                                      | what the device calls it: "LAN 1", "USB-C (PD in)"                                                              |
| `feature.face`             | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | front, rear, left, right, top or bottom                                                                         |
| `feature.at`               | `[number, number]`                                            | the centre: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)                  |
| `feature.d`                | `number`                                                      | diameter (mm)                                                                                                   |
| `feature.color`            | `string`                                                      | a colour like "#2a2d33"                                                                                         |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `moveFeature`

Move an LED or button. Positions are \[u, v] mm on the face (see the server instructions: seen from outside, from the bottom-left corner, centre of the item).

**Access:** makes changes.

| Input   | Type               | Description                                                                                                     |
| :------ | :----------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string`           | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string`           | the id of the item (as the add command or get\_draft reported it)                                               |
| `at`    | `[number, number]` | the new centre: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)              |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `updateFeature`

Change an LED's or button's fields; null clears one.

**Access:** makes changes.

| Input                    | Type                                                                  | Description                                                                                                     |
| :----------------------- | :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                  | `string`                                                              | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`                     | `string`                                                              | the id of the item (as the add command or get\_draft reported it)                                               |
| `patch`                  | `object`                                                              | –                                                                                                               |
| `patch.kind` (optional)  | `"led" \| "button" \| null`                                           | led or button                                                                                                   |
| `patch.label` (optional) | `string \| null`                                                      | what the device calls it: "LAN 1", "USB-C (PD in)"                                                              |
| `patch.face` (optional)  | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom" \| null` | front, rear, left, right, top or bottom                                                                         |
| `patch.at` (optional)    | `[number, number] \| null`                                            | the centre: \[u, v] mm from the face's bottom-left corner as seen from outside (u right, v up)                  |
| `patch.d` (optional)     | `number \| null`                                                      | diameter (mm)                                                                                                   |
| `patch.color` (optional) | `string \| null`                                                      | a colour like "#2a2d33"                                                                                         |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `removeFeature`

Remove an LED or button.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the id of the item (as the add command or get\_draft reported it)                                               |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setFinish`

Set the finish of the body or a zone ("zone:\<id>"): a colour, and a preset (matte-plastic, brushed-metal, perforated-grille, metal-foam). Sample the colour from a photo of the real device.

**Access:** makes changes.

| Input                      | Type                                                                        | Description                                                                                                     |
| :------------------------- | :-------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                    | `string`                                                                    | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `target`                   | `string`                                                                    | "body" or "zone:\<id>"                                                                                          |
| `finish`                   | `object`                                                                    | –                                                                                                               |
| `finish.color` (optional)  | `string`                                                                    | a colour like "#2a2d33"                                                                                         |
| `finish.preset` (optional) | `"matte-plastic" \| "brushed-metal" \| "perforated-grille" \| "metal-foam"` | matte-plastic, brushed-metal, perforated-grille, metal-foam                                                     |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setSource`

Say where a value comes from: measured (calipers, a ruler), datasheet, or estimated. Keys: body.w, body.d, body.h, body.radius, body.edge, body.shape, feet, mount, mass, port:\<id>, zone:\<id>, feature:\<id>.

**Access:** makes changes.

| Input    | Type                                               | Description                                                                                                                                                              |
| :------- | :------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`  | `string`                                           | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                          |
| `key`    | `string`                                           | which value: "body.w", "body.d", "body.h", "body.radius", "body.edge", "body.shape", "feet", "mount", "mass", "port:\<id>", "zone:\<id>", "feature:\<id>", "solid:\<id>" |
| `source` | `"measured" \| "datasheet" \| "estimated" \| null` | measured, datasheet or estimated; null: the default                                                                                                                      |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setMass`

Set the weight in grams (null clears it).

**Access:** makes changes.

| Input   | Type             | Description                                                                                                     |
| :------ | :--------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string`         | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `grams` | `number \| null` | the weight in grams; null clears it                                                                             |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setNote`

A note for whoever reopens these measurements: how they were taken, what is still a guess.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `note`  | `string` | how it was measured, what is still a guess (up to 2,000 characters)                                             |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setPhoto`

Tell the modeler you traced a face on an image of this size (pixels). The image stays with you; only its size is kept, so that corners and points on it can be turned into mm. source "drawing" for a datasheet page.

**Access:** makes changes.

| Input                     | Type                                                          | Description                                                                                                     |
| :------------------------ | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                   | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `face`                    | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | front, rear, left, right, top or bottom                                                                         |
| `photo`                   | `object \| null`                                              | the size of the image you traced (the image itself stays with you); null forgets it                             |
| `photo.w`                 | `number`                                                      | image width, px                                                                                                 |
| `photo.h`                 | `number`                                                      | image height, px                                                                                                |
| `photo.source` (optional) | `"photo" \| "drawing"`                                        | photo (default) or drawing (a datasheet page)                                                                   |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setCorners`

A photo's four face corners, in pixels (y down): bottom-left, bottom-right, top-right, top-left, as seen from outside. Then image\_to\_face turns points on the photo into mm on the face.

**Access:** makes changes.

| Input     | Type                                                                       | Description                                                                                                     |
| :-------- | :------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`   | `string`                                                                   | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `face`    | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"`              | front, rear, left, right, top or bottom                                                                         |
| `corners` | `[[number, number], [number, number], [number, number], [number, number]]` | a photo: the face's four corners on it, as seen from outside                                                    |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setScale`

A drawing: the two ends of a labelled dimension (pixels) and its length in mm.

**Access:** makes changes.

| Input   | Type                                                          | Description                                                                                                     |
| :------ | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `face`  | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | front, rear, left, right, top or bottom                                                                         |
| `a`     | `[number, number]`                                            | one end of a labelled dimension: \[x, y] image pixels, y down                                                   |
| `b`     | `[number, number]`                                            | its other end: \[x, y] image pixels, y down                                                                     |
| `mm`    | `number`                                                      | its length as labelled (mm)                                                                                     |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setOrigin`

A drawing: where the face's bottom-left corner is on it (pixels).

**Access:** makes changes.

| Input   | Type                                                          | Description                                                                                                     |
| :------ | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `face`  | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | front, rear, left, right, top or bottom                                                                         |
| `at`    | `[number, number]`                                            | a drawing: where the face's bottom-left corner is: \[x, y] image pixels, y down                                 |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

### `setSpec`

Replace the whole spec (as get\_draft returns it). Prefer the other commands; this one is for bulk edits.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `spec`  | `object` | a whole device spec (as get\_draft returns it)                                                                  |

**Answers:** `{ ok, draft, id?, ids?, unsaved: true, problemsWithThis?, problems }`: the id of what an add command created, and how many problems the spec has now (with the ones about what this command touched). A refusal says why, and nothing changes.

## Checking the work

See what the agent built, and turn pixels on a photo into millimetres.

### `image_to_face`

**Image pixels to face mm.** Turn points on the image traced on a face (pixels, y down) into positions on the face (\[u, v] mm), correcting the photo's perspective. Needs setPhoto and setCorners (a photo) or setScale and setOrigin (a drawing) for that face first.

**Access:** reads only.

| Input    | Type                                                          | Description                                                                                                     |
| :------- | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft`  | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `face`   | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | –                                                                                                               |
| `points` | `([number, number])[]`                                        | \[x, y] pixels on the image, e.g. the centre of each opening                                                    |

**Answers:** `{ face, points: [{ px, at, note? }] }`: each pixel's `[u, v]` mm on the face, noted if it falls off the face.

### `build_summary`

**Summarise the model.** What the modeler's builder makes of the spec, counted from it without building: the triangle count, the port anchors, the materials, about how big the GLB will be, every error and warning, and the checklist of what is still missing. Use render\_faces to see it.

**Access:** reads only.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** `{ draft, unsaved, changedElsewhere, shape, envelope, mounting, triangles, triangleTarget, anchors, materials, glbBytesEstimate, glbTarget, errors, warnings, checklist }`.

### `render_faces`

**Render the faces.** See what you built: each face drawn straight on as seen from outside (the same view as a straight-on photo of it), with its outline, the flat area (dashed), a 10 mm grid and ruler, zones in their colours, every opening with its port's name (rows once, with their count), LEDs and buttons, what can't be cut in red, the base and solids seen from that side (the base's height beside it), where solids cover the face (nothing can be cut there) and the cuts; overview: true adds an isometric wireframe of the whole device; section: "side" (or "front") adds a cut through the middle (sectionAt: elsewhere, mm) to check a profile, a plinth's curve or a groove's depth against a side photo. Returns PNG images (format: svg for SVG) and the same as a text listing. Compare each picture with the user's photos or drawings of that face: the position, size, spacing and name of every opening, the zones, the LEDs. Where they differ, correct the spec (movePort, updatePort, moveZone, …) and render again until they match.

**Access:** reads only.

| Input                  | Type                                                              | Description                                                                                                                                                                                                   |
| :--------------------- | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `draft`                | `string`                                                          | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                               |
| `faces` (optional)     | `("front" \| "rear" \| "left" \| "right" \| "top" \| "bottom")[]` | which faces; default: every face with something on it                                                                                                                                                         |
| `format` (optional)    | `"png" \| "svg"`                                                  | png (default: what most clients can show a model) or svg                                                                                                                                                      |
| `overview` (optional)  | `boolean`                                                         | also an isometric wireframe of the whole device                                                                                                                                                               |
| `section` (optional)   | `"side" \| "front"`                                               | also a section through the model: side (at the middle of the width, seen from the right, the front on the left) or front (at the middle of the depth, seen from the front): the base's profile, rims, grooves |
| `sectionAt` (optional) | `number`                                                          | where the section is instead: x (side) or y (front), model mm                                                                                                                                                 |

**Answers:** A text listing of each face (its size, flat area and everything on it), then one image per face drawn (PNG, or SVG with `format: "svg"`), and the overview when asked for. Faces that did not fit this call's pixel budget are listed as not drawn.

## Datasheets

Keep a device's datasheet PDF with it, privately.

### `attach_datasheet`

**Attach a datasheet.** Keep a copy of the device's datasheet (a PDF at a public http(s) URL, up to 15 MB) with the draft, as the modeler's Datasheets panel does: the server fetches it and stores it privately (only the user and admins can open it; the public page shows only its link). The device links to the URL if it had no datasheet link yet. Read the PDF yourself for the numbers. For a PDF on the user's computer, use create\_datasheet\_upload instead.

**Access:** makes changes; reaches beyond Rack Maker (fetches a URL).

| Input             | Type     | Description                                                                                                     |
| :---------------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`           | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `url`             | `string` | the PDF's public URL, e.g. "https\://example.com/specs/router.pdf"                                              |
| `name` (optional) | `string` | a file name to show (default: from the URL)                                                                     |

**Answers:** `{ draft, datasheet: { id, name, bytes, source }, note?, private, deviceLink, url }`: the kept copy, who can open it, and whether the device now links to the URL.

### `create_datasheet_upload`

**Upload a datasheet from this computer.** Get a link to upload the device's datasheet, a PDF on the user's computer (up to 15 MB), and keep a private copy with the draft, as the modeler's Datasheets panel does (only the user and admins can open it). Then upload the file yourself with a shell command: `curl -sS -T '/path/to/datasheet.pdf' '<url>'` (a PUT; `curl --data-binary @file.pdf <url>` also works). The link works once, for 10 minutes, for this draft only, and needs no sign-in header. This is the only way a file reaches Rack Maker from you, and only a datasheet PDF: never upload photos (read them yourself and send numbers). Read the PDF yourself for the numbers. For a PDF at a public URL, use attach\_datasheet.

**Access:** makes changes.

| Input      | Type     | Description                                                                                                     |
| :--------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`    | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `filename` | `string` | the PDF's file name, e.g. "router-datasheet.pdf" (it is kept under this name)                                   |

**Answers:** `{ draft, upload: { url, method, expiresAt, once, maxBytes }, curl, how, private, url }`: the one-time link, the command to upload the PDF with, and what the link answers.

## Detections

What the detector found on the user's photos, in their browser: candidates to accept, reject or relabel. Numbers and labels only; the photos never reach the server.

### `list_detections`

**List detections.** What the detector found on the user's photos of the device, run in their browser: candidate ports (by kind), vents, LEDs and buttons, each with a label, a confidence (0–1), its box on the face (\[u, v] centre and \[w, h] mm), the next best labels, and the user's notes. Default: the pending ones. Read them with the user's words and annotations; confirm uncertain ones with the user; then accept, reject or relabel each.

**Access:** reads only.

| Input               | Type                                                          | Description                                                                                                     |
| :------------------ | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft`             | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `face` (optional)   | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | only those on this face                                                                                         |
| `status` (optional) | `"pending" \| "accepted" \| "rejected" \| "all"`              | pending (default), accepted, rejected or all                                                                    |
| `min` (optional)    | `number`                                                      | only those at least this confident                                                                              |

**Answers:** `{ draft, detections: [{ id, face, label, what, confidence, at, size, rotate?, holes?, alternatives?, status, became?, foundAs?, note?, by? }], counts, how }`: each detection with its box in face mm, and how many are pending, accepted and rejected.

### `accept_detection`

**Accept a detection.** Make what a detection shows, with the ordinary commands, in your working copy: a port of its kind (turned on end, and with its own opening size when it differs from the standard one by more than 0.8 mm), a zone with a grille finish for a vent, or an LED or button. Correct it on the way: label, at, size, rotate. Say why in note when it helps the user. An "opening" of no known kind needs a label.

**Access:** makes changes.

| Input               | Type                                                                                                                                                                                                                                         | Description                                                                                                                                                                                                  |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`             | `string`                                                                                                                                                                                                                                     | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                              |
| `id`                | `string`                                                                                                                                                                                                                                     | the detection, as list\_detections names it: "d3"                                                                                                                                                            |
| `label` (optional)  | `"usb-c" \| "thunderbolt" \| "usb-a" \| "rj45" \| "hdmi" \| "displayport" \| "sfp" \| "qsfp" \| "dc-barrel" \| "audio-3.5" \| "ac-c8" \| "ac-c14" \| "sd-card" \| "power-button" \| "lock-slot" \| "vent" \| "led" \| "button" \| "opening"` | a port kind, or vent (a zone), led, button: usb-c, thunderbolt, usb-a, rj45, hdmi, displayport, sfp, qsfp, dc-barrel, audio-3.5, ac-c8, ac-c14, sd-card, power-button, lock-slot, vent, led, button, opening |
| `at` (optional)     | `[number, number]`                                                                                                                                                                                                                           | \[u, v] mm on the face: the centre                                                                                                                                                                           |
| `size` (optional)   | `[number, number]`                                                                                                                                                                                                                           | \[w, h] mm as it lies on the face                                                                                                                                                                            |
| `rotate` (optional) | `0 \| 90`                                                                                                                                                                                                                                    | 90: a port standing on end                                                                                                                                                                                   |
| `itemId` (optional) | `string`                                                                                                                                                                                                                                     | an id for the new port, zone or LED                                                                                                                                                                          |
| `note` (optional)   | `string`                                                                                                                                                                                                                                     | why, for the user                                                                                                                                                                                            |

**Answers:** `{ ok, draft, id, unsaved, problems, accepted, became }`, as a command answers: the id of the port, zone or LED it became.

### `reject_detection`

**Reject a detection.** Drop a detection that is nothing (a screw, a label, a shadow) or that you won't model, with a note saying why. The user sees it rejected, live.

**Access:** makes changes.

| Input             | Type     | Description                                                                                                     |
| :---------------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`           | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`              | `string` | the detection, as list\_detections names it: "d3"                                                               |
| `note` (optional) | `string` | why, for the user                                                                                               |

**Answers:** `{ draft, rejected, pending }`: how many are still pending.

### `update_detection`

**Relabel or adjust a detection.** Relabel a detection (the kind you or the user think it is), move or resize its box, or note something on it, without accepting it yet: e.g. when you want the user to confirm first. The label as found is kept (foundAs).

**Access:** makes changes.

| Input               | Type                                                                                                                                                                                                                                         | Description                                                                                                                                                                                                  |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`             | `string`                                                                                                                                                                                                                                     | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                              |
| `id`                | `string`                                                                                                                                                                                                                                     | the detection, as list\_detections names it: "d3"                                                                                                                                                            |
| `label` (optional)  | `"usb-c" \| "thunderbolt" \| "usb-a" \| "rj45" \| "hdmi" \| "displayport" \| "sfp" \| "qsfp" \| "dc-barrel" \| "audio-3.5" \| "ac-c8" \| "ac-c14" \| "sd-card" \| "power-button" \| "lock-slot" \| "vent" \| "led" \| "button" \| "opening"` | a port kind, or vent (a zone), led, button: usb-c, thunderbolt, usb-a, rj45, hdmi, displayport, sfp, qsfp, dc-barrel, audio-3.5, ac-c8, ac-c14, sd-card, power-button, lock-slot, vent, led, button, opening |
| `at` (optional)     | `[number, number]`                                                                                                                                                                                                                           | \[u, v] mm on the face: the centre                                                                                                                                                                           |
| `size` (optional)   | `[number, number]`                                                                                                                                                                                                                           | \[w, h] mm as it lies on the face                                                                                                                                                                            |
| `rotate` (optional) | `0 \| 90`                                                                                                                                                                                                                                    | 90: a port standing on end                                                                                                                                                                                   |
| `note` (optional)   | `string`                                                                                                                                                                                                                                     | a note for the user ("" removes it)                                                                                                                                                                          |

**Answers:** `{ draft, updated }`: the detection as it is now.

## Publishing

Submit to the catalog.

### `publish`

**Publish.** Save, then submit the device or part to the catalog under CC BY-SA 4.0. With contributor access it is published in your namespace with its model file (GLB), built here from its spec and credited to the user (if that build fails, it is published with its spec and the user's browser attaches the model file later; the answer says which). Without contributor access, the measurements go to an admin for review and it is drawn from its spec meanwhile. Ask the user before publishing.

**Access:** makes changes that can drop or replace earlier work.

| Input                  | Type      | Description                                                                                                     |
| :--------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                | `string`  | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `overwrite` (optional) | `boolean` | –                                                                                                               |

**Answers:** `{ draft, status, note?, model, asset?, url }`: `published` or `review`, and what became of the model file (built here, with its asset id, or attached by a browser later).

## More tools

Newer tools, not grouped yet.

### `list_annotations`

**List annotations.** The user's annotations on the draft's device: pins and boxes on its faces or model, and notes on its ports, zones and measurements. Kinds: issue and wrong ("this is wrong": corrections for you to make), measure (a number someone should measure), note. Each has its number (#3), where it is (face and \[u, v] mm), its text, replies, and how it was resolved. Default: the open ones. Read them before changing a device, fix every issue, and resolve each with a note on what you changed.

**Access:** reads only.

| Input               | Type                                                          | Description                                                                                                     |
| :------------------ | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft`             | `string`                                                      | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `status` (optional) | `"open" \| "resolved" \| "all"`                               | which ones (default: open)                                                                                      |
| `face` (optional)   | `"front" \| "rear" \| "left" \| "right" \| "top" \| "bottom"` | only those on this face                                                                                         |

**Answers:** JSON text; see the description.

### `add_annotation`

**Add an annotation.** Pin a note on the device for the user, shown at once in their modeler: most often kind "measure" to ask them to measure something you can't see well ("measure the distance from the left edge to the first USB port"), with an anchor where it is. Also "note" (something they should know) or "issue" (a problem you found but can't fix). Their answer comes back as a reply (list\_annotations).

**Access:** makes changes.

| Input             | Type                                                              | Description                                                                                                                                                                                                                                                                                                                                                                                                                          |
| :---------------- | :---------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`           | `string`                                                          | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                                                                                                                                                                                                                                                      |
| `kind` (optional) | `"note" \| "issue" \| "measure" \| "wrong"`                       | measure (default: ask the user to measure it), note, issue or wrong                                                                                                                                                                                                                                                                                                                                                                  |
| `text`            | `string`                                                          | what you ask or say, in one or two sentences                                                                                                                                                                                                                                                                                                                                                                                         |
| `anchor`          | `object`                                                          | Where it is. On a face (\[u, v] mm, as every position): \{ kind: "point", face, at: \[u, v] } a pin; \{ kind: "rect", face, at: \[u, v] (centre), size: \[w, h] } a box; \{ kind: "polygon", face, points: \[\[u, v], …] }. On the model: \{ kind: "surface", at: \[x, y, z] } (model mm). A thing: \{ kind: "item", ref: "port:\<id>" \| "zone:\<id>" \| "feature:\<id>" \| "measure:body.h" }. The whole model: \{ kind: "part" }. |
| `anchor.kind`     | `"point" \| "rect" \| "polygon" \| "surface" \| "item" \| "part"` | –                                                                                                                                                                                                                                                                                                                                                                                                                                    |

**Answers:** JSON text; see the description.

### `resolve_annotation`

**Resolve an annotation.** Mark an annotation resolved, with a note saying what you changed ("moved usb-c-1 3 mm left, to \[42, 12]") or why nothing needed to change. Do this after fixing each issue the user raised; the user sees it resolved, live.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the annotation: its number ("#3") or its id, as list\_annotations shows it                                      |
| `note`  | `string` | what you changed, with the numbers                                                                              |

**Answers:** JSON text; see the description.

### `reply_to_annotation`

**Reply to an annotation.** Answer an annotation without resolving it: ask the user what they meant, or for a measurement you need, when a correction doesn't make sense to you. The user sees the reply under it, live.

**Access:** makes changes.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`    | `string` | the annotation: its number ("#3") or its id, as list\_annotations shows it                                      |
| `text`  | `string` | your reply                                                                                                      |

**Answers:** JSON text; see the description.

### `list_solids`

**List the solids.** The draft's base in words and its solids (added and cut), each in words and as data (what updateSolid changes), where it reaches in the model, which faces it covers (nothing can be cut there) or which face it is cut into, and its problems.

**Access:** reads only.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** JSON text; see the description.

### `report_modeling_gap`

**Report a modelling gap.** Tell Rack Maker's admins about a shape of the device that the modeler can't represent, instead of hitting a dead end. Use it only after trying to compose the shape from the modeler's solids (extrude, revolve, loft, box, cylinder, with rim fillets/chamfers, added or cut) and presets. Describe the shape in words and mm, what you tried, and optionally the primitive that would do it. Text only: never include photos, links to them, or personal data. It is filed for the admins with the draft; tell the user it was filed.

**Access:** makes changes.

| Input                            | Type     | Description                                                                                                     |
| :------------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                          | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `description`                    | `string` | the shape, in words and mm, e.g. "a 30 mm dome, 120 mm across, on the top"                                      |
| `what_was_tried`                 | `string` | the solids, presets and commands you tried, and why they fell short                                             |
| `suggested_primitive` (optional) | `string` | the primitive or option that would do it, e.g. "sphere cap"                                                     |

**Answers:** JSON text; see the description.

### `define_form`

**Define the measurement form.** Build a measurement form for this device from what you know of it (its datasheet, photos, similar devices): the few numbers the user can measure with calipers or a ruler, grouped in sections in the order to measure them, each with how to measure it, a sensible range, your best value and its source (estimated until the user confirms). Then formulas turn them into the spec: body.w = case\_w, the u of a row of ports = margin + i\*pitch, a zone = case\_w - 2\*bezel. Values worked out from others are measurements with expr. Everything is optional but the sections. Clients that render MCP Apps show the form inline in the chat: the user sees every value with its unit, source and the formulas it feeds, a drawing of the face, and can edit values or flag one as wrong right there; everyone else gets the same as text. It is live in the user's open modeler too (the Measurements panel), where editing a value re-renders the 3D model at once. After defining it, show it to the user and ask them to check each value; apply what they correct with set\_measurements, and fix formulas they say are wrong with set\_formula.

**Access:** makes changes.

| Input                 | Type       | Description                                                                                                     |
| :-------------------- | :--------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`               | `string`   | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `title`               | `string`   | the form's title: "DGX Spark: case and rear ports"                                                              |
| `intro` (optional)    | `string`   | a line for the user: what they need (calipers, a ruler)                                                         |
| `sections`            | `object[]` | –                                                                                                               |
| `formulas` (optional) | `object[]` | the spec fields worked out from the measurements                                                                |

**Answers:** JSON text; see the description.

### `get_form`

**Get the measurement form.** The draft's measurement form: every measurement with its value, unit, source, how to measure it, the formulas it feeds and their results, errors, the user's open flags, and checks (a part bigger than its face, an opening over an edge, a value that looks 25.4× off). Clients that render MCP Apps show the form inline in the chat: the user sees every value with its unit, source and the formulas it feeds, a drawing of the face, and can edit values or flag one as wrong right there; everyone else gets the same as text. It is live in the user's open modeler too (the Measurements panel), where editing a value re-renders the 3D model at once.

**Access:** reads only.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** JSON text; see the description.

### `show_form`

**Show the measurement form.** Show the user the draft's measurement form to check and correct: inline in the chat where the client renders MCP Apps, as text otherwise (with the link to the modeler). Clients that render MCP Apps show the form inline in the chat: the user sees every value with its unit, source and the formulas it feeds, a drawing of the face, and can edit values or flag one as wrong right there; everyone else gets the same as text. It is live in the user's open modeler too (the Measurements panel), where editing a value re-renders the 3D model at once. Afterwards, read what they changed or flagged (get\_form, list\_flags).

**Access:** reads only.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** JSON text; see the description.

### `set_measurements`

**Set measurements.** Set several measurements at once, as the user gives them ("the case is 151.2, the bumper 1.5"): each \{ id, value, source }. Mark what the user measured as measured, what came from the datasheet as datasheet. Every formula is worked out again and the spec follows; the answer lists what changed, errors (a negative size, a value out of range) and checks. The inline form calls this too when the user edits a value.

**Access:** makes changes.

| Input    | Type       | Description                                                                                                     |
| :------- | :--------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`  | `string`   | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `values` | `object[]` | –                                                                                                               |

**Answers:** JSON text; see the description.

### `set_formula`

**Set a formula.** Drive a spec field by a formula over the measurements (expr), or drop its formula (expr: null; the field keeps its number). Several fields with one formula (paths) each get their index as i: the u of lan-1…lan-4 = margin + i\*pitch. Units are checked: a formula that gives mm² or a count where mm are due is refused, as are unknown names and cycles. Use it to fix a formula the user says is wrong.

**Access:** makes changes.

| Input              | Type             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                |
| :----------------- | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`            | `string`         | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1").                                                                                                                                                                                                                                                                                                                            |
| `path` (optional)  | `string`         | the spec field: body.w, body.d, body.h, body.radius, body.edge, body.bottom, body.top.front\|rear\|left\|right, feet.h\|d\|inset, massG, mount.units\|flange, port:\<id>.at.u\|v, port:\<id>.pitch\|count, port:\<id>.size.w\|h, zone:\<id>.at.u\|v, zone:\<id>.size.w\|h, zone:\<id>.radius\|depth, feature:\<id>.at.u\|v, feature:\<id>.d, block:\<id>.at.x\|y\|z, block:\<id>.size.w\|d\|h, deck:\<id>.at.x\|y\|z, deck:\<id>.size.w\|d |
| `paths` (optional) | `string[]`       | several fields, one formula: each gets its index as `i` (0, 1, 2…), e.g. the u of lan-1…lan-4 = margin + i\*pitch                                                                                                                                                                                                                                                                                                                          |
| `expr`             | `string \| null` | the formula; null drops it                                                                                                                                                                                                                                                                                                                                                                                                                 |

**Answers:** JSON text; see the description.

### `explain`

**Explain the formulas.** Every measurement and formula with its worked-out value, as text: what each spec field is computed from, which fields each measurement feeds, errors, open flags and checks. Use it to tell the user how their measurements are used, or to check your formulas.

**Access:** reads only.

| Input   | Type     | Description                                                                                                     |
| :------ | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft` | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |

**Answers:** JSON text; see the description.

### `list_flags`

**List the user's flags.** The values and formulas the user marked as wrong (in the modeler's Measurements panel or the inline form), each with their note: open ones first. Fix each (set\_measurements, set\_formula, or ask the user), then resolve\_flag it with what you did.

**Access:** reads only.

| Input               | Type                            | Description                                                                                                     |
| :------------------ | :------------------------------ | :-------------------------------------------------------------------------------------------------------------- |
| `draft`             | `string`                        | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `status` (optional) | `"open" \| "resolved" \| "all"` | default open                                                                                                    |

**Answers:** JSON text; see the description.

### `resolve_flag`

**Resolve a flag.** Close a flag the user raised, once you have fixed what it points at (or explained why it is right): resolution says what you did, and the user sees it next to the value.

**Access:** makes changes.

| Input                   | Type     | Description                                                                                                     |
| :---------------------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`                 | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `id`                    | `string` | the flag, "f1"                                                                                                  |
| `resolution` (optional) | `string` | what you changed, for the user                                                                                  |

**Answers:** JSON text; see the description.

### `flag_value`

**Flag a value as wrong.** The user marks a measurement ("measurement:\<id>") or a formula ("formula:\<path>") as wrong, with a note. The inline form calls this; list\_flags reads them.

**Access:** makes changes.

| Input    | Type     | Description                                                                                                     |
| :------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
| `draft`  | `string` | The draft: "\<namespace>/\<slug>@\<rev>", as list\_my\_drafts or create\_draft gave it (e.g. "ana/nas-box\@1"). |
| `target` | `string` | "measurement:\<id>" or "formula:\<path>"                                                                        |
| `note`   | `string` | what looks wrong                                                                                                |

**Answers:** JSON text; see the description.
