# Pascal 3D Editor (npm · @pascal-app/mcp)

Create, inspect, validate, and save editable 3D building scenes with Pascal's MCP server.

- Trust score: 79/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-24

## Components

- remote · `editor.pascal.app`: 40/100, [markdown](https://verifymcp.io/servers/app-pascal-editor/api-mcp.md), [page](https://verifymcp.io/servers/app-pascal-editor/api-mcp)
- npm · `@pascal-app/mcp`: 79/100 (this document), [markdown](https://verifymcp.io/servers/app-pascal-editor/pascal-app-mcp.md), [page](https://verifymcp.io/servers/app-pascal-editor/pascal-app-mcp)

## Channel facts

- Registry: `npm`
- Package: `@pascal-app/mcp`
- Version: `1.0.6`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-10-24.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 28 of 84 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to pascalorg/editor).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 1 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 70/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 26200 tokens (~284/item across 92 items; 86 tools + 6 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 92/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 73% of tool parameters carry a description.
  - Structured output schemas are declared (72% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 6 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 88 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the Pascal 3D Editor MCP server?

Pascal 3D Editor runs locally as an npm package, launched with npx -y @pascal-app/mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add app-pascal-editor -- npx -y @pascal-app/mcp
```

### Cursor

```json
{
  "mcpServers": {
    "app-pascal-editor": {
      "command": "npx",
      "args": [
        "-y",
        "@pascal-app/mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "app-pascal-editor": {
      "command": "npx",
      "args": [
        "-y",
        "@pascal-app/mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add app-pascal-editor -- npx -y @pascal-app/mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "app-pascal-editor": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@pascal-app/mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add app-pascal-editor --command npx --arg -y --arg @pascal-app/mcp
```

### Hermes

```yaml
mcp_servers:
  app-pascal-editor:
    command: "npx"
    args: ["-y", "@pascal-app/mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "app-pascal-editor": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "@pascal-app/mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add app-pascal-editor -t stdio -c npx -a -y @pascal-app/mcp
```

### Other

```json
{
  "mcpServers": {
    "app-pascal-editor": {
      "command": "npx",
      "args": [
        "-y",
        "@pascal-app/mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-10-24 (score 79, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-23 (score 64)

First indexed and scored.

## MCP tools (86)

### `get_scene` (~32 tokens)

Get scene

Returns the full scene graph: flat node dictionary, root node IDs, collections, and explicit installed plugin IDs when present.

Output parameters:

- `collections` (object)
- `installedPlugins` (array)
- `nodes` (object)
- `rootNodeIds` (array)

### `validate_design` (~203 tokens)

Validate design

Check a design (procedural item recipe) without changing the scene. Returns `valid`, coded `diagnostics` with paths (errors block placement, warnings flag floating or unbalanced parts), the parameter `sweep`, and `measurements` at the given parameters: bounds, per-part bounds and instances, true triangle counts, draw groups per slot, datum contact and connected components. It is the authority: some rules are not expressible in pascal://schema/design. Repair and re-validate until `valid` is true. Version 2 designs validate fully but carry a design_version_not_enabled warning: placement accepts version 1 only until the next release.

Input parameters:

- `design` (required): The design (procedural recipe, version 1 or 2) as an object, or as a JSON string for clients that cannot send recursive objects. Schema: pascal://schema/design.
- `parameters` (object): Parameter values to measure at, by parameter id. Defaults otherwise.

Output parameters:

- `design`
- `diagnostics` (array)
- `measurements`
- `sweep`
- `valid` (boolean)

### `place_design` (~498 tokens)

Place design

Create one instance of a design (procedural item recipe, object or JSON string) in the scene. The design must pass validate_design. Its mounting picks the host: floor designs go on a level (or a slab or zone of it) or on a named surface of a placed design; wall-side designs on a straight wall face; ceiling designs under a ceiling. Only creates: refusals carry a code (invalid_design with diagnostics, design_version_not_enabled for version 2 designs until the next release, design_too_large above 24 KiB, invalid_placement, node_exists, host_not_found, wrong_host, unknown_surface, does_not_fit) and change nothing.

Input parameters:

- `design` (required): The design (procedural recipe, version 1 or 2) as an object, or as a JSON string for clients that cannot send recursive objects. Schema: pascal://schema/design.
- `hostId` (string, required): Floor designs: a level, or a slab or zone on it, or a placed design with surfaceId. Wall-side designs: a straight wall. Ceiling designs: a ceiling.
- `id` (string): Optional new id ("procedural-item_…"). Refused if it exists: this tool only creates.
- `name` (string)
- `parameters` (object): Parameter values to measure at, by parameter id. Defaults otherwise.
- `position` (array, required): Metres. Floor: level-local [x, y, z], y above the floor. Wall-side: [along, height, offset] of the mounting reference from the wall start. Ceiling: plan [x, 0, z] of the reference. Surface: [x, 0, z]…
- `rotation` (number|string): Yaw. Not for wall-side designs; use side to face the other way. In radians, or text with a unit ("90°").
- `side` (string): Wall face for wall-side designs: front is the +normal side, normal (−Δz, Δx) of start→end. The wall's frontSide and backSide say which face is exterior (a facade light goes on that one).
- `slots` (object): Slot id to "#rrggbb" or a scene:/library: material reference.
- `surfaceId` (string): Named surface of the host design to rest on (see validate_design measurements.surfaces).

Output parameters:

- `designId` (string)
- `parentId` (string)
- `persistence` (object)
- `project` (string|null)
- `surfaceId` (string|null)
- `unsaved` (string)

### `measure_stair` (~74 tokens)

Measure stair

Inspect actual flight risers, walking-line going, slope, arrival, uniformity, headroom against floors, ceilings and stair bodies, and design-target diagnostics without changing measured geometry.

Input parameters:

- `available` (object): The space a new layout may take, to propose layouts that fit it.
- `stairId` (string, required)

Output parameters:

- `layouts`
- `measurements`

### `fit_stair` (~239 tokens)

Fit stair

Explicitly replace stair riser proportions with uniform risers from design targets. Optionally fit going by changing flight runs or arc sweep; preserves landing elevations. Optional straight, L or U layout replaces the flight chain with width-sized turning landings or quarter-turn winders. Winder going is measured along the selected walking line. One atomic edit.

Input parameters:

- `division` (string)
- `fitRun` (boolean)
- `innerGap` (number|string): In metres, or text with a unit ("6 ft"). Minimum 0 m.
- `landingDepth` (number|string): In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `layout` (string)
- `stairId` (string, required)
- `targets` (object)
- `turn` (string)
- `turningStrategy` (string)
- `walkingLineOffset` (number|string): In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `width` (number|string): In metres, or text with a unit ("6 ft"). Must be greater than 0 m.

Output parameters:

- `measurements`
- `persistence` (object)
- `project` (string|null)
- `stairId` (string)
- `unsaved` (string)

### `find_by_type` (~522 tokens)

Find by type

Find everything of one type in the scene, wherever it lives: nodes of that kind (column, door, window, wall, stair, imported-mesh, a plugin's kind…), items of that category, lights (any item that emits light), and typed parts inside scripted objects, windows and doors (a porch's columns, a window's grille). Narrow it to a level, a parent, a zone or an importer's source ids; without a type, every node the other filters match. A word that names nothing (no node kind, nor an item category or part type in this scene) is refused with the kinds the scene holds. Each result gives the node's id, name, kind, level and parent, with its metadata and provenance when it has them (one over 1 KB is named in omitted instead); get_node reads a node whole. Parts are read-only: they come back with their object's id, the part id and, for objects directly on a level, its bounds in level coordinates; edit them through their object. count says how many match; past limit (200 unless asked) the rest are left out of results, with truncated: narrow the call or raise limit.

Input parameters:

- `levelId` (string): Only this level. Default: every level.
- `limit` (integer): How many results to list at most. Default 200; count still says how many match.
- `parentId` (string): Only the nodes directly under this one: a wall's doors and windows, a roof's segments, a level's walls.
- `sourceId` (string): Only the nodes an importer recorded under this source id (provenance.refs[].id, or legacy metadata.sourceIds), exactly. Characters outside printable ASCII also match their percent-encoded form.
- `sourceIdPrefix` (string): Only the nodes with a source id starting with this.
- `type` (string): What to find, one word: a node kind (wall, slab, imported-mesh, fixture:bench), an item category, light, or a part type (column, beam, panel, railing, trim…). Omit it to find every node the other fil…
- `zoneId` (string): Only what stands in this zone: on the zone's level, its plan point inside the zone's outline and outside its holes (a wall's midpoint, a polygon's centroid, a door or window where its wall puts it, a…

### `list_levels` (~128 tokens)

List levels

List every level of every building in floor order: id, name, floorIndex, building (parentId), role (occupied storey, roof-only level or support level), child count, gradeStanding (at-grade, partly-below-grade or below-grade against the ground line and the terrain), isGroundLevel, and which one the person is viewing (isActive); groundLevels names, per building, the level at grade (levelId, whether the building names it, floorElevation, gradeElevation, floorOverGrade, seated). A roof-only level is the roof, not a storey.

Output parameters:

- `activeLevelId` (string|null)
- `activeSceneId` (string|null)
- `levelCount` (number)
- `levels` (array)
- `occupiedStoryCount` (number)
- `roofLevelIds` (array)
- `supportLevelCount` (number)

### `get_node` (~183 tokens)

Get node

Get the full data of any node by its id: its fields as stored, with what the scene decides for it beside them (a wall's or a ceiling's resolvedHeight, and heightIsExplicit, false when it follows its storey; a wall's resolvedThickness; a door's or window's resolvedOpening, its floor and what it hangs from; the displayName a level or a floor plate is shown by); its ancestorIds, its parent first (a door's wall, then the level, the building, the site); and the scene materials (scene:<id>) its finishes use. A container's node lists its children as stored; any that name no node are listed again in missingChildIds. A node its schema rejects comes back as stored, with nothing derived (verify_scene says what is wrong).

Input parameters:

- `id` (string, required): The node id.

Output parameters:

- `ancestorIds` (array)
- `materials` (object)
- `missingChildIds` (array)
- `node` (object)

### `get_level_summary` (~151 tokens)

Get level summary

Summarise one level so nothing on it goes unseen: its role and counts, then walls (length, heights, doors and windows), zones (name, area, size, floor_choices), slabs, ceilings, items (floor, wall and ceiling), floor openings, stairs, roofs, and anything else by type. Zone polygons: get_zones; a node's full data: get_node.

Input parameters:

- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.

Output parameters:

- `ceilings` (array)
- `counts` (object)
- `floorIndex` (number)
- `isOccupiedStory` (boolean)
- `isSupportLevel` (boolean)
- `items` (array)
- `levelId` (string)
- `levelName` (string)
- `metadataRole` (string|null)
- `openings` (array)
- `other` (array)
- `referenceLevelId` (string|null)
- `role` (string)
- `roofs` (array)
- `slabs` (array)
- `stairs` (array)
- `walls` (array)
- `zones` (array)

### `get_walls` (~190 tokens)

Get walls

Get the walls of a level: start and end in metres, length, stored and resolved height, thickness, and their doors and windows. wallFaceChains gives base-chain provenance for geometric faces a (left of start → end) and b (right): ref (authored face slot), legacy (inline spec), or default (declared fallback ref). These are not final rendered finishes: room intent, face paint regions, exterior assembly cladding and reference availability can change appearance. A chosen white or default-matching ref is legitimate; fallback alone is not a defect or a requirement to paint.

Input parameters:

- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.

Output parameters:

- `levelId` (string)
- `walls` (array)

### `get_zones` (~217 tokens)

Get zones

Get the zones (rooms) of a level: name, colour, polygon and holes in metres, area in m² (holes taken out), bounding size, and floor_choices: the floor plates the room can take (key, plateId, name, current; drawn and mezzanine flags). A room shares its floor plate with the rooms next to it unless detached. Persisted authored wall intent is wallMaterial (string, or null when absent) and wallOverrides (records of wallId, geometric face a or b, and finish; [] when absent). This is not a final rendered finish or visual verification: local overrides and face paint can take precedence, and absent room intent is not a defect or a requirement to paint.

Input parameters:

- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.

Output parameters:

- `levelId` (string)
- `zones` (array)

### `duplicate_level` (~203 tokens)

Duplicate level

Copy a level with everything on it, as the editor does: fresh ids, internal links kept, and units whose rooms are all on that level copied too; plan references, scans and spawn points stay behind. The copy goes above the original (or below it) and the floors past it move up one.

Input parameters:

- `levelId` (string): The level to copy, by an id list_levels returned. Default: the floor the person is viewing.
- `name` (string): Name of the copy. Default: none, so it reads by its floor like a new level (Floor 1, Ground floor…).
- `position` (string): Where the copy goes: above the original (default) or below it.
- `preset` (string): What to copy, as in the editor: everything (default); structure (walls, zones, slabs, ceilings, roofs, stairs, doors, windows) without materials; structure with its materials; or structure and furnit…

Output parameters:

- `achieved` (object)
- `copied` (object)
- `floorIndex` (number)
- `name` (string)
- `newLevelId` (string)
- `newNodeIds` (array)
- `newNodeIdsOmitted` (number)
- `persistence` (object)
- `project` (string|null)
- `shiftedLevelIds` (array)
- `skipped` (object)
- `unsaved` (string)

### `set_ground_level` (~392 tokens)

Set ground level

Make a level the building's ground floor: the one standing at grade, its floor 8 in over the ground (about 6 in on cast concrete foundations, 12 in on stone: the building's foundation construction seats it; the result names it). A building's levels stack on a ground line; the levels with a lower floor index are below grade (basements), the ones at or over it are not, and list_levels reads it as groundLevels (levelId, floorElevation, floorOverGrade, seated) and each level's gradeStanding (at-grade, partly-below-grade, below-grade) and isGroundLevel. This names the level on the building, seats the building so that level's floor datum, the top of the foundation where the wood framing starts, stands 0.203 m (8 in) over grade on a block foundation, 0.15 m on cast concrete (Europe), 0.3 m on stone (a historic style) (flat site: world Y 0.203; sculpted terrain: 0.203 m over the mean ground height under its floor), moving the building in height only — the plan stays where it is and every level keeps its place relative to the others — and renumbers the floor indices so it is 0 and the ones under it are -1, -2…. A level added below (add_level position below) stacks under the lowest level without moving the ground floor. A roof level or a support level cannot be the ground floor (not_a_storey). One undo step. Refusals: level_not_found, not_a_level, no_building, not_a_storey. Answering unchanged: true when the level is already the named ground floor, at that seat, at index 0.

Input parameters:

- `levelId` (string, required): The level to stand at grade, by an id list_levels returned.

Output parameters:

- `achieved` (object)
- `buildingId` (string)
- `floorElevation` (number)
- `gradeElevation` (number)
- `levelId` (string)
- `message` (string)
- `movedBuildingBy` (number)
- `ok` (boolean)
- `persistence` (object)
- `project` (string|null)
- `renumbered` (boolean)
- `unchanged` (boolean)
- `unsaved` (string)

### `verify_scene` (~425 tokens)

Verify scene

Optional whole-scene evidence: per-level content, roles (storey, roof-only, support) and the spaces its walls enclose, then every problem found, each with a type: empty levels, walls with no room or door, a space the walls enclose with no room over it (space_no_room) or with several and no wall between them (space_many_rooms), a room the walls do not close (room_not_enclosed) or with no name (room_unnamed), rooms with no floor or ceiling, storeys with no stair, roof levels misused, walls intersecting a covering slab or ending coplanar with its walking surface (wall_covering_slab_contact, with wallId and slabId), openings off their wall, stairs off their slab or blocked, furniture blocking a door or overlapping, a house past its lot line or in its required yards, every node field its schema rejects (schema_invalid, with its nodeId and path; valid is then false, and the level holding the node lists it as unread and is measured once it is fixed), a node kind no schema here knows, once per kind (schema_unknown_kind, info: a plugin kind), a hidden site (site_hidden). Static authored ceiling objects report transformed bounds extending above their ceiling attachment and bounds overlapping solid covering slab bands, with node IDs and measured metres; stored and derived slab holes are subtracted. Wall-mounted objects report a front oriented toward their host; static authored wall bounds report wall-body overlap and extension through the opposite face. With settled editor meshes, catalog wall bounds and large competing coplanar faces against the host or another item on the same wall are also measured within 1 mm. Hidden back-to-wall contact is not competing face evidence. These are observations, not instructions to change the scene or proof of exact mesh intersection. Bounds are measured in the owning building frame, including stacked level elevations. Unknown catalog raw bounds without meshes, animated models, unsupported attachment frames, unresolved scripted wall cuts, ro…

Output parameters:

- `activeLevelId` (string|null)
- `activeSceneId` (string|null)
- `authoredObjects` (array)
- `checkpoints` (array)
- `emptyLevelIds` (array)
- `hasIssues` (boolean)
- `issues` (array)
- `levelCount` (number)
- `levels` (array)
- `occupiedStoryCount` (number)
- `ok` (boolean)
- `roofLevelIds` (array)
- `since` (object)
- `supportLevelCount` (number)
- `valid` (boolean)

### `set_checkpoint` (~171 tokens)

Set a checkpoint

A named moment in your work (a roof, a floor, a batch of walls) that verify_scene({ since: name }) can compare against: it lists what was created, changed and deleted since, grouped by level and kind with a view each, and what you made that is gone. "start", the beginning of your work, is always one; a name used again replaces the first; a session keeps at most 8. A checkpoint is kept with your session, not in the project: it changes nothing in the scene, enters no undo, and does not restore anything. Refused: too_many_checkpoints (naming them), scene_too_large, name_required.

Input parameters:

- `name` (string, required): A short name for this moment, e.g. "walls done" or "before roof".

### `design_brief` (~1266 tokens)

Write the design brief

The design brief of the request, written once for the whole request (piece by piece when the request invites it): the request read axis by axis (action, size, pieces, openings, style, materials, source, dressing), each axis bounded by the person's own words or open with a design statement, a vision, and where the objects come from. It is recorded as the design record of the turn and answers with what it unlocks. It changes nothing in the scene.
Furnishings are placed or made after a brief: place_items, furnish_room, place_in_room (the catalog's) and add_object, place_design (the agent's own), also inside run_batch. Before one, search_assets and view_assets read the catalog and every other tool works as usual; rooms, walls, openings and finishes are not furnishings.
Bounded and open, in four readings. "a 4 by 5 m bedroom with a double bed, two nightstands, a wardrobe, one window, one door": action, size, pieces (as a minimum: those pieces must be there, more may come) and openings bounded; style, materials, source and dressing open. "add a lamp next to the sofa": action and dressing bounded by "add a lamp" (one object in a room that exists); style open, its design statement saying how this lamp is designed. "a bedroom with only a bed and a lamp": pieces exact and dressing bounded by "only a bed and a lamp"; style and materials open, the bed and the lamp designed, not defaulted. "Build a house": the action bounded only by "a house"; every other axis open, the dressing included.
source open: the pieces are made (add_object, place_design), view_assets shows the catalog's pieces for reference, and furnish_room, which places a whole stock set, stays closed. asIs keeps technical equipment only (sanitaryware, kitchen and laundry appliances, heating and similar services): each piece is named with a reason and was shown by view_assets earlier in the same turn (chat) or session (MCP); furniture is made, not kept as it is. source catalog, brand or existing: place_items, furnis…

Input parameters:

- `asIs` (array): With source open: technical equipment placed unchanged (place_items, place_in_room), each piece shown by view_assets first, each with its reason. Sanitaryware, kitchen and laundry appliances, heating…
- `axes` (object): The request read axis by axis: action, size, pieces, openings, style, materials, dressing, each bounded (the person's words as quote, the reading in a short line) or open (a design statement saying h…
- `beyond` (object, required): The dressing of the rooms beyond the pieces the request names, layer by layer. An open dressing is a call to dress the rooms fully; beyond is where that design is stated.
- `brand` (string): The range the request names, when source is brand ("IKEA").
- `source` (string, required): The source axis: where the objects come from. open: the request names no source. catalog: it asks for the catalog. brand: it names a range (give brand). existing: it names pieces that already exist.
- `sourceQuote` (string): Required with source catalog, brand or existing: the person's own words that name it (a brand, the catalog, what is already there), copied from the request ("IKEA furniture only").
- `vision` (string, required): A few lines: the design intent for what is open (character, forms, materials, how the room is meant to feel).

### `add_wall` (~601 tokens)

Add wall

Add a single wall between two points on a level, straight or, with curveOffset, an arc. Refused with a code: a level that is not a storey (a declared roof level), and a wall shorter than 1 cm. On a level below grade, a wall against the ground (on the outline of the walls of the level) is a foundation wall, built of what the building's region says: 8 in CMU with 2x4 pressure-treated furring laid flat inside (0.241 m) in the United States and where the scene does not say where the building stands, 25 cm cast concrete insulated and waterproofed outside (0.354 m) in Europe; the result names the region it read; a partition inside the outline is an ordinary wall; the result says which.

Input parameters:

- `curveOffset` (number|string): Bends the wall into an arc: the sagitta, from the middle of the straight line start→end to the middle of the arc. Positive bows the middle toward the back face (the −normal side, normal (−Δz, Δx) of…
- `end` (array, required): [x, z] end point in level coordinates (metres).
- `height` (number|string): Wall height (default: the storey's height). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `start` (array, required): [x, z] start point in level coordinates (metres).
- `thickness` (number|string): Wall thickness (default 0.1 m; a wall against the ground on a level below grade, given none, is a foundation wall built of what the building's region says: 8 in CMU with 2x4 pressure-treated furring…

### `add_fence` (~365 tokens)

Add fence

Add a fence along points on a level: one fence per side, a whole run or a lot's boundary (closed) in one call. Privacy (solid panels) by default; picket for a picket fence, slat for vertical slats, rail for open rails, horizontal for cladding boards, guard for a deck's barrier. Fences stand on the level, as the editor's fence tool puts them. Refused with a code: a side shorter than 10 cm (fence_too_short), a roof level (roof_level).

Input parameters:

- `baseStyle` (string): grounded (default: a kickboard on the ground), floating (the panel held off the ground on its posts) or raised (a bottom rail held above the ground, the posts to the ground: a deck guard).
- `closed` (boolean): Close the run back to the first point, as a lot's boundary.
- `height` (number|string): Fence height (default 1.8 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `materialPreset` (string): A library material (library:<id>), found with search_materials.
- `points` (array, required): [x, z] points in level coordinates (metres), the fence running through them in order.
- `style` (string): slat | rail | privacy | horizontal | guard | picket (default privacy).

### `add_corner_window` (~663 tokens)

Add corner window

A window that wraps the corner where two walls meet: one window on each wall, both running to the corner and joined, the glass fused at the corner (post none, a fixed window's default) or meeting at a thin post (post post). Any angle the walls make, a bay's 135° as well as a square or an acute corner. The corner is the point the two walls end at ([x, z], as get_walls gives their ends); width is how far the glass runs along each wall from the corner, the same on both unless widths names a wall. Each side is placed by add_window's rules (height, sillHeight, windowType, style the same on both sides; with no sillHeight, centred on the lower of the two walls). The walls are the level's (levelId, default the floor the person is viewing): stacked storeys share their corners' points. Refused with a code: no_corner where no two walls of the level end at the point, corner_ambiguous where more than two do (name two with wallIds), corner_angle for a corner nearly folded back or nearly straight, corner_not_fused for post none on a window type other than fixed, width_exceeds_wall. Returns windowIds and wallIds; the editor's window panel shows the pair under Corner.

Input parameters:

- `corner` (array, required): The point where the two walls meet, [x, z] in the level's plan (m).
- `height` (number|string): Window height (default 1.5 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `post` (string): none: the glass fused at the corner (a fixed window's default); post: the glass meets at a thin post (the default, and the only choice, for the other window types, whose frames stay).
- `sillHeight` (number|string): Height from the floor to the bottom of the window (default: centred vertically on the lower of the two walls, the same on both sides). In metres, or text with a unit ("6 ft"). Minimum 0 m.
- `style` (string): The panes' look on each side (rows and columns), the window panel's Style row.
- `wallIds` (array): The two walls, when more than two end at the corner.
- `width` (number|string): How far the glass runs along each wall from the corner (default 1.2 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `widths` (object): A width for a wall by its id, over width (e.g. { "wall_a": 1.6 }).
- `windowType` (string): How it opens (default fixed), the window panel's Type row; the fused corner is a fixed window's.

### `merge_windows` (~331 tokens)

Merge windows

Make two windows that exist one window, as the editor's Merge windows does: nothing is recreated. On two walls that meet at a corner, each window within 0.30 m of that corner, they become one corner window (the add_corner_window result): each is slid to the corner keeping its width, the glass fused (post none), and both take the first window's height, sill and type, so name first the window whose look you want; the corner is refused over 170° or under 10° (walls_not_meeting, corner_angle, window_not_at_corner, height_exceeds_wall). On one wall, two windows with at most 0.30 m between them and nothing between become one window spanning both outer edges, from the lower sill to the higher head, with the first window's type and style (windows_not_adjacent, opening_between). Refused with a code and a sentence that says why: window_not_found, not_a_window, same_window, window_not_on_wall, window_already_joined (unwrap it first), windows_already_joined, opening_overlap (sliding to the corner would land on another opening). Returns kind (corner or same_wall), the windows that stay (windowIds) and the one that went (removedIds). Use it for two windows already built that open a corner or one wide opening; add_corner_window builds a new pair, and verify_scene's corner_unjoined names two windows this joins.

Input parameters:

- `windowIds` (array, required): The two windows, the one whose height, sill and type the result keeps named first.

### `add_level` (~357 tokens)

Add level

Add an empty level to a building, as the editor does: above its top storey, under the building's roof level, which moves up one so the roof stays on top; or below its lowest level for a basement, which goes one storey under the lowest level and every existing level keeps its place, the ground floor included. On a scene with no building (a new or cleared scene), it starts the scene as the editor does: a site, a building and its ground level. A level that ends up below grade says in its result how a new wall on it is built (wallConstruction): a wall against the ground is a foundation wall, built of what the building's region says: 8 in CMU with 2x4 pressure-treated furring laid flat inside (0.241 m) in the United States and where the scene does not say where the building stands, 25 cm cast concrete insulated and waterproofed outside (0.354 m) in Europe; the result names the region it read, a partition is an ordinary wall. To copy a floor with its content, use duplicate_level.

Input parameters:

- `buildingId` (string): The building. Default: the building of the floor the person is viewing, else the scene's only building.
- `height` (number|string): Floor-to-floor storey height (default 2.5 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `name` (string): Name of the level, e.g. "Basement", "Attic".
- `position` (string): above (default): over the top storey, under the roof level; below: under the lowest, a basement.

### `create_stair` (~1321 tokens)

Create stair

Create a staircase rising from a level, as the editor's stair tool builds one: straight, L (a flight, a landing as deep as the stair is wide, a flight turned 90°) or U (a flight, a landing across, a flight back beside the first). It rises to the level above and owns the floor opening it cuts in every floor it passes; when no level stands above, a blank one is made for it. toLevelId null makes a flight to no level (porch steps, a deck, a raised platform): it rises height, cuts nothing and makes no level. Refused with a code: a flight to or from a declared roof level, and a toLevelId that is not above the level it rises from.

GEOMETRY (get this wrong and the stair pokes through a wall):
\- (x, z) is the middle of the bottom riser, where the flight starts. The first flight runs from there along the climb: at rotation 0 it covers x − width/2 .. x + width/2 and z .. z + its run.
\- rotation in degrees about Y: 0 climbs toward +Z, 90 → +X, 180 → −Z, 270 → −X. Point the climb into the room, away from the wall the bottom step sits against.
\- An L or U turns to the climber's left or right (turn). The result gives the whole footprint and the point the top step arrives at.
\- Keep 0.9 m clear at the foot and at the head of the flight, and the whole footprint inside one room on both levels (a hall is typical): read the room's outline first (get_zones).

OPENINGS: by default the stair owns the opening over its flights and keeps it as the stair moves (openingOffset widens it). An opening of another size, centre or turn, through a slab or ceiling you name, or through one of the two only, is cut as an opening the stair owns, as given.

Input parameters:

- `createDestinationSlabOpening` (boolean): Cut the floor it arrives through (default true).
- `createSourceCeilingOpening` (boolean): Cut the ceiling of the floor it leaves (default true).
- `destinationSlabId` (string): The slab it arrives through. Default: the floor of the level it arrives on.
- `height` (number|string): Vertical rise. Omit it unless asked for a specific rise: the flight then follows the storey's floor-to-floor height and keeps tracking it. In metres, or text with a unit ("6 ft"). Must be greater tha…
- `layout` (string): straight (default); l: a flight, a landing, a flight turned 90°; u: a flight, a landing across, a flight back beside the first.
- `length` (number|string): The flights' horizontal run along the climb, landings aside (default: from the stair's design targets, 0.28 m of going per riser, risers of at most 0.18 m). In metres, or text with a unit ("6 ft"). M…
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `lowerSteps` (integer): An L or U's risers in the first flight; the rest are in the last (default half).
- `materialPreset` (string): A library material for the flight (library:<id>); an unknown one is refused.
- `name` (string): Its name (default Staircase N).
- `openingCenter` (array): The opening centre (x, z). Default: the middle of the stair's footprint.
- `openingLength` (number|string): The opening along the climb (default the stair's footprint). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `openingOffset` (number|string): A margin round the opening on every side (default 0.08 m). In metres, or text with a unit ("6 ft"). Minimum 0 m.
- `openingRotation` (number|string): The opening's turn (default the stair's). In degrees, or text with a unit ("1.57rad").
- `openingWidth` (number|string): The opening across the climb (default the stair's footprint). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `railingMaterial` (string): A library material for the railings (library:<id>); an unknown one is refused.
- `railingMode` (string): Which sides have a railing, looking up the flight (default both).
- `railingStyle` (string): How the railings are built (default balusters): post-and-rail, cable (contemporary with a black railingMaterial), boards, glass or metal.
- `rotation` (number|string): The first flight's climb (default 0 = toward +Z; 90 = +X, 180 = −Z, 270 = −X). In degrees, or text with a unit ("1.57rad").
- `sourceCeilingId` (string): The ceiling it rises through. Default: the ceiling of the level it leaves.
- `steps` (integer): Number of risers in all. Omit it to derive ~18 cm risers from the rise; pass it when the run was planned from a step count.
- `toLevelId`: The level the flight arrives on. Default: the next level above, created when there is none. null: a flight to no level, rising height (default the storey's floor-to-floor height).
- `turn` (string): Which way an L or U turns, to the climber's left or right (default left).
- `width` (number|string): Side-to-side width, across the climb (default 1.0 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `x` (number, required): X of the middle of the bottom riser.
- `z` (number, required): Z of the middle of the bottom riser.

### `create_roof` (~884 tokens)

Create roof

Cap a storey with a roof over its footprint (its walls' outline, else its floor plates, else its rooms), swept in the building's own axes into rectangles, one roof segment each. Without a roofType: a gable for one rectangle, hips for a few wings, flat for a large block. The roof goes on the building's roof level above the top storey (the empty level directly above when it is the only one; made, named Roof, when there is none), and stands on the storey's wall tops, so exploded and solo level views lift it off the plan. Glazed construction supports gable, straight shed and barrel. Barrel depth is the span; barrelRise is at most half the span. glazing controls frame, glass, grids and panels. Paint glazing-frame, glazing-glass or glazing-solid. Call it once the storeys and their exterior walls stand. Refused with a code: storey_above (another storey stands above: a roof caps the top one; a lower wing's roof is drawn with the Roof tool), building_required (no level named on a site of several buildings: pass a level of the one to roof), roof_exists (the storey already has a roof: edit it, or delete_node it first when the person asked for a new one), no_rooms (no rooms or closed walls on any level of the building: pass width and depth to roof a rectangle of your own).

Input parameters:

- `barrelRise` (number|string): Barrel arch rise, at most half each segment span. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `barrelSegments` (integer)
- `center` (array): The rectangle's centre [x, z] in metres (default [0, 0]).
- `depth` (number|string): The rectangle's depth, along z. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `fasciaHeight` (number|string): A fascia band of that height round the roof's eaves and rakes; on a flat roof it reaches the roof's top, a coping over its edge. Absent (and no fasciaMaterialPreset): no band. In metres, or text with…
- `fasciaMaterialPreset` (string): The band's finish, a library material (a black fascia: library:preset-nearblack); the roof's wall finish when absent.
- `glazing` (object)
- `level` (string): Same as levelId.
- `levelId` (string): The storey to cover, by an id list_levels returned. Default: the top storey with rooms. A level without rooms stands for the highest storey of its building that has some.
- `materialPreset` (string): The roof finish, a library material.
- `name` (string): What the user would call it (default "Roof").
- `overhang` (number|string): Eave overhang: how far the eaves reach past the walls in plan (a segment stores it along its slope). Read it from the photo: a deep modern overhang is 1–2 m. Default 0.3. In metres, or text with a un…
- `pitch` (number|string): Slope of the roof planes (default 40°). In degrees, or rise over run ("6:12"). Range 0–85 deg.
- `roofSystem` (string)
- `roofType` (string): The shape of every segment. Default: chosen from the footprint.
- `wallHeight` (number|string): Knee walls under the eaves (default 0: the roof starts at the wall tops). In metres, or text with a unit ("6 ft"). Minimum 0 m.
- `wallThickness` (number|string): Roof wall thickness. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `width` (number|string): With depth: roof a rectangle of your own instead of the footprint (a wing, or walls drawn without rooms), along x. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.

### `move_roofs_to_roof_level` (~245 tokens)

Move roofs to the roof level

Move the roofs a storey holds on its own walls to the level the roof rule gives them, as a roof's panel does by hand (Move to the roof level): over the top storey, the building's roof level (made, named Roof, when there is none); under an upper storey, that storey. Each roof stands exactly where it stood, its segments, finishes and id kept: only the level holding it changes, so exploded and solo level views show the storey's plan with no roof over it. For a project built before roofs had their own level, when verify_scene reports roof_mixed_in_storey. Refused with a code: level_not_found, no_roof_on_walls (the storey holds no roof standing on its walls; a porch roof on its beam and a lower wing's roof on the storey above already sit where they belong). Returns the roofs moved (roofIds), the level they went to (roofLevelId) and the roof level made for them (createdRoofLevelId).

Input parameters:

- `levelId` (string, required): The storey whose roofs move, by an id list_levels returned.

### `place_items` (~397 tokens)

Place items

Place one or more catalog items (furniture, fixtures, plants, art, lights) in one call. Each assetId is a known catalog id, available from search_assets results or existing scene item data, never guessed. Positions are level (x, z) metres. An item stands on the floor of the level, or on the host it names (targetNodeId): a wall (art, a sconce: y is the height of its bottom above the floor, required; it goes on the side of the wall the point is on), a ceiling (a pendant, a downlight: it hangs flush under it), or an item standing on the floor (a lamp on a nightstand: it rests on its top; on an object built with add_object it rests on the real surface under the point, such as a porch landing, or a ceiling item hangs from the underside above it, such as a sloped vault, and the result names it in restingOn). A room, a slab or a level as the target means its floor. Each item is placed or refused on its own (asset_not_found; outside_rooms: an indoor item on the floor outside every room of a level that has rooms — garden and outdoor items may stand outside; height_required, item_too_tall, unsupported_host, host_not_found, host_not_on_level; blocks_door: a floor item standing where a door needs space, with a spot in its room that clears every door; too_large_for_room: a floor item its room cannot hold in any turn).

Input parameters:

- `items` (array, required): The items to place.
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.

### `delete_node` (~59 tokens)

Delete node

Delete a node with everything under it, as the editor's Delete does: a wall goes with its doors and windows, a level with everything on it. The floors above a deleted level keep their index.

Input parameters:

- `id` (string, required): The node id.

Output parameters:

- `achieved` (object)
- `deletedIds` (array)
- `persistence` (object)
- `project` (string|null)
- `unsaved` (string)

### `create_room` (~760 tokens)

Create room

Create a room on a level from its polygon: a wall per edge, an edge a wall already runs along reusing that wall (rooms share their boundary with their neighbour), and the room zone that names it. The floor plate and the ceiling are derived from the room — never author a slab or a ceiling for it. Declare the room's doors and windows in the same call by polygon edge (wallIndex) and t along it, no wall ids needed; each is placed with add_door's and add_window's rules, and one that cannot be is skipped and listed in skippedOpenings with its code. Returns zoneId, slabId, ceilingId, wallIds in edge order (null where no wall), doorIds, windowIds. outdoor: true draws a terrace instead: separators where no wall runs, no walls of its own and no ceiling. Where the walls already enclose a room the reconciler numbered, the call names that room instead of adding a second one; where they enclose a room with a name of its own (not a number the editor gave it), it is refused (room_named_by_person) unless rename is true, which you pass only when the person asked for the rename, so a name is never changed silently.

Input parameters:

- `color` (string): Hex colour of the room zone, e.g. "#22c55e"; omit it and the room gets a palette colour picked from its id.
- `doors` (array): The entry door and the doors to the rooms next to it.
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `name` (string, required): Room name, e.g. "Bedroom", "Kitchen".
- `outdoor` (boolean): true for an outdoor room (a terrace): closed with separators (the editor's Separator: a room boundary with no wall) where no wall runs, no walls of its own and no ceiling.
- `polygon` (array, required): The corners in order, as [x, z] in metres.
- `rename` (boolean): Only where the walls already enclose a room with a name of its own (not a number the editor gave it): true names that room with `name` instead of the refusal room_named_by_person. Pass it only when t…
- `wallHeight` (number|string): Omit to follow the storey and covering-slab underside. Equal to the stored storey height also follows that plane; other values are body heights above the wall's support, including deliberately taller…
- `wallThickness` (number|string): Thickness of the walls it builds. Omitted, a wall against the ground on a level below grade is a foundation wall built of what the building's region says: 8 in CMU with 2x4 pressure-treated furring l…
- `windows` (array): Windows, on exterior edges only: not on an edge shared with another room.

Output parameters:

- `achieved` (object)
- `areaSqMeters` (number)
- `ceilingId` (string|null)
- `doorIds` (array)
- `message` (string)
- `ok` (boolean)
- `persistence` (object)
- `project` (string|null)
- `reusedWalls` (number)
- `skippedOpenings` (array)
- `slabId` (string|null)
- `unsaved` (string)
- `wallIds` (array)
- `windowIds` (array)
- `zoneId` (string)

### `furnish_room` (~522 tokens)

Furnish room

Place the catalog's fixed furniture set for a room type: one set per type, not matched to a style. The bed, sofa, counters or toilet go against the wall facing the door, the rest along a side wall, each set off its wall by its own depth. Name the room by zoneId (its level and outline come with it), or give its polygon and level. The door wall is the edge a door of the room stands on, else doorWallIndex, else edge 0. Nothing lands in a door's clear zone or on another item: a piece that does not fit is nudged, else skipped, and skipped says why. Items stand on the room's level. The set per room type: bedroom: double-bed (a side of 3.2 m or more) or single-bed, two bedside-table, dresser (10 m² or more), closet (13 m² or more); kitchen: kitchen (back wall of 2.6 m or more) or kitchen-counter, stove (back wall of 3.5 m or more), fridge; bathroom: toilet, bathroom-sink, bathtub (6.5 m² or more) or shower-square; living: sofa, coffee-table, livingroom-chair, tv-stand; dining: dining-table, two dining-chair (four when both sides are 3 m or more); laundry: washing-machine, drying-rack; entry and hallway: coat-rack (both sides 1.4 m or more); storage: closet. The result lists each piece placed with its size and id, and each piece skipped with its reason. view_scene with interior { zoneId } shows the room from inside, at a person's height.

Input parameters:

- `doorWallIndex` (integer): The polygon edge (i → i + 1) to treat as the door wall: for a room with no door yet, or to override the one found.
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `polygon` (array): The room's corners as [x, z] in metres, when there is no zoneId.
- `roomType` (string, required): What the room is for.
- `zoneId` (string): The room: zoneId from create_room, or an id from get_zones.

Output parameters:

- `achieved` (object)
- `doorWallIndex` (number)
- `doorsDetected` (number)
- `itemIds` (array)
- `message` (string)
- `ok` (boolean)
- `persistence` (object)
- `placed` (number)
- `project` (string|null)
- `skipped` (array)
- `unsaved` (string)

### `paint` (~811 tokens)

Paint

Give walls, slabs, ceilings, roofs, columns, fences, doors, windows, stairs, blocks, cabinets, shelves, items and the other built parts a finish in one call, as the editor's Paint does. Give one finish: color (#rrggbb, painted with the nearest of the library's flat colours, said with how near); material, a library:<id> (find it with search_materials) or scene:<id> ref, or a native material {properties: {color, roughness, metalness}} for an exact finish, kept as a scene material (materialName names a new one) and reused when the scene holds an equal one; or erase: true, back to the surface's default. role picks the surface: a wall's face a or b, or exterior and interior (the faces turned out of its rooms or into them, else by its stored sides; a partition is interior on both; a wall whose sides are unknown, or with rooms along only part of a face, is refused: name its face, a or b), its trims aSkirting, bSkirting, aCrown, bCrown, aChairRail, bChairRail (skirting, crown or chairRail: that trim on both faces), a curtain wall's curtain-frame, curtain-glass, curtain-solid; a slab's surface, side, edge, riser, underside or foundation; a ceiling's surface; a roof's top (the covering), edge (the gable ends and the trim band), wall (the soffit and the deck edge, and the fascia and rake boards unless they have their own) or fascia (the fascia and rake boards, a fascia band); a glazed roof's glazing-frame, glazing-glass or glazing-solid; a column's shaft, base, capital or frame; a fence's posts, infill, base or rail; a door's panel, frame, glass or hardware; a window's frame or glass; a stair's treads, body or railing; a block's body; a cabinet's front, carcass, countertop, plinth, hardware or glass; another part's own slots, which a refusal names; an item's surfaces by the names its model gives them (its painted ones are in get_node's slots), which paint cannot check. Without a role: both faces of a wall, a slab's or a ceiling's surface, a roof's top, a glazed roof's frame a…

Input parameters:

- `color` (string): A colour, #rrggbb: painted with the nearest library colour.
- `erase` (boolean): Back to the surface's default.
- `material`: A library:<id> or scene:<id> ref, or a native material (properties color, roughness, metalness, and an existing texture) kept as a scene material.
- `materialName` (string): The name of a new native material; an equal one already in the scene keeps its own.
- `role` (string): The surface: see the description.
- `targets` (array, required): The nodes to paint.

Output parameters:

- `achieved` (object)
- `createdMaterial` (object)
- `finish` (string)
- `material` (object)
- `materialId` (string)
- `note` (string)
- `ok` (boolean)
- `painted` (array)
- `persistence` (object)
- `project` (string|null)
- `unsaved` (string)

### `place_in_room` (~662 tokens)

Place an item in a room

Put one floor-standing catalog item in a room where it fits, without working out a position. Wall/ceiling-attached assets need a host: place_items accepts targetNodeId and, for a wall, y for the bottom height. A wall-bound floor piece (fridge, counter, stove, sink, TV stand, dresser, wardrobe, sofa, bed, bedside table, toilet, bath, shower, bookcase, cabinet) stands against a wall, centred in the widest free stretch of that wall (clear of every door and every other item), its front toward the room; it is never put in the middle, and when no wall has room the answer says how wide the widest free stretch is. A free-standing piece (dining table, rug, coffee table, plant, armchair) takes the middle of the room, or goes with what it belongs with: a coffee table and an armchair face the sofa. A chair goes around its table, one call per chair, facing the table, and is refused when the room has no table. Explicit edge, align, offset or inset instead requests a wall free run for any floor piece. Name the room by its zoneId (create_room, get_zones), or by its polygon and level. The item is a catalog id from search_assets. To furnish a whole room by its type in one call, use furnish_room.

Input parameters:

- `align` (string): Desired wall alignment, clamped into a valid free run. Defaults to the widest run’s center.
- `allowDuplicate` (boolean): Place it even though the room already has this item. Off by default (a second toilet or stove is a mistake); chairs are always allowed.
- `assetId` (string, required): A known catalog id; search_assets can find it.
- `clearanceAreas` (array): Existing clearance rectangles, in world metres, to keep free before placing.
- `edge` (integer): Stand it against this polygon edge (i → i + 1) instead of the best one.
- `facing` (string): In by default. Out adds 180 degrees after any explicit rotationDeg.
- `inset` (number|string): Normal gap in metres between the rotated footprint and the wall centerline; defaults to 0.1 m. In metres, or text with a unit ("6 ft").
- `level` (string): Same as levelId.
- `levelId` (string): The level, by an id list_levels returned; an id made up from a name ("level_1", "level_ground") does not exist. Default: the floor the person is viewing, else the lowest floor.
- `offset` (number|string): Signed metres along the wall from the desired alignment; defaults to zero. In metres, or text with a unit ("6 ft").
- `polygon` (array): The room's corners as [x, z] in metres, when there is no zoneId.
- `rotationDeg` (number|string): World yaw in degrees, overriding the automatic inward, group or table-facing yaw. In degrees, or text with a unit ("1.57rad").
- `zoneId` (string): The room: zoneId from create_room, or an id from get_zones.

Output parameters:

- `achieved` (object)
- `against` (object)
- `around` (string)
- `facing` (string)
- `itemId` (string)
- `kind` (string)
- `message` (string)
- `ok` (boolean)
- `persistence` (object)
- `placed` (object)
- `project` (string|null)
- `unsaved` (string)

### `get_site` (~235 tokens)

Get site

Read the site a building stands on, in its level coordinates: the lot and its area, with the footprint a house usually covers on it (30 to 45%); the buildable area inside the setbacks with its centre, its area, each edge's frontage and depth, and its front edge (behind the street; while no street is recorded, behind the edge facing north, which the setbacks take as the front); the street side with the lot's frontage and depth on it; north; the ground (sloped with its fall, read flat, or not read); the address, parcel and zoning when known; whether the lot is only the new-project placeholder; and where the footprint sits now with its yards.

Input parameters:

- `building` (string): The building, by its id (list_levels' parentId). Default: the building of the floor the person is viewing, else the scene's only building.
- `site` (string): The site, by its id (get_site's siteId), when the scene has several: a lot no building stands on yet reads in site coordinates. Default: the building's site.

### `place_building` (~328 tokens)

Place building

Stand a building on its site before or after its walls go up. It moves only the building node, so walls, levels and roofs keep their coordinates and move with it. The footprint (outline, the plan you are about to draw, in the coordinates you will draw it; else the walls standing) lands centred in the buildable area, or at the yards a site plan or the person gives. A building with walls keeps its rotation unless rotationDeg is given (get_site's squareRotationDeg squares it to the street); one with none yet is turned square to the street. Fences, paths and plants on its levels move with it.

Input parameters:

- `building` (string): The building, by its id (list_levels' parentId). Default: the building of the floor the person is viewing, else the scene's only building.
- `outline` (array): The footprint's corners in order, as [x, z] in metres, in the level coordinates the walls are (or will be) drawn in. Default: the walls standing.
- `rotationDeg` (number|string): The building's rotation about the vertical. Default: the rotation it has, or square to the street while it has no walls. In degrees, or text with a unit ("1.57rad").
- `yards` (object): Where the house stands instead of centred: at most one of front and rear, and one of left and right, from a site plan or the person. An axis not given stays centred in the buildable area. Needs the s…

Output parameters:

- `achieved` (object)
- `buildingId` (string)
- `fit` (string)
- `message` (string)
- `moved` (boolean)
- `persistence` (object)
- `project` (string|null)
- `unsaved` (string)

### `search_assets` (~173 tokens)

Search assets

Search the item library (furniture, fixtures, lights, plants, outdoor) by keyword, with one or several queries in a call. The catalog is a limited set that varies by server: what a search returns is what exists. Results provide valid asset ids and placement facts when these are not already known. A query matches the items whose name, id, category or tags hold every one of its words. Returns one group per query, each item with its id, name, category, dimensions [width, height, depth] in metres and attachTo (null on the floor, else 'wall', 'wall-side' or 'ceiling'). Related lookups (sofa, coffee table, tv stand) can share a call.

Input parameters:

- `queries` (array, required): One or more searches to run in a single call.

### `view_assets` (~56 tokens)

View catalog items

Pictures of catalog items by id, each with its size and the names of its paintable surfaces (paint takes one as role).

Input parameters:

- `ids` (array, required): One to six catalog item ids, as search_assets or furnish_room returned them.

### `search_materials` (~315 tokens)

Search materials

Search or browse the material library (the finishes paint gives), several queries in one call: find a library material's ref here rather than guessing it. The library is Pascal's materials and, in the editor, the ones it holds besides (the community's, yours, your workspace's). A query's words match the words of a material's id, name and kind, or a piece of its id (plank48, cco), building words as the library writes them (timber is wood, weatherboard is siding, render is stucco), colour words by its shade (dark, light, white, grey) and surface words (roof, wall, floor, ceiling) by what it suits; a word of one or two letters, or one as common as 'the', searches nothing. Without a query, a browse lists by name every material of its kind, or that suits its surface (flat colours suit every one), or the whole library. Returns one group per query: each material's ref (library:<id>), name, kind, the surfaces it suits and its colour, best first (a browse by name), 12 at a time for a search and 150 for a browse; the words no material answers (missing), so a finish the library lacks is said, not guessed; and on a long list how many more there are (more) and the offset that lists them (next).

Input parameters:

- `queries` (array, required): One or more searches or browses to run in one call.

### `cut_floor_opening` (~141 tokens)

Cut floor or ceiling opening

Create a persistent opening on a room or level from a polygon or rectangle. A floor cut also opens the automatic ceiling directly below; a ceiling cut can also open the floor above. Use cutsAdjacent:false for a one-surface cut. A mezzanine room cuts only its own plate. levelIds repeats the opening as a shaft on several levels.

Input parameters:

- `cutsAdjacent` (boolean)
- `cutsPrimary` (boolean)
- `drawnOn` (string)
- `levelId` (string)
- `levelIds` (array)
- `polygon` (array)
- `rect` (object)
- `zoneId` (string)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `remove_floor_opening` (~32 tokens)

Remove floor opening

Remove an authored floor or ceiling opening and restore the derived surfaces it cut.

Input parameters:

- `id` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `set_floor_foundation` (~243 tokens)

Set floor and foundation

Supply slabId or slabIds; grouped plates receive the same floor-top displacement, while edge slots apply only to the first plate. A ground-contact floor has two inputs: thickness (slab, min 0.01 m, grows upward from the foundation) and foundationHeight (0 = on the ground, no foundation; > 0 = raised on a solid foundation; foundation.type "solid" alone raises it 0.3 m, "none" puts it on the ground). The floor top is derived and never floats: grade + foundationHeight + thickness; everything the floor carries (walls, openings, items, rooms, storeys above) moves with the top. Legacy floorHeight (a target top) maps to foundationHeight = top - grade - thickness, never below 0; null = on the ground. On upper plates the underside stays on the walls below: thickness (or floorHeight as a target top) changes the top; only ground-contact plates accept a foundation. Explicit room floors translate with the floor to preserve steps.

Input parameters:

- `patch` (object, required)
- `slabId` (string)
- `slabIds` (array)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `set_room_floor_construction` (~157 tokens)

Set room floor construction

Edit the single floor volume associated with a room, with the same patch as set_floor_foundation. On a ground-contact floor: thickness (slab on the foundation, grows upward, min 0.01 m) and foundationHeight (0 = on the ground; > 0 = raised on a solid foundation); the top is derived as grade + foundationHeight + thickness and carries everything on the floor. A drawn slab takes thickness, finishes and a numeric floorHeight (its top) only. Supply slabId when the room spans multiple construction domains; an absorbed legacy slab is edited through its derived base plate.

Input parameters:

- `patch` (object, required)
- `slabId` (string)
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `rebase_floor_reference` (~72 tokens)

Rebase floor reference

Change or clear one or more footprint reference datums in one atomic edit, compensating supported storey offsets so existing world geometry stays in place. Supply slabIds together for a shared storey.

Input parameters:

- `referenceFloorElevation` (required)
- `slabId` (string)
- `slabIds` (array)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `create_mezzanine` (~160 tokens)

Create mezzanine

Add an open-below room inside a host reference polygon, with a separate thin plate and railings on open edges. Minimum area 1 m²; elevation defaults to half the storey snapped to 0.05 m, thickness to 0.2 m. Elevation must exceed thickness and stay at least 0.3 m below the resolved footprint plane; overlapping mezzanines are refused. The host floor and walls stay intact.

Input parameters:

- `elevation` (number|string): In metres, or text with a unit ("6 ft").
- `hostZoneId` (string, required)
- `polygon` (array, required)
- `thickness` (number|string): In metres, or text with a unit ("6 ft"). Minimum 0.02 m.

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `move_zone` (~103 tokens)

Move or rotate room

Move room intent and contents. Shared walls and openings stay with neighbours; the room takes plain copies. Rotation is radians about Y, before translation, about the room centroid unless a pivot is supplied. Crossings split walls and overlaps re-derive rooms. Force can reposition obstructing openings when they fit; otherwise the edit is refused.

Input parameters:

- `force` (boolean)
- `rotate` (object)
- `translate` (array)
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `duplicate_zone` (~100 tokens)

Duplicate room

Copy room intent and contents with fresh IDs; shared walls are copied without openings. Rotation is radians about Y before translation. Collinear destination walls are merged, preserving their IDs and hosted children. Crossings split walls and rooms re-derive; overlap is allowed. Force can reposition obstructing openings only when they fit.

Input parameters:

- `force` (boolean)
- `rotate` (object)
- `translate` (array, required)
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `rotate_zone` (~117 tokens)

Rotate room a quarter turn

Rotate a room by quarterTurns 1 or -1 about its centroid, then align its first reference vertex to gridStep (default 0.5 m). Crossings split walls and collinear overlaps merge. Force can reposition obstructing openings only when they fit.

Input parameters:

- `force` (boolean)
- `gridStep` (number|string): In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `quarterTurns` (integer, required): 1 or -1.
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `lock_outside_faces` (~68 tokens)

Keep outside dimensions

Keep the building's outer size: put exterior wall faces on their fixed reference lines, moving wall bodies inward so walls thicken inward and later thickness edits preserve outer dimensions. Supply either levelId or zoneIds.

Input parameters:

- `levelId` (string)
- `zoneIds` (array)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `set_zone_intent` (~355 tokens)

Set room intent

Update room name, floor finish/elevation/regions, floor.footprint ("new" mints a unique floor key tied to this creator room, an existing key joins that floor on this level, null returns to the shared floor). Touching rooms with the same key share a base plate; disconnected pieces share construction settings. Conversion preserves world geometry in one edit and refuses when infeasible. get_zones exposes floor_choices. A keyed floor uses "<creator room name> floor" while the creator remains on that key; otherwise it uses the largest named room. Existing keys such as "own" without a creator use that fallback. A user-given plate name wins; the shared floor stays "Shared floor". Keyed rooms can have raised or sunken floor.elevation, stored as a level-local walking top. Slab thickness, foundation height and edge finish use set_room_floor_construction, updating every piece of the key. Update ceiling paint regions (ceiling.regions: [x, z] polygons with a finish, later wins), step finishes (floorStepFinish for every step of the room; floorStepOverrides: [{ key, step?, finish }] per doorway, key = the door id the step sits under or the lower room id), wall material and construction opt-outs. daylight { artificialLight, mechanicalVentilation } declares a habitable room lit and aired by artificial means (the IRC R303.1 exceptions to daylight; both true), stored in the room's metadata; a bedroom, living room or kitchen needs a window to outside or this declaration, above and below grade alike. Absent fields are unchanged; null clears.

Input parameters:

- `patch` (object, required)
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `divide_zone` (~137 tokens)

Divide room

Divide a room with an open path (endpoints snap to its boundary; the seed side keeps its id) or a closed island (outer room keeps its id). The two rooms are parted by a separator (the editor's Separator: a room boundary with no wall), not a wall. Supply path or the legacy two-point cut. Islands need 0.25 m² and 5 cm wall clearance.

Input parameters:

- `closed` (boolean)
- `cut` (array)
- `endBoundaryId` (string)
- `path` (array)
- `startBoundaryId` (string)
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `merge_zones` (~50 tokens)

Merge rooms

Merge two rooms by removing the separators they share (the editor's Separator: a room boundary with no wall). Walls are preserved; wall-only boundaries return a conflict.

Input parameters:

- `zoneIds` (array, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `delete_zone` (~147 tokens)

Delete room

Delete a room. A room Divide made, which shares a separator (the editor's Separator: a room boundary with no wall) with a room on its outline, merges back into that room: the separators go, walls and items stay (mode "merge"). A room whose every boundary is a wall shared with other rooms is refused with a "shared-walls" conflict and nothing changes (mode "blocked"); delete one of its walls instead. Otherwise the room goes with its unshared walls, separators and their openings (mode "delete"), and contents chooses whether its items are kept or deleted. Returns the disposition payload.

Input parameters:

- `contents` (string, required)
- `zoneId` (string, required)

Output parameters:

- `achieved` (object)
- `changes` (number)
- `conflicts` (array)
- `hints` (array)
- `idMap` (object)
- `openingId` (string)
- `openingIds` (array)
- `payload` (object)
- `persistence` (object)
- `project` (string|null)
- `separatorId` (string)
- `separatorIds` (array)
- `unsaved` (string)
- `zoneId` (string)
- `zoneIds` (array)

### `run_batch` (~267 tokens)

Run a batch of scene tools

Optionally run several scene tools in one call: in order, each on the scene the calls before it left, applied as one undo step, with a report per call. Supports repeated or known-in-advance work; individual calls remain available. A call cannot use an id another call of the same batch creates; created ids are available in the report for subsequent calls. onRefusal stop (default): a refused call stops the batch and nothing is applied; skip: the others are applied and each refusal is reported; add_object calls are independent of each other under both: one that fails is reported with its own error and the others are applied, each with the receipt a single add_object gives. Tools: paint, search_materials, list_levels, get_node, get_level_summary, get_walls, get_zones, duplicate_level, verify_scene, delete_node, measure_stair, fit_stair, find_by_type, create_roof, add_wall, add_fence, add_level, create_stair, add_corner_window, merge_windows, add_column, add_object, place_design, place_in_room.

Input parameters:

- `calls` (array, required)
- `onRefusal` (string): stop (default): nothing is applied if a call is refused. skip: apply the rest.

### `list_collections` (~46 tokens)

List collections

List the scene's collections with their elements (id, name, type, level), and the templates a collection can start from.

Input parameters:

- `collectionId` (string): Only this collection.

### `edit_collection` (~209 tokens)

Edit collection

Create, rename, recolour or delete a collection, or add and remove its elements. A collection is a named set of any scene elements (lights, windows, doors, columns, objects built with add_object…), listed in the editor's Collections; being in one changes nothing about the elements. Without collectionId the call works on the scene's collection with that template, or else that name, and creates it when there is none.

Input parameters:

- `add` (array): Ids of the elements to add.
- `collectionId` (string): The collection to change.
- `color` (string): Colour, e.g. #f5b83d.
- `delete` (boolean): Delete the collection; its elements stay in the scene.
- `name` (string): Name of the collection, or its new name.
- `remove` (array): Ids of the elements to take out.
- `template` (string): Start from a template, which gives the name and colour unless you pass them.

### `add_column` (~715 tokens)

Add column

Add a structural or decorative column at (x, z), or rebuild one by nodeId. Supports native fields or optional code using the same module and conventions as add_object, with floor mount and its bottom centre at the support point. Params named height, width and depth drive the native size controls. Only add columns when requested.

Input parameters:

- `category` (string): What this design is, in one or two words.
- `code` (string): A module (three.js, or bpy with language) when the native fields cannot express the column. Same module and conventions as add_object; mount floor. Declare height, width and depth params for editable…
- `crossSection` (string): Plan shape: 'round' (default), 'square' (equal width/depth), 'rectangular' (independent width/depth), 'octagonal', 'sixteen-sided'.
- `depth` (number|string): Depth when crossSection is "square" / "rectangular" (default: 0.44). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `description` (string): A short description of this design, at most 200 characters.
- `height` (number|string): Column height (default: 2.5). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `language` (string): The code's language: 'three' (default) for a three.js module, 'bpy' for a Blender module (see add_object). On an edit it defaults to the language the object was built in.
- `level` (string): Level id from list_levels; omitted uses the viewed or lowest level.
- `name` (string): What the user would call this design.
- `nodeId` (string): Rebuild this column with code, params or native size fields; get_source provides current code when needed.
- `params` (object): Values for the params the module declares; on an edit, each one you leave out keeps its current value (clamped to the param's range) and a param new to the code starts at its default.
- `radius` (number|string): Radius when crossSection is "round" (default: 0.22). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `rotation` (number|string): Y-axis rotation (default: 0). In degrees, or text with a unit ("1.57rad").
- `style` (string): Visual style: 'plain' (default smooth pillar), 'faceted' (sharp prism faces), 'fluted' (vertical channels — classical), 'lathe-turned' (turned bands — porch / interior), 'dravidian-carved' (heavy car…
- `tags` (array): Up to five short lowercase search tags, at most 32 characters each.
- `width` (number|string): Width when crossSection is "square" / "rectangular" (default: 0.44). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `x` (number): X position of the column center (meters)
- `y` (number): Support point height above the level, in metres (default 0).
- `z` (number): Z position of the column center (meters)

### `add_object` (~1883 tokens)

Build object

Author parametric Three.js furniture, decor and custom objects of any shape, dimensions or style, whether or not the catalog holds something similar (the catalog is a limited set that varies by server). Examples include a low oak table, a slatted bench, a paper lantern, built-ins, custom columns and capitals, mouldings and trim, panels, exposed beams, ceiling bodies, canopies, a porch and railings. Pascal runs the module in a sandbox, stores the result and places it as one object the user can move, paint, and ask you to edit again.

Module shape (import THREE from the three package as usual; the addons below too):
  // also available: three/addons/utils/BufferGeometryUtils.js, three/addons/geometries/{RoundedBoxGeometry,ConvexGeometry,LoftGeometry,ParametricGeometry}.js, three-bvh-csg (Brush, Evaluator, SUBTRACTION, ADDITION, INTERSECTION)
  export const params = { width: { default: 4.8, min: 3, max: 8, step: 0.1, unit: 'm', label: 'Width' } }
  export const mount = 'floor'   // 'floor' | 'wall-side' (on a wall face) | 'wall' (through a wall, like a window) | 'ceiling'
  export default function build({ params, THREE }) { const group = new THREE.Group(); /* … */ return group }

One object is one feature that changes together: a table, a bench, a porch, a railing run, a fireplace surround, a ceiling with its beams. Never a whole house, and never walls, rooms, floors, roofs, stairs, doors or windows: those have their own tools. A new object's reason records its design intent: what the authored shape, dimensions or style contributes to the request. The scene check lists every authored object with its reason; no catalog-exhaustion proof is required. On a floor, a plain box with a wall's size or a floor plate is refused with the tool to use (use_walls, use_slab); a detailed object with a wall's size (a bookcase, a screen), or one named after something Pascal builds, is built with a hint naming the tool.

Conventions (they make the object work in Pascal; follow them):
\- M…

Input parameters:

- `category` (string): What it is, one word or two ("table", "bench", "porch", "lantern", "trim").
- `code` (string): The module (see the tool description). Required to create; omit to rebuild an object with new params.
- `description` (string): A short description of this design, at most 200 characters.
- `language` (string): The code's language: 'three' (default) for a three.js module, 'bpy' for a Blender module (see add_object). On an edit it defaults to the language the object was built in.
- `name` (string): What the user would call it ("Low oak table").
- `nodeId` (string): Edit this existing authored object instead of creating one.
- `params` (object): Values for the params the module declares; on an edit, each one you leave out keeps its current value (clamped to the param's range) and a param new to the code starts at its default.
- `parentId` (string): Host for a new object: a level (default: the floor in view), a wall (wall / wall-side mounts), a ceiling, or an item it rests on.
- `position` (array): Placement of the mount origin in the host frame, metres. Floor: [x, y, z] locates the bottom-centre (y = 0 on the level floor). Ceiling: [x, y, z] locates the top-centre (y = 0 on the drawn ceiling a…
- `reason` (string): Design intent: why this authored shape, size or style suits the request ("low oak table for the Japandi seating area"). Required to create; an edit keeps it unless given. No catalog-exhaustion proof…
- `rotation` (number|string): Y-axis rotation in the host frame. New wall-side back mounts default to 180° so +Z faces away from the wall; other new objects default to 0°. An edit keeps its saved rotation unless given. In degrees…
- `side` (string): Which wall face a wall-side object sits on.
- `tags` (array): Up to five short lowercase search tags, at most 32 characters each.

### `author_scene` (~711 tokens)

Author scene

Turn a whole design you wrote as free three.js into an editable Pascal scene, in one call. You read your own module however you like (its source, running it and reading the objects, both) and hand over a conversion payload: levels, walls with their openings, rooms, slabs, columns, railings, every piece of furniture as an authored object with its own module, and lights. Pascal does the rest server-side: compiles and stores each item, places it, derives the rooms and their floors, paints, runs its checks, saves, and answers with a gate report you iterate against. Nothing you hand over is silently dropped: what cannot be placed comes back by name with a reason, and what Pascal corrected (a wall end snapped onto the centerline it meets) is listed.

Coordinates: metres, plan x/z, Y up, each level's floor at y = 0 in its own frame. A wall is its centerline; walls meet where centerlines meet, so end a partition on the centerline of the wall it runs into. An opening's 'along' is measured from the wall's start to the opening's centre; give each opening its type (how it opens) and style (its look), or its own module for a shape they cannot express. An item's position is where its bottom centre stands; its module follows the add_object contract (metres, Y up, built in its installed orientation; at most 300k triangles and 32 materials per item; name materials slot_<finish>; drawers, doors and lids as animation clips; a THREE light named light:<id> where a bulb is). A hex colour is written as an exact material, one per distinct colour. Pascal builds a wall's cap, the floor plate and the ceiling itself: fold a module's caps, underlays and floor planks into the wall, the room's floor finish and the room (say so in notes), never into slabs or regions: baseboards are the wall's skirting, a plank or tile floor is the room's floorCode, tiles, panelling or boards on a wall are its faceLeft / faceRight (or the room's wallCode); a slab is a terrace, a platform, a balcony. Build the buil…

Input parameters:

- `adapter` (string): The program that produced the payload from the module. Stored with the scene, so a regenerated design starts from it.
- `buildingId` (string): The building to convert into. Required when there are several and no active level selects one.
- `module` (string): The three.js module the payload was read from. Stored with the scene as its source.
- `payload` (object, required)
- `replace` (boolean): Apply a new design delta to the previous conversion, preserving manual edits (default false).
- `sceneId` (string): The scene to write; the active one when omitted.

### `get_source` (~187 tokens)

Read object script

The module (three.js or Blender bpy, named by language) an object (or a window, door or column built from code) runs, with its params and their current values; a room's id reads its floor script, the module that builds its floor finish; a wall's id with face reads that face's script (face may be left out when only one face has one). Optional source evidence when the current module is needed. An edited module can be passed back with the same nodeId: to add_object for an object, to add_window / add_door / add_column for their respective kinds.

Input parameters:

- `face` (string): A wall's face whose script to read; needed when both faces have one.
- `nodeId` (string, required): An object, window, door or column built from code, a room with a floor script, or a wall with a face script.

### `measure` (~184 tokens)

Measure

Measure the distance (in meters) between two nodes, or the net area of a polygon node when fromId === toId. Distance is between world-space reference points, returned as fromPoint/toPoint: a positioned node's origin in its host's frame (an item's base, a door's or window's centre in its wall), a wall's or fence's midpoint at its base, a slab's, ceiling's or zone's polygon centroid on its plane, a block's or imported mesh's vertex-bounds centre, a level's plan centre on its base plane, and another container's descendants' centre. `approximate` lists nodes whose renderer derives the pose from data not modelled here (a downspout at its gutter outlet, a gutter at the eave), with the reason.

Input parameters:

- `fromId` (string, required)
- `toId` (string, required)

Output parameters:

- `approximate` (array)
- `areaSqMeters` (number)
- `areaUnits` (string)
- `distanceMeters` (number)
- `fromPoint` (array)
- `toPoint` (array)
- `units` (string)

### `view_scene` (~1373 tokens)

Look at the scene

Look at the building in 3D from a viewpoint you pick and get the picture back: the finished house, every storey and the roof by default, or an explicit Solo/Exploded level presentation, whatever view mode the person has on. Use it to compare what you built with a reference and say what differs before you fix it: the facade from the photo's own camera (camera: its position, aim, field of view and aspect), rendered at the photo's aspect to lay beside it; or from the photo's side at street height (from 'street', 'street-left', …, eyeHeight 1.7), in its light (lighting); one face square on (projection orthographic); the massing from above, or an aerial view (elevation 40). The view frames the target (a building, a level, a wall or a zone; the whole building, its roof included, by default) from outside, unless you place the eye yourself with position or camera. To stand inside a room and see its furniture and finishes at a person's height, give interior {zoneId} (a room: an id from get_zones or create_room): the eye stands in the room a step in from a corner, looking across it, with the cut below; from picks the side of the room it stands on, target something in the room to look at it. To see inside a floor, give interior {levelId}: the other floors and that floor's roofs and ceilings are left out of the picture, and everything on it, walls, openings and furniture alike, is cut at 0.65 of the storey's height (about two thirds), so a tall wardrobe or a door head is cut, not short; a roof level has no inside; with no eye given it looks down into the floor, or into the room or zone you target. A level below grade, targeted alone, is looked into the same way (from outside the ground would cover it), the ground over it drawn as a faint veil. The live view is not changed. A door, a window or an item on a floor frames at detail scale, an opening seen from its outside face: a close-up to lay beside the photo's crop of the same element, to see what differs. Over the MCP the user…

Input parameters:

- `camera`: A photo's camera (position, target, fov, aspect): the render takes its eye, aim and field of view at the photo's aspect. Or 'current': the person's own view, as their editor shows it now (the MCP has…
- `elevation` (number|string): Degrees above the horizon the eye looks down from: 35 to 60 for an aerial view. Default 12. In degrees, or text with a unit ("1.57rad"). Range -10–89 deg.
- `eyeHeight` (number|string): Metres above the ground for a street view (1.7), instead of elevation. In metres, or text with a unit ("6 ft"). Minimum 0 m.
- `fov` (number|string): Vertical field of view in degrees for a perspective view. Default 45. In degrees, or text with a unit ("1.57rad"). Range 10–100 deg.
- `from` (string): The side the eye stands on: a compass side (north is the plan's top edge, z grows south, x east), above, or a side as a front photo names it, standing in the street facing the house ('street-left' is…
- `interior` (object): Look inside, with one of: levelId (a floor): the other floors and its roofs and ceilings are left out of the picture, everything on it (walls, openings, furniture) cut at 0.65 of the storey height. D…
- `lighting` (string): The light the picture is rendered in: the scene's own (current, the default), or day, overcast, sunset, dusk (twilight, the lights on) or night, as a reference photo was taken in.
- `maxPx` (integer): The picture's longest side in pixels, for a smaller one. Default 1920 by 1200, and never longer than 1920.
- `photo` (object): The photo's crop of the element the view frames, returned beside it in the same call: a door's glass, a lamp's shape, a window's panes, at detail scale, to say what differs.
- `position` (array): The eye exactly, [x, height, z] in metres; it looks at the target.
- `presentation`: Verify the actual level presentation: stacked (default), exploded, or solo with a levelId. Solo keeps full-height geometry, including ceilings and any roofs incorrectly owned by that floor; it is not…
- `projection` (string): orthographic for a square-on elevation of a face, no perspective.
- `target` (string): A building, level, wall, zone, door, window or floor item id to frame. Default: every storey in the scene, walls and roofs.

### `create_story_shell` (~327 tokens)

Create story shell

Create one level-owned building shell from a footprint: the perimeter walls. The floor plate and the ceiling are DERIVED from the rooms the walls enclose — never author a slab or a ceiling here, and never pass boundary/autoFromWalls; createSlab / createCeiling / slabElevation are recorded as room intent instead. Use once per story; do not make first-floor walls span multiple stories.

Input parameters:

- `ceilingHeight` (number|string): Ceiling height. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `ceilingMaterialPreset` (string)
- `createCeiling` (boolean): Ceiling intent for the rooms inside the shell. False records hasCeiling: false.
- `createSlab` (boolean): Floor intent for the rooms inside the shell. False records hasFloor: false.
- `footprint` (array, required)
- `levelId` (string, required)
- `namePrefix` (string)
- `slabElevation` (number|string): Walking surface of the derived plate, stored as the room's floor elevation. In metres, or text with a unit ("6 ft").
- `slabMaterialPreset` (string)
- `wallHeight` (number|string): Explicit wall height override. Omit for level-plane-bound walls. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `wallMaterialPreset` (string)
- `wallThickness` (number|string): Wall thickness. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.

Output parameters:

- `ceilingId` (string|null)
- `createdIds` (array)
- `levelId` (string)
- `persistence` (object)
- `project` (string|null)
- `slabId` (string|null)
- `unsaved` (string)
- `wallIds` (array)
- `zoneIds` (array)

### `add_door` (~891 tokens)

Add door

Add a door to an existing straight wall at t (0..1 along it), or a passage with no leaf (openingKind opening: a cased opening, an arch). The door slides to stay on the wall and reports clamped. Refused with a code, as in the editor: curved walls, walls shorter than the door, and overlapping another door, window or wall item unless force is set. Match the reference with the outline (rectangle, rounded, arch), doorType and style; what they cannot express (glass strips, a pattern of lites, carved panels) is written as a script in code, never left as not possible.

Input parameters:

- `archHeight` (number|string): Rise of the arch above its straight sides, within height (arch only; default 0.45 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `category` (string): What this design is, in one or two words.
- `code` (string): An optional module (three.js, or bpy with language) for a door (a fan grille, tracery, carved trim): the same module and conventions as add_object, with mount 'wall'. Its size is what it builds (name…
- `cornerRadius` (number|string): Radius of the rounded top corners (rounded only; default 0.15 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `description` (string): A short description of this design, at most 200 characters.
- `doorType` (string): How it opens (default hinged); garage types for garage doors.
- `force` (boolean): Place it even where it overlaps another door, window or wall item, like holding Alt in the editor. Only when the person asks for the overlap.
- `handleSide` (string): Handle side (default: opposite the hinges).
- `height` (number|string): Door height (default 2.1 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `hingesSide` (string): Hinge side (default left).
- `language` (string): The code's language: 'three' (default) for a three.js module, 'bpy' for a Blender module (see add_object). On an edit it defaults to the language the object was built in.
- `name` (string): What the user would call this design.
- `nodeId` (string): Rebuild this door instead of adding one: new code and/or params (get_source provides current code when needed). Its native fields change with update_node.
- `openingKind` (string): door (default), or opening: a passage with no leaf, framed or arched by the outline, as the editor's door panel offers.
- `openingShape` (string): Outline of the opening: rectangle (default), rounded top corners, or arch (round top).
- `params` (object): Values for the params the module declares; on an edit, each one you leave out keeps its current value (clamped to the param's range) and a param new to the code starts at its default.
- `position` (number): Same as t.
- `style` (string): Visual preset (panels only, never the size); same presets as create_room doors[].
- `swingDirection` (string): Which way the door opens (default inward).
- `t` (number): Position along the wall, 0..1: 0 = start, 0.5 = centre, 1 = end.
- `tags` (array): Up to five short lowercase search tags, at most 32 characters each.
- `wallId` (string): The wall to add the door to.
- `width` (number|string): Door width (default 0.9 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.

Output parameters:

- `achieved` (object)
- `clamped` (boolean)
- `coordinateSystem` (string)
- `doorId` (string)
- `localX` (number)
- `message` (string)
- `ok` (boolean)
- `persistence` (object)
- `project` (string|null)
- `t` (number)
- `unsaved` (string)
- `wallId` (string)
- `wallLength` (number)

### `add_window` (~926 tokens)

Add window

Add a window to an existing straight wall at t (0..1 along it), centred vertically on the wall, or on sillHeight above the floor when given. It slides to stay on the wall and under the wall's ceiling, and reports clamped. Refused with a code, as in the editor: curved walls, walls shorter than the window, overlapping another door, window or wall item unless force is set, and a style on a window that is not Fixed (style_needs_fixed_window). Match the reference with the outline (rectangle, rounded, arch), windowType and panes (columns × rows, or a style); what they cannot express (glass strips, leaded lites, a feature frame) is written as a script in code, never left as not possible.

Input parameters:

- `archHeight` (number|string): Rise of the arch above its straight sides, within height (arch only; default 0.35 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `category` (string): What this design is, in one or two words.
- `code` (string): An optional module (three.js, or bpy with language) for a window (a fan grille, tracery, carved trim): the same module and conventions as add_object, with mount 'wall'. Its size is what it builds (na…
- `columns` (integer): Panes across, equal widths; overrides the style's panes.
- `cornerRadius` (number|string): Radius of the rounded top corners (rounded only; default 0.15 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `description` (string): A short description of this design, at most 200 characters.
- `force` (boolean): Place it even where it overlaps another door, window or wall item, like holding Alt in the editor. Only when the person asks for the overlap.
- `height` (number|string): Window height (default 1.5 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `language` (string): The code's language: 'three' (default) for a three.js module, 'bpy' for a Blender module (see add_object). On an edit it defaults to the language the object was built in.
- `name` (string): What the user would call this design.
- `nodeId` (string): Rebuild this window instead of adding one: new code and/or params (get_source provides current code when needed). Its native fields change with update_node.
- `openingShape` (string): Outline of the opening: rectangle (default), rounded top corners, or arch (round top).
- `params` (object): Values for the params the module declares; on an edit, each one you leave out keeps its current value (clamped to the param's range) and a param new to the code starts at its default.
- `position` (number): Same as t.
- `rows` (integer): Panes up, equal heights; overrides the style's panes.
- `sillHeight` (number|string): Height from the floor to the bottom of the window (default: centred vertically on the wall). In metres, or text with a unit ("6 ft"). Minimum 0 m.
- `style` (string): Visual preset of a Fixed window's panes (never the size); same presets as create_room windows[]. Fixed windows only: an operable window draws its own sashes.
- `t` (number): Position along the wall, 0..1: 0 = start, 0.5 = centre, 1 = end.
- `tags` (array): Up to five short lowercase search tags, at most 32 characters each.
- `wallId` (string): The wall to add the window to.
- `width` (number|string): Window width (default 1.5 m). In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `windowType` (string): How it opens (default fixed).

Output parameters:

- `achieved` (object)
- `clamped` (boolean)
- `coordinateSystem` (string)
- `localX` (number)
- `message` (string)
- `ok` (boolean)
- `persistence` (object)
- `project` (string|null)
- `sillHeight` (number)
- `t` (number)
- `unsaved` (string)
- `wallId` (string)
- `wallLength` (number)
- `windowId` (string)

### `apply_patch` (~341 tokens)

Apply patch

Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.

Input parameters:

- `patches` (array, required)

Output parameters:

- `appliedOps` (number)
- `createdIds` (array)
- `deletedIds` (array)
- `notes` (array)
- `persistence` (object)
- `project` (string|null)
- `unsaved` (string)

### `clear_scene` (~104 tokens)

Clear the project

Empty the project on purpose, back to the default scaffold: a site, a building and one level of the standard storey height. It is the only way to empty a project: a write that would empty it is refused (scene_wipe_blocked), since that is far more often an accident. Call it only when the person asked to start over, and say what they asked in reason.

Input parameters:

- `reason` (string, required): What the person asked for, in their words.

Output parameters:

- `cleared` (object)
- `graphHash` (string|null)
- `persistence` (object)
- `project` (string|null)
- `unsaved` (string)
- `version` (number|null)

### `create_unit` (~61 tokens)

Create unit

Create a named unit in a building, optionally referencing zones across its levels.

Input parameters:

- `buildingId` (string, required)
- `color` (string)
- `kind` (string)
- `memberZoneIds` (array)
- `name` (string, required)

Output parameters:

- `persistence` (object)
- `project` (string|null)
- `unitId` (string)
- `unsaved` (string)

### `set_unit_members` (~46 tokens)

Set unit members

Replace the zones referenced by a unit. Every zone must belong to a level in the same building.

Input parameters:

- `memberZoneIds` (array, required)
- `unitId` (string, required)

Output parameters:

- `memberCount` (number)
- `persistence` (object)
- `project` (string|null)
- `unitId` (string)
- `unsaved` (string)

### `list_units` (~32 tokens)

List units

List units with their member zones and current area reports, optionally filtered by building.

Input parameters:

- `buildingId` (string)

Output parameters:

- `units` (array)

### `set_zone` (~119 tokens)

Set zone

Create a polygonal zone on the given level. label is stored as the zone name and properties are merged into metadata. A polygon along walls that already enclose a room is that room, never a second zone: a room still named "Room N", or already named label, takes the label and properties; a room a person named otherwise is refused (rename it with update_node only when the person asked).

Input parameters:

- `label` (string, required)
- `levelId` (string, required)
- `polygon` (array, required)
- `properties` (object)

Output parameters:

- `persistence` (object)
- `project` (string|null)
- `unsaved` (string)
- `zoneId` (string)

### `undo` (~37 tokens)

Undo

Undo the most recent N steps in the scene history (default 1). Returns the number of steps actually undone.

Input parameters:

- `steps` (integer)

Output parameters:

- `persistence` (object)
- `project` (string|null)
- `undone` (number)
- `unsaved` (string)

### `redo` (~37 tokens)

Redo

Redo the next N previously-undone steps (default 1). Returns the number of steps actually redone.

Input parameters:

- `steps` (integer)

Output parameters:

- `persistence` (object)
- `project` (string|null)
- `redone` (number)
- `unsaved` (string)

### `export_json` (~37 tokens)

Export JSON

Return the scene as a serialized JSON string. Pass `pretty: true` to indent with 2 spaces.

Input parameters:

- `pretty` (boolean)

Output parameters:

- `json` (string)

### `export_glb` (~43 tokens)

Export GLB

GLB export is not available in headless mode — it requires the Three.js renderer, which is browser-only. Returns a structured `not_implemented` tool error.

Output parameters:

- `reason` (string)
- `status` (string)

### `check_collisions` (~144 tokens)

Check collisions

Assess declared item footprints with a rotation-aware plan AABB test. Supports an explicit minimum clearance and reports units, source dimensions, skipped evidence, and unsupported checks. Optionally scope to one level or floor-standing items.

Input parameters:

- `candidate` (object): Read-only prospective furniture item. It is checked against the target level but never added to the scene.
- `floorOnly` (boolean): When true, wall-, wall-side-, and ceiling-attached items are excluded.
- `levelId` (string)
- `minimumClearance` (number|string): Minimum free plan-space required between item footprints. In metres, or text with a unit ("6 ft"). Minimum 0 m.

Output parameters:

- `assessmentGraphHash` (string)
- `candidateItemId` (string|null)
- `checkedItems` (array)
- `collisions` (array)
- `floorOnly` (boolean)
- `method` (string)
- `minimumClearanceMeters` (number)
- `skippedItems` (array)
- `status` (string)
- `units` (string)
- `unsupportedChecks` (array)

### `list_templates` (~37 tokens)

List scene templates

List the seed Pascal scene templates available to `create_from_template`. Returns the id, display name, one-line description and node count for each.

Output parameters:

- `templates` (array)

### `create_from_template` (~117 tokens)

Create scene from template

Instantiate a seed Pascal scene template into the bridge. Regenerates all ids before applying. When `save: true` and a SceneStore is wired, also persists the new scene and returns the SceneMeta.

Input parameters:

- `id` (string, required): Template id (see `list_templates`). Currently one of: "empty-studio", "two-bedroom", "garden-house".
- `name` (string): Optional display name for the saved scene. Defaults to the template name.
- `projectId` (string)
- `save` (boolean)

Output parameters:

- `nodeCount` (number)
- `rootNodeIds` (array)
- `saveSkipped` (boolean)
- `scene` (object)
- `templateId` (string)

### `create_house_from_brief` (~106 tokens)

Create house from brief

High-level hosted workflow for external agents: choose a starter house from a brief, create/save/publish it, and return the editor URL. Use semantic tools afterward for exact customization.

Input parameters:

- `bedroomCount` (integer)
- `brief` (string, required)
- `constraints` (string)
- `landscaping` (boolean)
- `projectId` (string)
- `projectName` (string)
- `rooms` (array)
- `style` (string)

Output parameters:

- `defaultLevelId` (string|null)
- `editorUrl` (string|null)
- `isDraft` (boolean)
- `levelIds` (array)
- `limitations` (array)
- `nextStep` (string)
- `nodeCount` (number)
- `projectId` (string|null)
- `published` (boolean)
- `roomCount` (number)
- `summary` (string)
- `templateId` (string)
- `url` (string|null)
- `validation` (object)
- `version` (number|null)

### `create_project` (~55 tokens)

Create project

Create a browser-visible Pascal project for the authenticated user. Use this before save_scene when the user asks for a new project.

Input parameters:

- `id` (string)
- `isPrivate` (boolean)
- `name` (string, required)

Output parameters:

- `browserVisibleVersion` (number|null)
- `createdAt` (string)
- `defaultLevelId` (string|null)
- `draftVersion` (number|null)
- `editorUrl` (string)
- `graphHash` (string|null)
- `id` (string)
- `isEmpty` (boolean)
- `latestVersion` (number|null)
- `levelIds` (array)
- `name` (string)
- `nextStep` (string)
- `nodeCount` (number)
- `ownerId` (string|null)
- `projectId` (string)
- `publishedVersion` (number|null)
- `sizeBytes` (number)
- `thumbnailUrl` (string|null)
- `updatedAt` (string)
- `url` (string)
- `version` (number)

### `get_project_status` (~47 tokens)

Get project status

Authoritative status/debug call for a Pascal project: editor URL, browser-visible version, latest saved version, published version, node count, and graph hash.

Input parameters:

- `id` (string, required)

Output parameters:

- `browserVisibleVersion` (number|null)
- `createdAt` (string)
- `defaultLevelId` (string|null)
- `draftVersion` (number|null)
- `editorUrl` (string)
- `graphHash` (string|null)
- `id` (string)
- `isEmpty` (boolean)
- `latestVersion` (number|null)
- `levelIds` (array)
- `name` (string)
- `nextStep` (string)
- `nodeCount` (number)
- `ownerId` (string|null)
- `projectId` (string)
- `publishedVersion` (number|null)
- `sizeBytes` (number)
- `thumbnailUrl` (string|null)
- `updatedAt` (string)
- `url` (string)
- `version` (number)

### `save_scene` (~341 tokens)

Save scene

Save the current scene (or a provided graph) to the SceneStore. Without id or projectId it saves to the project this session is bound to (create_project or load_scene). Defaults to a browser-visible draft save so agents can iterate without creating many project versions. Use saveMode: "checkpoint" for meaningful version history.

Input parameters:

- `expectedVersion` (integer)
- `graph` (object): Full SceneGraph { nodes, rootNodeIds, collections? } to save instead of the bridge state. Judged against the scene it is saved over (the session's scene when the save goes to it, else the stored scen…
- `id` (string)
- `includeCurrentScene` (boolean): If true, save the bridge current scene. If false, use the graph arg.
- `name` (string, required)
- `projectId` (string)
- `publish` (boolean): Hosted Pascal: also publish this checkpoint, making it the version the project's viewers see. Only when the user asks to publish.
- `replace` (boolean): Write this session's scene over a project or scene it was not loaded from, replacing what that one holds.
- `saveMode` (string): `draft` updates the browser-visible working model without polluting version history. `checkpoint` creates a meaningful saved version; it does not publish.
- `thumbnail` (string)

Output parameters:

- `createdAt` (string)
- `defaultLevelId` (string|null)
- `editorUrl` (string)
- `graphHash` (string)
- `id` (string)
- `isDraft` (boolean)
- `levelIds` (array)
- `name` (string)
- `nodeCount` (number)
- `notes` (array)
- `ownerId` (string|null)
- `projectId` (string|null)
- `published` (boolean)
- `saveMode` (string)
- `sizeBytes` (number)
- `thumbnailUrl` (string|null)
- `updatedAt` (string)
- `url` (string)
- `version` (number)

### `load_scene` (~44 tokens)

Load scene

Load a scene from the SceneStore into the bridge. Returns the scene metadata. Throws `scene_not_found` if the id does not exist.

Input parameters:

- `id` (string, required)

Output parameters:

- `createdAt` (string)
- `defaultLevelId` (string|null)
- `editorUrl` (string)
- `graphHash` (string)
- `id` (string)
- `isDraft` (boolean)
- `levelIds` (array)
- `name` (string)
- `nodeCount` (number)
- `ownerId` (string|null)
- `projectId` (string|null)
- `published` (boolean)
- `saveMode` (string)
- `sizeBytes` (number)
- `thumbnailUrl` (string|null)
- `updatedAt` (string)
- `url` (string)
- `version` (number)

### `list_scenes` (~51 tokens)

List scenes

List scenes in the SceneStore. Optionally filter by `projectId` and cap results with `limit` (default 100).

Input parameters:

- `limit` (integer)
- `projectId` (string)

Output parameters:

- `scenes` (array)

### `delete_scene` (~109 tokens)

Delete scene

Remove a stored scene by id. In Pascal this never deletes the project: it hides it from Pascal's public listings until it is next saved with content, and answers `deleted: false, hidden: true` with a message; its owner can delete it from the projects page. Only someone who can manage the project may hide it. A local scene store deletes the scene. Optionally pass `expectedVersion` for optimistic concurrency.

Input parameters:

- `expectedVersion` (integer)
- `id` (string, required)

Output parameters:

- `deleted` (boolean)
- `hidden` (boolean)
- `message` (string)

### `rename_scene` (~56 tokens)

Rename scene

Rename a scene in the SceneStore. Returns the updated SceneMeta. Optionally pass `expectedVersion` for optimistic concurrency.

Input parameters:

- `expectedVersion` (integer)
- `id` (string, required)
- `newName` (string, required)

Output parameters:

- `createdAt` (string)
- `id` (string)
- `name` (string)
- `nodeCount` (number)
- `ownerId` (string|null)
- `projectId` (string|null)
- `sizeBytes` (number)
- `thumbnailUrl` (string|null)
- `updatedAt` (string)
- `version` (number)

### `generate_variants` (~133 tokens)

Generate variants

Generate N variations of a base scene by forking and applying seeded mutations. Example: "give me 5 variations of this kitchen". If `save=true`, each variant is persisted via scene operations and returned with an id + URL; otherwise the graph is returned inline.

Input parameters:

- `baseSceneId` (string): If set, fork from this saved scene; else fork from current bridge state.
- `count` (integer)
- `save` (boolean): If true, also save each variant via SceneStore and return ids.
- `seed` (integer): Deterministic RNG seed.
- `vary` (array)

Output parameters:

- `variants` (array)

### `photo_to_scene` (~185 tokens)

Photo to Pascal scene

Orchestrator: analyse a floor-plan photo via MCP sampling, translate the structured vision result into a Pascal SceneGraph (site → building → level with walls and zones), optionally save it, and swap the bridge to the new scene. Requires host support for sampling.

Input parameters:

- `defaultWallHeight` (number|string): Default wall height. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `defaultWallThickness` (number|string): Default wall thickness. In metres, or text with a unit ("6 ft"). Must be greater than 0 m.
- `image` (string, required): Base64 or https URL of the floor-plan photo
- `name` (string)
- `save` (boolean)
- `scaleHint` (string): e.g. "1 cm = 1 m" or "approx 80 m²"

Output parameters:

- `confidence` (number)
- `graph`
- `notes` (string)
- `rooms` (number)
- `sceneId` (string)
- `url` (string)
- `walls` (number)

### `analyze_floorplan_image` (~89 tokens)

Analyze floor-plan image

Defer to the MCP host (via sampling) to extract walls, rooms, and approximate dimensions from a floor-plan image. Requires host support for sampling.

Input parameters:

- `image` (string, required): Base64-encoded image or http(s) URL
- `scaleHint` (string): Text hint about scale, e.g. '1 cm = 1 m' or 'approximately 80 m²'

Output parameters:

- `approximateDimensions` (object)
- `confidence` (number)
- `rooms` (array)
- `walls` (array)

### `analyze_room_photo` (~57 tokens)

Analyze room photo

Defer to the MCP host (via sampling) to extract approximate dimensions, fixtures, and windows from a single-room photograph. Requires host support for sampling.

Input parameters:

- `image` (string, required): Base64-encoded image or http(s) URL

Output parameters:

- `approximateDimensions` (object)
- `identifiedFixtures` (array)
- `identifiedWindows` (array)

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/app-pascal-editor/pascal-app-mcp#diagnostics

## Score history

- 2026-10-24: 79
- 2026-10-23: 64

## Common questions

### What is the Pascal 3D Editor MCP server?

Pascal 3D Editor is an MCP server listed in the public MCP registry as app.pascal/editor. Create, inspect, validate, and save editable 3D building scenes with Pascal's MCP server. This page covers its npm package (@pascal-app/mcp).

### Is the Pascal 3D Editor MCP server safe to use?

Pascal 3D Editor scores 79 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 24 October 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Pascal 3D Editor MCP server expose?

Pascal 3D Editor exposes 86 tools: get_scene, validate_design, place_design, measure_stair, fit_stair, and 81 more. Their descriptions and schemas cost roughly 26,008 tokens of context every time the server is loaded.

### Is the Pascal 3D Editor MCP server still maintained?

Pascal 3D Editor is still listed as active in the MCP registry. We last reached this channel on 24 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the Pascal 3D Editor MCP server under?

Pascal 3D Editor declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/@pascal-app/mcp
- Socket report: https://socket.dev/npm/package/@pascal-app/mcp
- Repository: https://github.com/pascalorg/editor
- Website: https://editor.pascal.app/docs/developers/mcp
- Changelog RSS feed: https://verifymcp.io/servers/app-pascal-editor/pascal-app-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/app-pascal-editor/pascal-app-mcp.json
- HTML version of this page: https://verifymcp.io/servers/app-pascal-editor/pascal-app-mcp
