get_scene | Return the full scene graph. | — | { nodes, rootNodeIds, collections } |
get_node | Fetch a node by id, whole, with what the scene decides for it beside its stored fields (a wall's or ceiling's resolvedHeight and heightIsExplicit, a wall's resolvedThickness, a door's or window's resolvedOpening, a level's or floor plate's displayName), its ancestors parent first, and the scene materials its finishes use; a node its schema rejects comes back as stored, nothing derived. | { id } | { node, ancestorIds, missingChildIds?, materials? }, or refusal node_not_found |
find_by_type | Find everything of one type: nodes of a kind (any core or plugin kind), items of a category, lights, and typed parts inside authored objects, windows and doors; narrowed by level, parent, zone (on its level, by plan point, holes left out) or import source id (provenance.refs[].id or legacy metadata.sourceIds, exact or prefix); without a type, every node the filters match. Each result is the node's id, name, kind, level and parent, with its metadata and provenance (one over 1 KB named in omitted). | { type?, levelId?, parentId?, zoneId?, sourceId?, sourceIdPrefix?, limit? } | { type?, count, nodes, parts, results[], truncated?, hint? }, at most limit (200) results listed; refusals unknown_type, level_not_found, node_not_found, zone_not_found, not_a_zone |
list_levels | List every building's levels in floor order with their role (storey, roof-only, support), their standing against the ground line (gradeStanding, isGroundLevel) and, per building, the level at grade. | — | { activeSceneId, activeLevelId, levelCount, occupiedStoryCount, supportLevelCount, roofLevelIds, groundLevels[], levels[] } |
get_level_summary | Compact summary of one level: role, counts, walls with openings, zones with areas, slabs, ceilings, items, stairs, roofs, and everything else by type. Omit levelId for the lowest storey. | { levelId?, level? } | { levelId, role, counts, walls, zones, slabs, ceilings, items, stairs, roofs, other } |
get_walls | Walls on a level with length, stored and resolved height, and child doors/windows. | { levelId?, level? } | { levelId, walls[] } |
get_zones | Room/zone polygons with holes, areas (holes taken out), bounds and floor choices. | { levelId?, level? } | { levelId, zones[] } |
get_site | The site a building stands on, in its level coordinates (a lot with no building yet, in site coordinates): the lot and its area with the footprint a house usually covers (30 to 45%), the buildable area inside the setbacks with its centre, area, each edge's frontage and depth and its front edge, the street side with the lot's frontage and depth, north, the ground (sloped, read flat or not read), address, parcel and zoning, whether the lot is the new-project placeholder, and the footprint's fit and yards. | { building?, site? } | { siteId, buildingId, pose, lot, placeholder, buildable, street, ground, northDeg, address, parcel, zone, footprint, others, summary }; refusals no_site, building_required, site_not_found, not_a_site, building_not_on_site |
place_building | Stand a building on its site by moving the building node only (walls keep their level coordinates): its footprint, the outline about to be drawn or the walls standing, centred in the buildable area or at the yards given; a house standing keeps its rotation unless rotationDeg is given, one with no walls yet is squared to the street. | { building?, outline?, yards?: { front?, rear?, left?, right? }, rotationDeg? } | { buildingId, position, rotationDeg, moved, footprint, yards, fit, overhang, movedWithIt?, message }; refusals no_site, needs_footprint, other_building_on_site, no_street, yards_conflict, yards_infeasible, building_required |
measure | Distance between two nodes' world-space reference points (hosted doors, windows and items resolved through their host; every node kind); area when applicable. | { fromId, toId } | { distanceMeters, fromPoint?, toPoint?, areaSqMeters?, units: 'meters' } |
view_scene | Look at the finished building, every storey and the roof, from a viewpoint the agent picks (a compass side, a side as seen from the house's street, a street-height eye, a photo's camera, square-on, from above), in the scene's own light or a photo's, and get the picture back, with the photo's crop beside it when a region is given. interior: { zoneId } stands inside a room at eye height (1.6 m by default), a step in from a corner and looking across it; interior: { levelId } looks inside one floor: the other floors and its roofs and ceilings left out, everything on it (walls, openings, furniture) cut at 0.65 of the storey's height, for the picture only. Pictures are 1920 × 1200 unless maxPx (64 to 1920) asks for a smaller longest side. The server has no renderer: the host's sceneViews renders it (the hosted app, through the user's open editor tab). Read-only. | { target?, interior?, from?, elevation?, eyeHeight?, position?, fov?, projection?, camera?, lighting?, maxPx?, photo? } | image content plus { status, camera, size, tab, capturedAt }; refusals view_unavailable, no_project, which_building (a street side, no target, buildings on several sites), no_current_camera (camera: 'current': the server has no view of the person's), camera_and_viewpoint (a camera with a viewpoint of the agent's own), photo_crop_unavailable, interior_level_invalid, interior_hidden, interior_transform_unsupported, interior_target_elsewhere; on the hosted server editor_tab_required, editor_tab_hidden, editor_tab_busy (a tab open but busy or still loading, no link: wait a few seconds and call again, do not ask the user), editor_tab_timeout (a tab claimed the view and then stopped answering); what the refusal says about opening the editor follows the person's "Agents may open the editor for me" setting (ask first, allow, never) |
search_assets | Search the host's item library (the built-in list on a standalone server), several queries in one call; a query matches name, id, category or tags. A query that finds nothing gets a hint to build the item with add_object. | { queries: [{ query, category? }] } | { groups: [{ query, total, results[] }], total, hint? }; refusal no_catalog |
design_brief | The request read as a brief, once for the whole request: axes (the request read axis by axis: action, size, pieces, openings, style, materials, dressing, each bounded — the person's own words as quote, the reading in a short line, pieces as a minimum or exact — or open with a design statement of at least three words; an absent axis is refused axis_missing, a quote not in the person's message in the chat axis_quote_not_in_request, an open axis without its design axis_open_without_design; over MCP an axis quote is required and not compared, there being no message), the eighth axis source (open, catalog, brand with its brand, existing), a vision for what is open, the dressing of the rooms layer by layer (beyond: textiles, light, windows, walls, floor, plants, objects, small furniture, each pieces or { none: why }; an open dressing axis owes 6 of the 8, beyond_too_thin, a bounded one owes none, and the named pieces read as a minimum do not bound it, dressing_bounded_by_minimum; handed back as dressing), the person's words that set a source other than open (sourceQuote, required: at least two words naming the brand, the catalog or what is already there; not compared with a request here) and, with an open source, technical equipment placed as it is (asIs: sanitaryware, appliances, fixed services; each piece shown by view_assets first, each with a reason; furniture is made). Placing or making furnishings follows a brief: place_items, furnish_room, place_in_room (the catalog's) and add_object, place_design (made), inside run_batch too, are refused before one with design_brief_required.search_assets and view_assets stay open. Source open opens the made pieces, places technical equipment only when named in asIs and keeps furnish_room closed (brief_source_open); catalog, brand and existing open the catalog's tools, and creation for what it lacks. A batch holding a refused call is refused whole. The brief is the session's: a scene load or clear starts another, and the next design_brief replaces it. Changes nothing in the scene. | | |
view_assets | Pictures of 1 to 6 catalog items by id, each labelled with its size, category and the names of its paintable surfaces (its model's slot_<id> materials; paint takes one as role), returned as image content. The picture and surfaces are read before the call (a hosted server fetches and resizes the thumbnail to at most 512 px; a standalone server's built-in list has no host to fetch from, so it answers sizes and says it has no picture). Opt-in: nothing changes when it is not called. | { ids: string[] (1 to 6) } | { items: [{ id, name, category, dimensions, surfaces, picture, unavailable? }], shown, note? } plus one labelled image per item shown; refusals unknown_asset, no_catalog |
search_materials | Search or browse the material library, several queries in one call: words match as the library writes them (timber is wood, render is stucco), shades and surfaces; a query without words lists a kind, a surface or the whole library by name. A search lists 12 at a time and a browse 150, a long one saying how many more (more) and the offset that lists them (next). Reads Pascal's library and the materials the host registered (the editor registers its library; this server registers none). Read-only. | { queries: [{ query?, kind?, surface?, offset? }] } | { groups: [{ query?, kind?, surface?, offset?, results: [{ ref, label, kind, surfaces, color? }], missing?, more?, next? }] } |
paint | Give walls, slabs, ceilings, roofs and every other part with surfaces (columns, fences, doors, windows, stairs, blocks, cabinets, shelves…) a finish in one call, as the editor's Paint does: a colour (the nearest library colour), a library: or scene: ref, a native material kept as a scene material (reused when an equal one exists), or erase. role picks the surface (a wall's exterior/interior faces, by its rooms or else its stored sides, refused when rooms stand on only part of a face, a slot by name, a roof's top/edge/wall, or its fascia, the fascia and rake boards, held by every segment; a glazed roof's glazing-frame, glazing-glass or glazing-solid, and without a role its frame and solid panels, the glass kept); a roof shows one finish per surface, so a segment's finish goes on its roof and a roof's clears its segments' own; a wall face is painted whole, over its room's wall finish, its painted parts keeping theirs (named in note). | { targets[], role?, color?, material?, materialName?, erase? } (one finish) | { ok, painted[], finish, materialId?, material?, createdMaterial?, note?, achieved }; refusals target_not_found, unknown_role, side_unknown, unknown_material, materials_unavailable, not_paintable |
create_story_shell | Create one level-owned story shell from a footprint: the perimeter walls. The floor plate and ceiling are derived from the enclosed rooms; createSlab / createCeiling / slabElevation are recorded as room intent. Use once per story. | { levelId, footprint, wallHeight?, wallThickness?, createSlab?, createCeiling? } | { wallIds, zoneIds, slabId, ceilingId, createdIds } |
create_stair | Create a straight, L or U stair as the editor's stair tool does: from a level to the next one above (made when there is none), owning the floor openings it cuts, or to no level (toLevelId: null).(x, z) is the middle of the bottom riser, rotation in degrees (0 climbs toward +Z). | { x, z, levelId?, toLevelId?, rotation?, layout?, turn?, lowerSteps?, width?, length?, height?, steps?, railingMode?, railingStyle?, railingMaterial? } | { stairId, segmentIds, upperLevelId, createdUpperLevel, layout, stepCount, rise, length, footprint, arrival, slabHoleCut, openingIds? }; refusals roof_level, not_above, level_not_found, slab_not_found / ceiling_not_found, slab_not_on_level / ceiling_not_on_level, stair_layout, stair_layout_required, no_level_above |
measure_stair | Read actual risers, going at the walking line, slope, uniformity, arrival, headroom against floors/ceilings/stair bodies, and design-target diagnostics. | { stairId, available?: { width, length } } | { measurements, layouts } |
fit_stair | Fit uniform risers or replace the chain with a straight/L/U preset with width-sized landings or quarter-turn winders in one undoable edit. | { stairId, fitRun?, targets?, layout?, turn?, width?, landingDepth?, turningStrategy?, innerGap?, walkingLineOffset?, division? } | { stairId, measurements } |
create_roof | Cap the top storey with a roof over its footprint (its walls' outline, else its floor plates, else its rooms), one segment per rectangle, or over a rectangle of your own (width, depth, center). The roof goes on the building's roof level above the storey (reused, the empty level directly above when it is the only one, else made and named Roof) and stands on the storey's wall tops; the hand roof tool follows the same rule. roofType: "barrel" arches each segment (depth is the span, barrelRise at most half of it, barrelSegments facets from 4 to 32); roofSystem: "glazed" builds a gable, straight shed or barrel roof as panes and framing, set by glazing (see glazed roofs). | { levelId? (or level), roofType?, roofSystem? ('standard' | 'glazed'), glazing?, barrelRise?, barrelSegments?, pitch? (degrees, or rise over run: "6:12"), overhang? (the eaves' reach past the walls in plan), wallHeight?, wallThickness?, width?, depth?, center?, materialPreset?, fasciaHeight?, fasciaMaterialPreset?, name? } | { roofId, roofLevelId, createdRoofLevelId, coveredLevelId, segmentIds, roofType, achieved }; refusals storey_above, roof_exists, building_required (several buildings, no level named), storey_without_building, conflicting_level, no_levels, no_rooms, rectangle_incomplete, unknown_material (the roof's or the fascia's finish) |
move_roofs_to_roof_level | Move the roofs a storey holds on its own walls to the level the roof rule gives them (the building's roof level over the top storey, made when there is none; the storey above a lower one), as a roof's panel does by hand. Each roof stands where it stood, segments, finishes and id kept. For projects built before the roof level, when verify_scene reports roof_mixed_in_storey. | { levelId } | { roofIds, roofLevelId, createdRoofLevelId, achieved }; refusals level_not_found, no_roof_on_walls |
create_room | Create a room from a polygon: one wall per edge (an edge a wall already runs along reuses it) and the room zone; its doors and windows can be declared in the same call by edge (wallIndex) and t, each placed with add_door's and add_window's rules or listed in skippedOpenings. The floor plate and ceiling are derived, never authored. outdoor: true makes a terrace: separators (the editor's Separator: a room boundary with no wall) where no wall runs, no walls of its own, no ceiling. Where every edge of the polygon already runs along a wall (nothing to build) and the polygon fits the room those walls make, the call names that room instead of stacking a second zone on it, and zoneId is that room's. If that room has a name of its own (not a number the editor gave it: any name but the editor's "Room N", whoever gave it) the call is refused (room_named_by_person, worded with the room's name): pass rename: true only when the person asked for the rename; otherwise ask them. A room is never renamed unasked, and the same name over it says there is nothing to create. A terrace (outdoor: true) drawn over a fully walled courtyard names it outdoor. The refusal is agent-only: the editor's own room tool behaves as before (a person sees and names rooms directly). | { name, polygon, levelId?, color?, wallHeight?, wallThickness?, outdoor?, rename?, doors?, windows? } | { zoneId, slabId, ceilingId, wallIds, reusedWalls, areaSqMeters, doorIds, windowIds, skippedOpenings? }; refusals cannot_enclose, outdoor_room_overlap, room_named_by_person |
add_door | Add a door to a straight wall at t (0..1 along it) with the editor's placement rules, or a passage with no leaf (openingKind: "opening"); it slides to stay on the wall and reports clamped.doorType, style and the outline (openingShape) match a reference, code builds what they cannot, and nodeId rebuilds a scripted door. | { wallId, t?, width?, height?, openingKind?, hingesSide?, swingDirection?, doorType?, style?, openingShape?, code?, nodeId?, force? } | { doorId, wallId, localX, t, clamped, achieved }; refusals wall_not_found, not_a_wall, curved_wall, wall_too_short, opening_overlap (unless force) |
add_window | Add a window as add_door places a door, centred vertically on the wall unless sillHeight (its bottom edge above the floor) is given; windowType, columns / rows, style (a Fixed window's panes; refused on another type, style_needs_fixed_window) and the outline match a reference. | { wallId, t?, width?, height?, sillHeight?, windowType?, columns?, rows?, style?, openingShape?, code?, nodeId?, force? } | { windowId, wallId, localX, t, clamped, sillHeight, achieved }; the same refusals as add_door |
furnish_room | Furnish a room for its type from the host's item catalog: the main piece against the wall facing the door, the rest along a side wall. 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. | { zoneId?, polygon?, levelId?, roomType, doorWallIndex? } | { placed, itemIds, skipped, doorWallIndex, doorsDetected }; refusals room_required, roof_level, level_not_found, no_catalog |
add_corner_window | A window wrapping the corner where two walls meet: one window per wall, both running to the corner and joined, the glass fused (a fixed window) or at a thin post (the other window types, whose frames stay), at any angle from 10° to 170° (a square corner, a bay's 135°, an acute one). corner is the point the walls of levelId end at (default the floor in view: stacked storeys share their corners' points), [x, z] in the level's plan as get_walls gives their ends; width is how far the glass runs along each wall from the corner (default 1.2 m) and widths overrides it per wall id; post is 'none' (the glass fused, a fixed window's default) or 'post' (the other types' default and only choice); height defaults to 1.5 m and the window is centred on the wall unless sillHeight (its bottom edge above the floor) is given; wallIds (two) is needed only when more than two walls end at the corner; windowType and style are add_window's (a style needs a Fixed window). By hand: select a window that ends at a corner and use the window panel's Corner section (Wrap the corner, This side and Other side, Fused glass or Corner post for a fixed window, Unwrap). | { corner: [x, z], levelId? (or level), wallIds?, width?, widths?, height?, sillHeight?, post?, windowType?, style? } | { windowIds, wallIds, angle, post, achieved }; refusals no_corner (no two walls end there), corner_ambiguous (more than two do: pass wallIds), corner_angle (under 10° or over 170°: use add_window), corner_not_fused (post: 'none' on a type other than fixed), width_exceeds_wall, style_needs_fixed_window |
merge_windows | Make two windows that exist one window, as the editor's Merge windows button does; nothing is recreated. On two walls that meet at a corner, each window within 0.30 m of it, they become one corner window (the add_corner_window result): each is slid to the corner keeping its width, the glass fused (a fixed window's; other types meet at a post), 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°. On one wall, two windows with at most 0.30 m between them and nothing between become one window spanning both (overlapping or one inside the other, their union), from the lower sill to the higher head, with the first window's type and style. Use it for two windows already built that open a corner or one wide opening (verify_scene's corner_unjoined names a pair for the first); add_corner_window builds a new pair. By hand: select two windows and use Merge windows in the multi-selection panel, or place or drag a window near a corner where one waits and click or release (Alt places it plain). | { windowIds: [a, b] } | { kind: 'corner' | 'same_wall', windowIds, removedIds, achieved }; refusals window_not_found, not_a_window, same_window, window_not_on_wall, window_already_joined (unwrap it first), windows_already_joined, windows_not_adjacent, opening_between, walls_not_meeting, corner_angle, window_not_at_corner, height_exceeds_wall, opening_overlap |
apply_patch | Batched create/update/delete/move, validated and dry-run before commit. Batch-first is the default: send all create/update/delete ops for a build step in one atomic call (stable order, later ops may reference earlier created ids); do not loop one-op calls. A create with an id already in the scene is refused (node_exists); delete it earlier in the same patch to replace it. Updates cannot change id or type (identity_change), object or children (immutable_field), move a node under a missing or childless parent (invalid_parent), or add schema issues (invalid_update); default gutters and downspouts a delete regenerates are addressable only in a later call (regenerated_default). Refusals are tool errors with JSON text { code, patchIndex, id, message }. A level created without a height, or an update that clears it, gets the height the editor's load derives from what the level holds (an empty level: 2.5 m), one line per level in notes. A roof the patch makes or moves onto the storey whose walls carry it is seated on the level above those walls, one line per roof in notes; a new storey at or above the roof level moves it up. Refused: storey content on a roof level (roof_level), declaring a level that holds storey content the roof level (roof_level_holds_storey), floor indexes that put the roof level under a storey (roof_level_on_top). | { patches: Patch[] } | { applied: number, notes?: string[] } |
add_level | Add an empty level to a building, as the editor does: on top of its storeys, under its roof level (which moves up one), or below the lowest for a basement (every existing floor, the ground floor included, keeps its place). Omit buildingId for the scene's only building. | { buildingId?, position?, name?, height? } | { levelId, buildingId, floorIndex, height, shiftedLevelIds? }; refusals building_required, building_not_found, no_building |
set_ground_level | Make a level the building's ground floor, as the level panel's "Set as ground floor": the building names it, is seated so that floor stands over grade as the building's foundation construction seats it, the top of the foundation (8 in, 0.203 m, on block: the default and the US; 0.15 m on cast concrete in Europe; height only, the plan stays), and the floor indexes are renumbered so it is 0 and the levels under it are the basements. | { levelId } | { levelId, buildingId, floorElevation, aboveGrade, foundation { region, construction, basis }, movedBuildingBy, renumbered, unchanged? }; refusals level_not_found, not_a_level, no_building, not_a_storey |
add_wall | Add a wall to a level, straight or, with curveOffset, an arc. Omit levelId for the lowest storey. | { start, end, levelId?, thickness?, height?, curveOffset? } | { wallId, levelId, length }; refusals roof_level, wall_too_short, level_not_found |
add_fence | Add a fence along points on a level, one fence per side; closed runs it back to the first point (a lot's boundary). Privacy panels unless style says otherwise. | { points, closed?, height?, style?, baseStyle?, materialPreset?, levelId? } | { count, fenceIds, levelId, length, achieved }; refusals fence_too_short, roof_level, unknown_material, level_not_found |
place_items | Place catalog items in one call, each placed or refused on its own, at level (x, z): on the level's floor, or on the host each names (targetNodeId): a wall (y, the height of its bottom, required), a ceiling, or an item standing on the floor. A floor item in a door's clearance is refused blocks_door (naming the door and a spot that clears every door), one its room cannot hold in any turn too_large_for_room. | { items: [{ assetId, x, z, y?, targetNodeId?, rotation? }], levelId? } | { levelId, items: [{ ok, itemId?, hostId?, restingOn?, code? }] }; per-item refusals asset_not_found, outside_rooms, blocks_door, too_large_for_room, height_required, item_too_tall, unsupported_host, host_not_found, host_not_on_level |
place_design | Create one design (procedural item recipe, object or JSON string) that passes validate_design. Its mounting picks the host: level/slab/zone or a design surface (surfaceId), a straight wall face, or a ceiling. Create-only, one undo step, inline designs up to 24 KiB, with coded refusals. | { design, hostId, position, rotation?, side?, surfaceId?, parameters?, slots?, name?, id? } | { designId, parentId, surfaceId } |
set_zone | Create a zone/room polygon on a level; over walls that already enclose a room, it is that room, never a second zone: one still named "Room N", or already named label, takes the label; one a person named otherwise is refused. | { levelId, polygon, label, properties? } | { zoneId } |
duplicate_level | Copy a level as the editor does (units whose rooms are all on it included; plan references, scans and spawns left behind), above or below, shifting the floors past it. | { levelId, position?, name?, preset? } | { newLevelId, name, floorIndex, shiftedLevelIds, copied, skipped, newNodeIds[] } |
delete_node | Delete a node with everything under it, as the editor's Delete does. | { id } | { deletedIds: [] } |
clear_scene | Empty the project on purpose, back to the default site, building and level. The only way to empty a project: a write that would leave it empty is refused (scene_wipe_blocked) by a store that guards against accidental wipes, and the session keeps the project as stored. | { reason } | { cleared: { removed }, version, graphHash } |
undo | Step back through temporal history. | { steps? } | { undone: number } |
redo | Step forward through temporal history. | { steps? } | { redone: number } |
export_json | Serialize the scene graph as JSON. | { pretty? } | { json: string } |
export_glb | Stubbed: GLB export requires the browser renderer. | — | throws not_implemented |
validate_design | Read-only: validate a design (procedural item recipe, object or JSON string) and measure it. The authority over pascal://schema/design. | { design, parameters? } | { valid, diagnostics[], design, sweep, measurements } |
verify_scene | The one scene check: schema validity, per-level counts, roles and the spaces the walls enclose, empty levels, and typed practical issues (every node field its schema rejects as schema_invalid with its nodeId and path, a node kind no schema knows once per kind, a space with no room or with several, a room the walls do not close or with no name, storeys with no stair, openings off their wall, stairs off their slab, blocked doors, a house past its lot line or in its required yards…). | — | { valid, levels[], issues: { type, message, … }[], hasIssues } |
check_collisions | Find overlapping items and out-of-bounds placements. | { levelId? } | { collisions: { aId, bId, kind }[] } |
analyze_floorplan_image | Vision tool: extract walls, rooms, and approximate dimensions from a floorplan image. | { image, scaleHint? } | { walls, rooms, approximateDimensions, confidence } |
analyze_room_photo | Vision tool: extract approximate dimensions and fixtures from a room photo. | { image } | { approximateDimensions, identifiedFixtures, identifiedWindows } |