{"$schema":"https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json","version":"1.0","protocolVersion":"2025-11-25","serverInfo":{"name":"pascal-editor","title":"Pascal Editor MCP","version":"0.6.1"},"description":"Create, inspect, validate, and save editable 3D building scenes with Pascal's hosted MCP server.","iconUrl":"https://editor.pascal.app/icons/icon-512.png","documentationUrl":"https://editor.pascal.app/docs/developers/mcp","transport":{"type":"streamable-http","endpoint":"https://editor.pascal.app/api/mcp"},"capabilities":{"tools":{"listChanged":true},"resources":{"listChanged":true},"prompts":{"listChanged":true}},"authentication":{"required":true,"schemes":["bearer"]},"tools":[{"name":"get_scene","title":"Get scene","description":"Returns the full scene graph: flat node dictionary, root node IDs, collections, and explicit installed plugin IDs when present.","inputSchema":{"type":"object","properties":{},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"describe_node","title":"Describe node","description":"Return a structured summary of a node including its ancestry, children IDs, key properties, and a short human description.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"find_nodes","title":"Find nodes","description":"Find nodes matching any combination of type, parentId, levelId, zoneId, sourceId or sourceIdPrefix filters. sourceId / sourceIdPrefix match the ids an importer recorded for the source elements: provenance.refs[].id, or legacy metadata.sourceIds.","inputSchema":{"type":"object","properties":{"type":{"description":"Node kind: a core kind (site, building, elevator, unit, level, lean-to-extension, column, construction-dimension, block, structural-grid, wall, separator, floor-opening, fence, fence-gate, fence-opening, cabinet, cabinet-module, item, procedural-item, imported-mesh, zone, slab, ceiling, roof, roof-segment, shelf, stair, stair-segment, scan, guide, measurement, spawn, window, door, box-vent, ridge-vent, turbine-vent, cupola, eyebrow-vent, gutter, chimney, solar-panel, skylight, dormer, downspout, duct-segment, duct-fitting, duct-terminal, hvac-equipment, lineset, liquid-line, pipe-segment, pipe-fitting, pipe-trap) or a plugin kind such as \"fixture:bench\".","type":"string","minLength":1},"parentId":{"type":"string","minLength":1},"levelId":{"type":"string","minLength":1},"zoneId":{"type":"string","minLength":1},"sourceId":{"description":"Exact match on any import source id of the node: provenance.refs[].id or legacy metadata.sourceIds. Characters outside printable ASCII also match their percent-encoded form.","type":"string","minLength":1},"sourceIdPrefix":{"description":"Prefix match on any import source id of the node (provenance.refs[].id or legacy metadata.sourceIds).","type":"string","minLength":1}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"measure_stair","title":"Measure stair","description":"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.","inputSchema":{"type":"object","properties":{"stairId":{"type":"string","minLength":1},"available":{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"length":{"type":"number","exclusiveMinimum":0}},"required":["width","length"]}},"required":["stairId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"fit_stair","title":"Fit stair","description":"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.","inputSchema":{"type":"object","properties":{"stairId":{"type":"string","minLength":1},"fitRun":{"default":false,"type":"boolean"},"layout":{"type":"string","enum":["straight","l","u"]},"turn":{"type":"string","enum":["left","right"]},"width":{"type":"number","exclusiveMinimum":0},"landingDepth":{"type":"number","exclusiveMinimum":0},"turningStrategy":{"type":"string","enum":["landing","winder"]},"innerGap":{"type":"number","minimum":0},"walkingLineOffset":{"type":"number","exclusiveMinimum":0},"division":{"type":"string","enum":["equal-going","equal-angle"]},"targets":{"type":"object","properties":{"maxRiserHeight":{"default":0.18,"type":"number","exclusiveMinimum":0},"minimumGoing":{"default":0.25,"type":"number","exclusiveMinimum":0},"targetGoing":{"default":0.28,"type":"number","exclusiveMinimum":0},"minimumHeadroom":{"default":2,"type":"number","exclusiveMinimum":0}}}},"required":["stairId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"find_by_type","title":"Find by type","description":"Find everything of one type in the scene, wherever it lives: nodes of that kind (column, door, window, wall, stair…), 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). 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.","inputSchema":{"type":"object","properties":{"type":{"type":"string","minLength":1,"description":"What to find, one word: light, column, door, window, beam, panel, railing, trim…"},"levelId":{"description":"Only this level. Default: every level.","type":"string","minLength":1}},"required":["type"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"list_levels","title":"List levels","description":"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, and which one the person is viewing (isActive). A roof-only level is the roof, not a storey.","inputSchema":{"type":"object","properties":{},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"get_node","title":"Get node","description":"Get the full data of any node by its id.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"The node id."}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"get_level_summary","title":"Get level summary","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"description":"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.","type":"string","minLength":1},"level":{"description":"Same as levelId.","type":"string","minLength":1}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"get_walls","title":"Get walls","description":"Get the walls of a level: start and end in metres, length, stored and resolved height, thickness, and their doors and windows.","inputSchema":{"type":"object","properties":{"levelId":{"description":"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.","type":"string","minLength":1},"level":{"description":"Same as levelId.","type":"string","minLength":1}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"get_zones","title":"Get zones","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"description":"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.","type":"string","minLength":1},"level":{"description":"Same as levelId.","type":"string","minLength":1}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"duplicate_level","title":"Duplicate level","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"description":"The level to copy, by an id list_levels returned. Default: the floor the person is viewing.","type":"string","minLength":1},"position":{"description":"Where the copy goes: above the original (default) or below it.","type":"string","enum":["above","below"]},"name":{"description":"Name of the copy. Default: the original name.","type":"string","minLength":1,"maxLength":120},"preset":{"description":"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 furniture.","type":"string","enum":["everything","structure","structure-materials","structure-furniture"]}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"verify_scene","title":"Verify scene","description":"Check the whole scene after complex edits, and before retrying a failed tool: per-level content and roles (storey, roof-only, support), then every problem found, each with a type: empty levels, walls with no room or door, rooms with no floor or ceiling, storeys with no stair, roof levels misused, openings off their wall, stairs off their slab or blocked, furniture blocking a door or overlapping, nodes their schema rejects.","inputSchema":{"type":"object","properties":{},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"delete_node","title":"Delete node","description":"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.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"The node id."}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"list_collections","title":"List collections","description":"List the scene's collections with their elements (id, name, type, level), and the templates a collection can start from.","inputSchema":{"type":"object","properties":{"collectionId":{"description":"Only this collection.","type":"string","minLength":1}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"edit_collection","title":"Edit collection","description":"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.","inputSchema":{"type":"object","properties":{"collectionId":{"description":"The collection to change.","type":"string","minLength":1},"template":{"description":"Start from a template, which gives the name and colour unless you pass them.","type":"string","enum":["lights","windows","doors","electrical","plumbing","framing","masonry","roof","cabinetry","kitchen-cabinetry"]},"name":{"description":"Name of the collection, or its new name.","type":"string","minLength":1},"color":{"description":"Colour, e.g. #f5b83d.","type":"string"},"add":{"description":"Ids of the elements to add.","type":"array","items":{"type":"string","minLength":1}},"remove":{"description":"Ids of the elements to take out.","type":"array","items":{"type":"string","minLength":1}},"delete":{"description":"Delete the collection; its elements stay in the scene.","type":"boolean"}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"add_column","title":"Add column","description":"Add a structural or decorative column at (x, z), or rebuild one by nodeId. Native fields first; optional code uses 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.","inputSchema":{"type":"object","properties":{"x":{"description":"X position of the column center (meters)","type":"number"},"z":{"description":"Z position of the column center (meters)","type":"number"},"height":{"description":"Column height (default: 2.5). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"radius":{"description":"Radius when crossSection is \"round\" (default: 0.22). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"width":{"description":"Width when crossSection is \"square\" / \"rectangular\" (default: 0.44). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"depth":{"description":"Depth when crossSection is \"square\" / \"rectangular\" (default: 0.44). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"rotation":{"description":"Y-axis rotation (default: 0). Accepts a number (degrees) or a natural-language string; other units are converted. e.g. 45, \"45°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]},"style":{"description":"Visual style: 'plain' (default smooth pillar), 'faceted' (sharp prism faces), 'fluted' (vertical channels — classical), 'lathe-turned' (turned bands — porch / interior), 'dravidian-carved' (heavy carving — temple), 'cluster' (bundled colonnette — gothic).","type":"string","enum":["plain","faceted","fluted","lathe-turned","dravidian-carved","cluster"]},"crossSection":{"description":"Plan shape: 'round' (default), 'square' (equal width/depth), 'rectangular' (independent width/depth), 'octagonal', 'sixteen-sided'.","type":"string","enum":["round","square","rectangular","octagonal","sixteen-sided"]},"level":{"description":"Level id from list_levels; omitted uses the viewed or lowest level.","type":"string"},"y":{"description":"Support point height above the level, in metres (default 0).","type":"number"},"name":{"type":"string"},"nodeId":{"description":"Rebuild this column with code, params or native size fields; get_source first when changing code.","type":"string","minLength":1},"code":{"description":"A three.js module 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 dimensions.","type":"string","minLength":1,"maxLength":48000},"params":{"description":"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.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","boolean","string"]}}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"add_object","title":"Build object","description":"Build an object by writing a plain three.js module, the way you would in any three.js project. Use it for what the catalog and the structure tools cannot reproduce faithfully: custom columns and capitals, mouldings and trim, panels, lanterns and fixtures, exposed beams, vaulted or tray ceiling bodies, canopies, a porch, railings, built-ins. 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.\n\nModule shape (import THREE from the three package as usual; the addons below too):\n  // also available: three/addons/utils/BufferGeometryUtils.js, three/addons/geometries/{RoundedBoxGeometry,ConvexGeometry,LoftGeometry,ParametricGeometry}.js, three-bvh-csg (Brush, Evaluator, SUBTRACTION, ADDITION, INTERSECTION)\n  export const params = { width: { default: 4.8, min: 3, max: 8, step: 0.1, unit: 'm', label: 'Width' } }\n  export const mount = 'floor'   // 'floor' | 'wall-side' (on a wall face) | 'wall' (through a wall, like a window) | 'ceiling'\n  export default function build({ params, THREE }) { const group = new THREE.Group(); /* … */ return group }\n\nOne object is one feature that changes together: 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.\n\nConventions (they make the object work in Pascal; follow them):\n- Metres, Y up, modelled as it stands. Pascal puts the bottom-centre of the bounds at the placement point; for wall-side the back face sits on the wall and the object faces +Z.\n- Paint: name every material slot_<finish> (slot_trim, slot_frame, slot_metal) and reuse one material per finish; a material named \"glass\" renders as glass.\n- Parts: name the few groups a person would point at part:<id> (part:column_left, part:canopy, part:landing), usually 2–24, a group per part, not every mesh. Set userData.type on parts someone would look for: column, beam, slab, roof, railing, panel, trim, step, light.\n- Lights: add a THREE.PointLight or SpotLight named light:<id> where the bulb is; it becomes a switchable light.\n- Motion: put THREE.AnimationClips on the returned group's .animations, as in any three.js project; tracks target <objectName>.<property> or <object.uuid>.<property> and may use any transform property (position, rotation, rotation[y], quaternion, scale); material and visibility tracks do not animate. A clip named open becomes the object's open/close control (close plays a clip named close, or open reversed); a clip named loop runs continuously; every other clip gets its own play toggle labelled with its name (name it for the person: \"Twirl\", \"Music\"). Write as many clips as the object needs.\n- Cuts: a mesh named cutout cuts only the mounted host; cut:wall, cut:ceiling and cut:slab require that host kind (floor items cut their support slab). The hidden cutter must reach the host; walls use its real shape, ceilings and slabs its through-hole footprint.\n- Sockets: an empty Object3D named anchor:<id> marks where other things attach.\n- No textures, network or DOM. At most 300k triangles, 32 materials, 60 m per side.\n\nEdit: pass nodeId with new code and/or params (params alone rebuild the stored script; read it first with get_source to change the code); identity, placement, paint and the params you leave out are kept. The result lists the size, parts, slots, lights, animations and params.","inputSchema":{"type":"object","properties":{"code":{"description":"The three.js module (see the tool description). Required to create; omit to rebuild an object with new params.","type":"string","minLength":1,"maxLength":48000},"params":{"description":"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.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","boolean","string"]}},"nodeId":{"description":"Edit this existing authored object instead of creating one.","type":"string","minLength":1},"parentId":{"description":"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.","type":"string","minLength":1},"position":{"description":"Placement in the host frame, metres. Level: [x, y, z] (y = 0 on the floor). Wall: [distance along the wall from its start, height of the bottom, 0].","minItems":3,"maxItems":3,"type":"array","items":{"type":"number"}},"rotation":{"description":"Y-axis rotation (default: 0). Accepts a number (degrees) or a natural-language string; other units are converted. e.g. 45, \"45°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]},"side":{"description":"Which wall face a wall-side object sits on.","type":"string","enum":["front","back"]},"name":{"description":"What the user would call it (\"Front porch\").","type":"string","maxLength":120},"category":{"description":"What it is, one word or two (\"porch\", \"lantern\", \"ceiling\", \"trim\").","type":"string","maxLength":60}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"get_source","title":"Read object script","description":"The three.js module an object (or a window, door or column built from code) runs, with its params and their current values. Read it before changing its code, then pass the edited module back with the same nodeId: to add_object for an object, to add_window / add_door / add_column for their respective kinds.","inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","minLength":1,"description":"An object, window, door or column built from code."}},"required":["nodeId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"measure","title":"Measure","description":"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.","inputSchema":{"type":"object","properties":{"fromId":{"type":"string","minLength":1},"toId":{"type":"string","minLength":1}},"required":["fromId","toId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"create_story_shell","title":"Create story shell","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"footprint":{"minItems":3,"type":"array","items":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}}},"wallHeight":{"description":"Explicit wall height override. Omit for level-plane-bound walls. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"wallThickness":{"description":"Wall thickness. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"createSlab":{"default":true,"description":"Floor intent for the rooms inside the shell. False records hasFloor: false.","type":"boolean"},"createCeiling":{"default":true,"description":"Ceiling intent for the rooms inside the shell. False records hasCeiling: false.","type":"boolean"},"slabElevation":{"description":"Walking surface of the derived plate, stored as the room's floor elevation. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\".","type":["number","string"]},"ceilingHeight":{"description":"Ceiling height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"namePrefix":{"type":"string"},"wallMaterialPreset":{"type":"string"},"slabMaterialPreset":{"type":"string"},"ceilingMaterialPreset":{"type":"string"}},"required":["levelId","footprint"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"create_roof","title":"Create roof","description":"Create a roof container with one roof segment. By default creates a dedicated roof level above the reference level so exploded/solo level views can isolate the roof.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"roofLevelId":{"type":"string","minLength":1},"useDedicatedRoofLevel":{"default":true,"type":"boolean"},"roofLevelLabel":{"default":"Roof","type":"string"},"roofLevelElevation":{"type":"number"},"roofLevelHeight":{"description":"Roof level height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"center":{"minItems":3,"maxItems":3,"type":"array","items":{"type":"number"}},"width":{"description":"Roof width. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"depth":{"description":"Roof depth. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"roofType":{"default":"hip","type":"string","enum":["hip","gable","shed","gambrel","dutch","mansard","flat","conical"]},"pitch":{"description":"Roof pitch. Accepts a number (degrees) or a natural-language string; other units are converted. e.g. 45, \"45°\", \"1.57rad\", \"0.25 turn\". Range 0–85 deg.","type":["number","string"]},"wallHeight":{"description":"Knee-wall height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Minimum 0 m.","type":["number","string"]},"wallThickness":{"description":"Wall thickness. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"overhang":{"description":"Eave overhang. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Minimum 0 m.","type":["number","string"]},"materialPreset":{"type":"string"},"name":{"type":"string"}},"required":["levelId","width","depth"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"create_stair_between_levels","title":"Create stair between levels","description":"Create a straight stair and a persistent floor-opening node for its destination floor and the ceiling directly below. This disables stair auto-opening mode to avoid duplicate cuts.","inputSchema":{"type":"object","properties":{"fromLevelId":{"type":"string","minLength":1},"toLevelId":{"type":"string","minLength":1},"position":{"minItems":3,"maxItems":3,"type":"array","items":{"type":"number"}},"rotation":{"description":"Y-axis rotation. Accepts a number (radians) or a natural-language string; other units are converted. e.g. 1.5708, \"90°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]},"width":{"description":"Stair width. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"runLength":{"description":"Horizontal run length; omitted derives from shared stair design targets. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"totalRise":{"description":"Total vertical rise. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"stepCount":{"type":"integer","minimum":2,"maximum":9007199254740991},"railingMode":{"default":"both","type":"string","enum":["none","left","right","both"]},"destinationSlabId":{"type":"string","minLength":1},"sourceCeilingId":{"type":"string","minLength":1},"createDestinationSlabOpening":{"default":true,"type":"boolean"},"createSourceCeilingOpening":{"default":true,"type":"boolean"},"openingWidth":{"description":"Floor opening width. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"openingLength":{"description":"Floor opening length. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"openingOffset":{"description":"Opening offset. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Minimum 0 m.","type":["number","string"]},"openingCenter":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}},"openingRotation":{"description":"Opening rotation. Accepts a number (radians) or a natural-language string; other units are converted. e.g. 1.5708, \"90°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]},"materialPreset":{"type":"string"},"name":{"type":"string"}},"required":["fromLevelId","toLevelId","position"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"search_assets","title":"Search assets","description":"Search the built-in MCP item catalog by keyword. Call before place_item when you need a valid catalogItemId.","inputSchema":{"type":"object","properties":{"query":{"type":"string","minLength":1},"category":{"type":"string"}},"required":["query"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"create_room","title":"Create room","description":"Create a room on a level from a polygon: one wall per edge (reusing or splitting the walls already there) plus 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 a room, and never pass boundary/autoFromWalls. Returns wallIds in polygon edge order and the derived slabId / ceilingId. outdoor: true draws a terrace instead: separators where no wall runs and no ceiling (wallIds are null for its own sides).","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"polygon":{"minItems":3,"type":"array","items":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}}},"color":{"type":"string"},"wallHeight":{"description":"Wall height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"wallThickness":{"description":"Wall thickness. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"outdoor":{"description":"true for an outdoor room (a terrace): closed with separators where no wall runs, no walls of its own and no ceiling.","type":"boolean"}},"required":["levelId","name","polygon"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"add_door","title":"Add door","description":"Add a door to an existing straight wall at t (0..1 along it). 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.","inputSchema":{"type":"object","properties":{"wallId":{"description":"The wall to add the door to.","type":"string","minLength":1},"nodeId":{"description":"Rebuild this door instead of adding one: new code and/or params (get_source first to change its code). Its native fields change with update_node.","type":"string","minLength":1},"t":{"description":"Position along the wall, 0..1: 0 = start, 0.5 = centre, 1 = end.","type":"number","minimum":0,"maximum":1},"position":{"description":"Same as t.","type":"number","minimum":0,"maximum":1},"force":{"description":"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.","type":"boolean"},"width":{"description":"Door width (default 0.9 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"height":{"description":"Door height (default 2.1 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"hingesSide":{"description":"Hinge side (default left).","type":"string","enum":["left","right"]},"swingDirection":{"description":"Which way the door opens (default inward).","type":"string","enum":["inward","outward"]},"openingShape":{"description":"Outline of the opening: rectangle (default), rounded top corners, or arch (round top).","type":"string","enum":["rectangle","rounded","arch"]},"archHeight":{"description":"Rise of the arch above its straight sides, within height (arch only; default 0.45 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"cornerRadius":{"description":"Radius of the rounded top corners (rounded only; default 0.15 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"code":{"description":"A three.js module for a door the fields cannot express (a fan grille, tracery, carved trim): the same module and conventions as add_object, with mount 'wall'. Its size is what it builds (name params width and height so the door's size controls edit them), and its cutout mesh cuts the wall. Make it open like a real door: add an `open` clip (the leaves swinging or sliding; `close` is optional, `open` reversed by default) unless it is fixed. Fields first; code only beyond them.","type":"string","minLength":1,"maxLength":48000},"params":{"description":"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.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","boolean","string"]}},"doorType":{"description":"How it opens (default hinged); garage types for garage doors.","type":"string","enum":["hinged","double","french","folding","pocket","barn","sliding","garage-sectional","garage-rollup","garage-tiltup"]},"style":{"description":"Visual preset (panels only, never the size); same presets as create_room doors[].","type":"string","enum":["panel","glass","modern","paneled-glass","french","shaker","six-panel","craftsman","half-louvered","barn"]}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"add_window","title":"Add window","description":"Add a window to an existing straight wall at t (0..1 along it), on sillHeight above the floor. 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, and overlapping another door, window or wall item unless force is set. Match the reference with the outline (rectangle, rounded, arch), windowType and panes (columns × rows, or a style).","inputSchema":{"type":"object","properties":{"wallId":{"description":"The wall to add the window to.","type":"string","minLength":1},"nodeId":{"description":"Rebuild this window instead of adding one: new code and/or params (get_source first to change its code). Its native fields change with update_node.","type":"string","minLength":1},"t":{"description":"Position along the wall, 0..1: 0 = start, 0.5 = centre, 1 = end.","type":"number","minimum":0,"maximum":1},"position":{"description":"Same as t.","type":"number","minimum":0,"maximum":1},"force":{"description":"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.","type":"boolean"},"width":{"description":"Window width (default 1.5 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"height":{"description":"Window height (default 1.5 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"sillHeight":{"description":"Height from the floor to the bottom of the window (default 0.9 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Minimum 0 m.","type":["number","string"]},"openingShape":{"description":"Outline of the opening: rectangle (default), rounded top corners, or arch (round top).","type":"string","enum":["rectangle","rounded","arch"]},"archHeight":{"description":"Rise of the arch above its straight sides, within height (arch only; default 0.35 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"cornerRadius":{"description":"Radius of the rounded top corners (rounded only; default 0.15 m). Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"code":{"description":"A three.js module for a window the fields cannot express (a fan grille, tracery, carved trim): the same module and conventions as add_object, with mount 'wall'. Its size is what it builds (name params width and height so the window's size controls edit them), and its cutout mesh cuts the wall. Make it open like a real window: add an `open` clip (the sash sliding or swinging; `close` is optional, `open` reversed by default) unless it is fixed. Fields first; code only beyond them.","type":"string","minLength":1,"maxLength":48000},"params":{"description":"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.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","boolean","string"]}},"windowType":{"description":"How it opens (default fixed).","type":"string","enum":["fixed","sliding","casement","awning","hopper","single-hung","double-hung","bay","bow","louvered"]},"columns":{"description":"Panes across, equal widths; overrides the style's panes.","type":"integer","minimum":1,"maximum":12},"rows":{"description":"Panes up, equal heights; overrides the style's panes.","type":"integer","minimum":1,"maximum":12},"style":{"description":"Visual preset (panes only, never the size); same presets as create_room windows[].","type":"string","enum":["single","double-hung","triple-hung","casement","sliding","grid","tall-grid","wide-grid","horizontal-bands","transom","picture"]}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"furnish_room","title":"Furnish room","description":"Place furniture for a room type (levelId+polygon or zoneId). Skips or nudges poses that block door clear zones or overlap existing items (rotation-aware). Parent floor items to the level.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"zoneId":{"type":"string","minLength":1},"roomType":{"type":"string","enum":["bedroom","kitchen","bathroom","living","dining","hallway","entry","laundry","storage"]},"polygon":{"minItems":3,"type":"array","items":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}}},"doorWallIndex":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["roomType"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"apply_patch","title":"Apply patch","description":"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). Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.","inputSchema":{"type":"object","properties":{"patches":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"op":{"type":"string","const":"create"},"node":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"parentId":{"type":"string","minLength":1}},"required":["op","node"]},{"type":"object","properties":{"op":{"type":"string","const":"update"},"id":{"type":"string","minLength":1},"data":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["op","id","data"]},{"type":"object","properties":{"op":{"type":"string","const":"delete"},"id":{"type":"string","minLength":1},"cascade":{"type":"boolean"}},"required":["op","id"]}]}}},"required":["patches"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"create_level","title":"Create level","description":"Append a new level above the given building's current top level. height is stored as the level's floor-to-floor storey height.","inputSchema":{"type":"object","properties":{"buildingId":{"type":"string","minLength":1},"elevation":{"description":"Legacy parameter; new levels are appended above the building's current top level.","type":"number"},"height":{"description":"Stored floor-to-floor storey height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Minimum 0 m.","type":["number","string"]},"label":{"type":"string"}},"required":["buildingId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"create_unit","title":"Create unit","description":"Create a named unit in a building, optionally referencing zones across its levels.","inputSchema":{"type":"object","properties":{"buildingId":{"type":"string","minLength":1},"name":{"type":"string"},"kind":{"default":"apartment","type":"string","enum":["apartment","hotel-room","commercial","common"]},"color":{"type":"string"},"memberZoneIds":{"type":"array","items":{"type":"string","minLength":1}}},"required":["buildingId","name"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"set_unit_members","title":"Set unit members","description":"Replace the zones referenced by a unit. Every zone must belong to a level in the same building.","inputSchema":{"type":"object","properties":{"unitId":{"type":"string","minLength":1},"memberZoneIds":{"type":"array","items":{"type":"string","minLength":1}}},"required":["unitId","memberZoneIds"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false}},{"name":"list_units","title":"List units","description":"List units with their member zones and current area reports, optionally filtered by building.","inputSchema":{"type":"object","properties":{"buildingId":{"type":"string","minLength":1}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"create_wall","title":"Create wall","description":"Create a new wall on the given level between two 2D points. Thickness and height default to the core library defaults when omitted.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"start":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}},"end":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}},"thickness":{"description":"Wall thickness. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"height":{"description":"Wall height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]}},"required":["levelId","start","end"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"place_item","title":"Place item","description":"Place a catalog item into the scene. Target a level/slab/zone for floor items, a wall for wall-attached items, a ceiling for ceiling-attached items, or an item to rest on it (position in level coordinates; on an object built with add_object it lands on the real surface below the point, such as a porch landing, and a ceiling item hangs from the underside above it, such as a vaulted ceiling, unless position[1] is set above 0). Do not target the site node directly.","inputSchema":{"type":"object","properties":{"catalogItemId":{"type":"string","minLength":1},"targetNodeId":{"type":"string","minLength":1},"position":{"minItems":3,"maxItems":3,"type":"array","items":{"type":"number"}},"rotation":{"description":"Y-axis rotation. Accepts a number (radians) or a natural-language string; other units are converted. e.g. 1.5708, \"90°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]}},"required":["catalogItemId","targetNodeId","position"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"place_design","title":"Place design","description":"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 are tool errors whose JSON carries 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.","inputSchema":{"type":"object","properties":{"design":{"anyOf":[{"type":"string"},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"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":{"type":"string","minLength":1,"description":"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."},"position":{"minItems":3,"maxItems":3,"type":"array","items":{"type":"number"},"description":"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] in the surface frame."},"rotation":{"description":"Yaw. Not for wall-side designs; use side to face the other way. Accepts a number (radians) or a natural-language string; other units are converted. e.g. 1.5708, \"90°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]},"side":{"description":"Wall face for wall-side designs.","type":"string","enum":["front","back"]},"surfaceId":{"description":"Named surface of the host design to rest on (see validate_design measurements.surfaces).","type":"string"},"parameters":{"description":"Parameter values to measure at, by parameter id. Defaults otherwise.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"slots":{"description":"Slot id to \"#rrggbb\" or a scene:/library: material reference.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"name":{"type":"string"},"id":{"description":"Optional new id (\"procedural-item_…\"). Refused if it exists: this tool only creates.","type":"string"}},"required":["design","hostId","position"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"cut_opening","title":"Cut opening","description":"Cut a door or window opening into an existing wall. position is a parametric 0..1 offset along the wall centreline.","inputSchema":{"type":"object","properties":{"wallId":{"type":"string","minLength":1},"type":{"type":"string","enum":["door","window"]},"position":{"type":"number","minimum":0,"maximum":1},"width":{"description":"Opening width. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"height":{"description":"Opening height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]}},"required":["wallId","type","position","width","height"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"set_zone","title":"Set zone","description":"Create a polygonal zone on the given level. label is stored as the zone name and properties are merged into metadata.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"polygon":{"minItems":3,"type":"array","items":{"minItems":2,"maxItems":2,"type":"array","items":{"type":"number"}}},"label":{"type":"string","minLength":1},"properties":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["levelId","polygon","label"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"cut_floor_opening","title":"Cut floor or ceiling opening","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"zoneId":{"type":"string","minLength":1},"levelIds":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}},"polygon":{"minItems":3,"type":"array","items":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"rect":{"type":"object","properties":{"x":{"type":"number"},"z":{"type":"number"},"width":{"type":"number","exclusiveMinimum":0},"depth":{"type":"number","exclusiveMinimum":0}},"required":["x","z","width","depth"]},"drawnOn":{"type":"string","enum":["floor","ceiling"]},"cutsPrimary":{"type":"boolean"},"cutsAdjacent":{"type":"boolean"}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"remove_floor_opening","title":"Remove floor opening","description":"Remove an authored floor or ceiling opening and restore the derived surfaces it cut.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"set_floor_foundation","title":"Set floor and foundation","description":"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.","inputSchema":{"type":"object","properties":{"slabId":{"type":"string","minLength":1},"slabIds":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}},"patch":{"type":"object","properties":{"thickness":{"description":"Slab thickness in meters. On the ground it sits on the foundation (or the ground) and grows upward: a thicker slab raises the floor top and everything on it. Never below 0.01 m (smaller values are clamped). Upstairs the underside stays on the walls below.","type":"number","minimum":0},"foundationHeight":{"description":"Ground-bearing floors only: height of the foundation under the slab, in meters. 0 = on the ground (no foundation); > 0 = raised on a solid foundation. The floor top is derived: grade + foundationHeight + thickness.","type":"number","minimum":0},"floorHeight":{"description":"Legacy: a target floor top in level-local meters. On the ground it is mapped to foundationHeight = top - grade - thickness (never below 0); null = on the ground. Prefer thickness and foundationHeight.","type":["number","null"]},"foundation":{"type":"object","properties":{"type":{"type":"string","enum":["solid","none"]},"material":{"anyOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"preset":{"default":"custom","type":"string","enum":["white","brick","concrete","wood","glass","metal","plaster","tile","marble","custom"]},"properties":{"type":"object","properties":{"color":{"default":"#ffffff","type":"string"},"roughness":{"default":0.5,"type":"number","minimum":0,"maximum":1},"metalness":{"default":0,"type":"number","minimum":0,"maximum":1},"opacity":{"default":1,"type":"number","minimum":0,"maximum":1},"transparent":{"default":false,"type":"boolean"},"side":{"default":"front","type":"string","enum":["front","back","double"]}}},"texture":{"type":"object","properties":{"url":{"type":"string"},"repeat":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2},"scale":{"type":"number"}},"required":["url"]}}}]}},"required":["type"]},"slots":{"type":"object","properties":{"edge":{"type":"string"},"riser":{"type":"string"},"underside":{"type":"string"}},"additionalProperties":false}},"additionalProperties":false}},"required":["patch"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"set_room_floor_construction","title":"Set room floor construction","description":"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.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"slabId":{"type":"string","minLength":1},"patch":{"type":"object","properties":{"thickness":{"description":"Slab thickness in meters. On the ground it sits on the foundation (or the ground) and grows upward: a thicker slab raises the floor top and everything on it. Never below 0.01 m (smaller values are clamped). Upstairs the underside stays on the walls below.","type":"number","minimum":0},"foundationHeight":{"description":"Ground-bearing floors only: height of the foundation under the slab, in meters. 0 = on the ground (no foundation); > 0 = raised on a solid foundation. The floor top is derived: grade + foundationHeight + thickness.","type":"number","minimum":0},"floorHeight":{"description":"Legacy: a target floor top in level-local meters. On the ground it is mapped to foundationHeight = top - grade - thickness (never below 0); null = on the ground. Prefer thickness and foundationHeight.","type":["number","null"]},"foundation":{"type":"object","properties":{"type":{"type":"string","enum":["solid","none"]},"material":{"anyOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"preset":{"default":"custom","type":"string","enum":["white","brick","concrete","wood","glass","metal","plaster","tile","marble","custom"]},"properties":{"type":"object","properties":{"color":{"default":"#ffffff","type":"string"},"roughness":{"default":0.5,"type":"number","minimum":0,"maximum":1},"metalness":{"default":0,"type":"number","minimum":0,"maximum":1},"opacity":{"default":1,"type":"number","minimum":0,"maximum":1},"transparent":{"default":false,"type":"boolean"},"side":{"default":"front","type":"string","enum":["front","back","double"]}}},"texture":{"type":"object","properties":{"url":{"type":"string"},"repeat":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2},"scale":{"type":"number"}},"required":["url"]}}}]}},"required":["type"]},"slots":{"type":"object","properties":{"edge":{"type":"string"},"riser":{"type":"string"},"underside":{"type":"string"}},"additionalProperties":false}},"additionalProperties":false}},"required":["zoneId","patch"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"rebase_floor_reference","title":"Rebase floor reference","description":"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.","inputSchema":{"type":"object","properties":{"slabId":{"type":"string","minLength":1},"slabIds":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}},"referenceFloorElevation":{"type":["number","null"]}},"required":["referenceFloorElevation"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"create_mezzanine","title":"Create mezzanine","description":"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.","inputSchema":{"type":"object","properties":{"hostZoneId":{"type":"string","minLength":1},"polygon":{"minItems":3,"type":"array","items":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"elevation":{"type":"number"},"thickness":{"type":"number","minimum":0.02}},"required":["hostZoneId","polygon"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"move_zone","title":"Move or rotate room","description":"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.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"translate":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2},"rotate":{"type":"object","properties":{"angle":{"type":"number"},"pivot":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"required":["angle"]},"force":{"type":"boolean"}},"required":["zoneId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"duplicate_zone","title":"Duplicate room","description":"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.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"translate":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2},"rotate":{"type":"object","properties":{"angle":{"type":"number"},"pivot":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"required":["angle"]},"force":{"type":"boolean"}},"required":["zoneId","translate"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"rotate_zone","title":"Rotate room a quarter turn","description":"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.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"quarterTurns":{"anyOf":[{"type":"number","const":1},{"type":"number","const":-1}]},"gridStep":{"type":"number","exclusiveMinimum":0},"force":{"type":"boolean"}},"required":["zoneId","quarterTurns"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"lock_outside_faces","title":"Keep outside dimensions","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"zoneIds":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"set_zone_intent","title":"Set room intent","description":"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. Absent fields are unchanged; null clears.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"patch":{"type":"object","properties":{"name":{"type":["string","null"]},"floor":{"anyOf":[{"type":"object","properties":{"footprint":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}]},"thickness":{"anyOf":[{"type":"number","minimum":0.02},{"type":"null"}]},"elevation":{"type":["number","null"]},"finish":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}]},{"type":"null"}]},"regions":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"polygon":{"type":"array","items":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"finish":{"anyOf":[{"type":"string"},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}]}},"required":["id","polygon","finish"]}},{"type":"null"}]}},"additionalProperties":false},{"type":"null"}]},"ceiling":{"anyOf":[{"type":"object","properties":{"regions":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"polygon":{"type":"array","items":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"finish":{"anyOf":[{"type":"string"},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}]}},"required":["id","polygon","finish"]}},{"type":"null"}]}},"additionalProperties":false},{"type":"null"}]},"floorStepFinish":{"type":["string","null"]},"floorStepOverrides":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"step":{"type":"integer","minimum":0,"maximum":9007199254740991},"finish":{"type":"string"}},"required":["key","finish"]}},{"type":"null"}]},"floorEdgeFinish":{"type":["string","null"]},"wallMaterial":{"type":["string","null"]},"hasFloor":{"type":["boolean","null"]},"hasCeiling":{"type":["boolean","null"]}},"additionalProperties":false}},"required":["zoneId","patch"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"divide_zone","title":"Divide room","description":"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). Supply path or the legacy two-point cut. Islands need 0.25 m² and 5 cm wall clearance.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"cut":{"type":"array","items":[{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2},{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}],"additionalItems":false,"minItems":2,"maxItems":2},"path":{"minItems":2,"type":"array","items":{"type":"array","items":[{"type":"number"},{"type":"number"}],"additionalItems":false,"minItems":2,"maxItems":2}},"closed":{"type":"boolean"},"startBoundaryId":{"type":"string","minLength":1},"endBoundaryId":{"type":"string","minLength":1}},"required":["zoneId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"merge_zones","title":"Merge rooms","description":"Remove shared separators between two rooms. Walls are preserved; wall-only boundaries return a conflict.","inputSchema":{"type":"object","properties":{"zoneIds":{"type":"array","items":[{"type":"string","minLength":1},{"type":"string","minLength":1}],"additionalItems":false,"minItems":2,"maxItems":2}},"required":["zoneIds"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"delete_zone","title":"Delete room","description":"Delete a room. A room Divide made (a separator shared 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.","inputSchema":{"type":"object","properties":{"zoneId":{"type":"string","minLength":1},"contents":{"type":"string","enum":["delete","keep"]}},"required":["zoneId","contents"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"undo","title":"Undo","description":"Undo the most recent N steps in the scene history (default 1). Returns the number of steps actually undone.","inputSchema":{"type":"object","properties":{"steps":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"redo","title":"Redo","description":"Redo the next N previously-undone steps (default 1). Returns the number of steps actually redone.","inputSchema":{"type":"object","properties":{"steps":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"export_json","title":"Export JSON","description":"Return the scene as a serialized JSON string. Pass `pretty: true` to indent with 2 spaces.","inputSchema":{"type":"object","properties":{"pretty":{"type":"boolean"}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"export_glb","title":"Export GLB","description":"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.","inputSchema":{"type":"object","properties":{},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"validate_scene","title":"Validate scene","description":"Run Zod validation against every node in the scene. Returns `{ valid, errors, warnings }` where each entry has `{ nodeId, path, message }`. Warnings do not fail validation; for example a hidden Site, whose flag hides only its own ground and boundary while the buildings on it stay visible and exported.","inputSchema":{"type":"object","properties":{},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"validate_design","title":"Validate design","description":"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.","inputSchema":{"type":"object","properties":{"design":{"anyOf":[{"type":"string"},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"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":{"description":"Parameter values to measure at, by parameter id. Defaults otherwise.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}}},"required":["design"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"check_collisions","title":"Check collisions","description":"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.","inputSchema":{"type":"object","properties":{"levelId":{"type":"string","minLength":1},"minimumClearance":{"description":"Minimum free plan-space required between item footprints. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Minimum 0 m.","type":["number","string"]},"floorOnly":{"default":false,"description":"When true, wall-, wall-side-, and ceiling-attached items are excluded.","type":"boolean"},"candidate":{"description":"Read-only prospective furniture item. It is checked against the target level but never added to the scene.","type":"object","properties":{"id":{"default":"candidate","type":"string","minLength":1,"maxLength":120},"name":{"default":"Candidate item","type":"string","minLength":1,"maxLength":200},"levelId":{"type":"string","minLength":1},"position":{"minItems":3,"maxItems":3,"type":"array","items":{"description":"Candidate position coordinate. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\".","type":["number","string"]}},"dimensions":{"minItems":3,"maxItems":3,"type":"array","items":{"description":"Candidate declared size. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]}},"rotationY":{"description":"Candidate yaw around the vertical axis. Accepts a number (radians) or a natural-language string; other units are converted. e.g. 1.5708, \"90°\", \"1.57rad\", \"0.25 turn\".","type":["number","string"]},"source":{"type":"object","properties":{"assetId":{"type":"string","minLength":1},"uri":{"type":"string","minLength":1}}}},"required":["levelId","position","dimensions"]}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"list_templates","title":"List scene templates","description":"List the seed Pascal scene templates available to `create_from_template`. Returns the id, display name, one-line description and node count for each.","inputSchema":{"type":"object","properties":{},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"create_from_template","title":"Create scene from template","description":"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.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Template id (see `list_templates`). Currently one of: \"empty-studio\", \"two-bedroom\", \"garden-house\"."},"name":{"description":"Optional display name for the saved scene. Defaults to the template name.","type":"string","minLength":1,"maxLength":200},"save":{"default":false,"type":"boolean"},"projectId":{"type":"string"}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"create_house_from_brief","title":"Create house from brief","description":"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.","inputSchema":{"type":"object","properties":{"brief":{"type":"string","minLength":1},"projectId":{"type":"string"},"projectName":{"type":"string","minLength":1,"maxLength":200},"bedroomCount":{"type":"integer","minimum":0,"maximum":12},"rooms":{"type":"array","items":{"type":"string","minLength":1}},"style":{"type":"string"},"landscaping":{"type":"boolean"},"constraints":{"type":"string"}},"required":["brief"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"create_project","title":"Create project","description":"Create a browser-visible Pascal project for the authenticated user. Use this before save_scene when the user asks for a new project.","inputSchema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"id":{"type":"string","minLength":1,"maxLength":64},"isPrivate":{"default":true,"type":"boolean"}},"required":["name"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"get_project_status","title":"Get project status","description":"Authoritative status/debug call for a Pascal project: editor URL, browser-visible version, latest saved version, published version, node count, and graph hash.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"save_scene","title":"Save scene","description":"Save the current scene (or a provided graph) to the SceneStore. Defaults to a browser-visible draft save so agents can iterate without creating many project versions. Use saveMode: \"checkpoint\" for meaningful version history.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"name":{"type":"string","minLength":1,"maxLength":200},"projectId":{"type":"string"},"expectedVersion":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"saveMode":{"default":"draft","description":"`draft` updates the browser-visible working model without polluting version history. `checkpoint` creates a meaningful saved version; it does not publish.","type":"string","enum":["draft","checkpoint"]},"publish":{"description":"Hosted Pascal: also publish this checkpoint, making it the version the project's viewers see. Only when the user asks to publish.","type":"boolean"},"thumbnail":{"type":"string","format":"uri"},"includeCurrentScene":{"default":true,"description":"If true, save the bridge current scene. If false, use the graph arg.","type":"boolean"},"graph":{"description":"Full SceneGraph { nodes, rootNodeIds, collections? } to save instead of the bridge state.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["name"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"load_scene","title":"Load scene","description":"Load a scene from the SceneStore into the bridge. Returns the scene metadata. Throws `scene_not_found` if the id does not exist.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"list_scenes","title":"List scenes","description":"List scenes in the SceneStore. Optionally filter by `projectId` and cap results with `limit` (default 100).","inputSchema":{"type":"object","properties":{"projectId":{"type":"string"},"limit":{"type":"integer","exclusiveMinimum":0,"maximum":1000}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"delete_scene","title":"Delete scene","description":"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.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"expectedVersion":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"rename_scene","title":"Rename scene","description":"Rename a scene in the SceneStore. Returns the updated SceneMeta. Optionally pass `expectedVersion` for optimistic concurrency.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"newName":{"type":"string","minLength":1,"maxLength":200},"expectedVersion":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["id","newName"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}},{"name":"generate_variants","title":"Generate variants","description":"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.","inputSchema":{"type":"object","properties":{"baseSceneId":{"description":"If set, fork from this saved scene; else fork from current bridge state.","type":"string"},"count":{"default":3,"type":"integer","minimum":1,"maximum":10},"vary":{"default":["wall-thickness","wall-height"],"minItems":1,"type":"array","items":{"type":"string","enum":["wall-thickness","wall-height","zone-labels","room-proportions","open-plan","door-positions","fence-style"]}},"seed":{"description":"Deterministic RNG seed.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"save":{"default":false,"description":"If true, also save each variant via SceneStore and return ids.","type":"boolean"}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false}},{"name":"photo_to_scene","title":"Photo to Pascal scene","description":"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.","inputSchema":{"type":"object","properties":{"image":{"type":"string","description":"Base64 or https URL of the floor-plan photo"},"scaleHint":{"description":"e.g. \"1 cm = 1 m\" or \"approx 80 m²\"","type":"string"},"name":{"default":"Scene from photo","type":"string"},"save":{"default":true,"type":"boolean"},"defaultWallThickness":{"description":"Default wall thickness. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]},"defaultWallHeight":{"description":"Default wall height. Accepts a number (meters) or a natural-language string; other units are converted. e.g. 0.9, \"6 ft\", \"180cm\", \"2 ft 3 in\". Must be greater than 0 m.","type":["number","string"]}},"required":["image"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":true}},{"name":"analyze_floorplan_image","title":"Analyze floor-plan image","description":"Defer to the MCP host (via sampling) to extract walls, rooms, and approximate dimensions from a floor-plan image. Requires host support for sampling.","inputSchema":{"type":"object","properties":{"image":{"type":"string","description":"Base64-encoded image or http(s) URL"},"scaleHint":{"description":"Text hint about scale, e.g. '1 cm = 1 m' or 'approximately 80 m²'","type":"string"}},"required":["image"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},{"name":"analyze_room_photo","title":"Analyze room photo","description":"Defer to the MCP host (via sampling) to extract approximate dimensions, fixtures, and windows from a single-room photograph. Requires host support for sampling.","inputSchema":{"type":"object","properties":{"image":{"type":"string","description":"Base64-encoded image or http(s) URL"}},"required":["image"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},{"name":"list_pascal_services","title":"List Pascal services","description":"List the hosted services an installed plugin may use in this project, current prices and available media models. This lookup is free. Discoverability does not grant permission or spend credits.","inputSchema":{"type":"object","properties":{"pluginId":{"type":"string","pattern":"^[a-z0-9-]+:[a-z0-9-]+$","description":"The installed Pascal plugin whose service grant this request uses. Read get_scene.installedPlugins or the host's plugin list; an ID is not permission."},"projectId":{"description":"The hosted project to use. Omit only when the host has an active hosted project. Local scene IDs do not authorize hosted services.","type":"string","pattern":"^project_[A-Za-z0-9_-]+$"}},"required":["pluginId"],"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"pull_site_data","title":"Pull site context","description":"Retrieve sourced parcel, terrain/elevation, flood, wetlands and other selected site context for a hosted project. Uses the fixed Pascal credit price from list_pascal_services, bounded by maxCredits. Keep pullKey for retries. Returns a retained operation; use get_site_pull while running or to reopen it free. Partial coverage and unknowns remain explicit. Does not edit the scene.","inputSchema":{"type":"object","properties":{"pluginId":{"type":"string","pattern":"^[a-z0-9-]+:[a-z0-9-]+$","description":"The installed Pascal plugin whose service grant this request uses. Read get_scene.installedPlugins or the host's plugin list; an ID is not permission."},"projectId":{"description":"The hosted project to use. Omit only when the host has an active hosted project. Local scene IDs do not authorize hosted services.","type":"string","pattern":"^project_[A-Za-z0-9_-]+$"},"address":{"type":"string","minLength":1,"maxLength":500},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"layers":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string","enum":["parcel","zoning","flood","code_basis","elevation","utilities","soils","wetlands","structures","boundaries"]}},"geometry":{"type":"boolean"},"adjacent":{"type":"boolean"},"pullKey":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$","description":"A unique key for this requested pull. Reuse the same key and input after a lost response; changing either starts a different operation or is refused."},"maxCredits":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The maximum Pascal credits approved for this operation. Read list_pascal_services for the price; the gateway refuses a higher charge."}},"required":["pluginId","pullKey","maxCredits"],"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":true}},{"name":"get_site_pull","title":"Read saved site context","description":"Read a retained site operation and its sourced result for this project and plugin without another charge. May reconcile an interrupted operation and its refund. Does not start another pull or edit the scene.","inputSchema":{"type":"object","properties":{"pluginId":{"type":"string","pattern":"^[a-z0-9-]+:[a-z0-9-]+$","description":"The installed Pascal plugin whose service grant this request uses. Read get_scene.installedPlugins or the host's plugin list; an ID is not permission."},"projectId":{"description":"The hosted project to use. Omit only when the host has an active hosted project. Local scene IDs do not authorize hosted services.","type":"string","pattern":"^project_[A-Za-z0-9_-]+$"},"operationId":{"type":"string","minLength":1,"maxLength":128}},"required":["pluginId","operationId"],"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},{"name":"generate_studio_media","title":"Generate project media","description":"Generate an image, material or video using an available model from list_pascal_services. Charges this project's Pascal wallet up to maxCredits; returns a job ID to read with get_studio_generation. Uses authorized project snapshot/render references. Does not edit the scene. After a lost response, inspect the project's Studio history before submitting again; a new submission may charge again.","inputSchema":{"type":"object","properties":{"pluginId":{"type":"string","pattern":"^[a-z0-9-]+:[a-z0-9-]+$","description":"The installed Pascal plugin whose service grant this request uses. Read get_scene.installedPlugins or the host's plugin list; an ID is not permission."},"projectId":{"description":"The hosted project to use. Omit only when the host has an active hosted project. Local scene IDs do not authorize hosted services.","type":"string","pattern":"^project_[A-Za-z0-9_-]+$"},"modelId":{"type":"string","minLength":1,"maxLength":100},"prompt":{"type":"string","maxLength":400000},"sources":{"maxItems":32,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["snapshot","render"]},"id":{"type":"string","minLength":1,"maxLength":128},"role":{"type":"string","maxLength":100},"frame":{"type":"string","enum":["start","end"]}},"required":["type","id"],"additionalProperties":false}},"settings":{"type":"object","properties":{"aspectRatio":{"type":"string","maxLength":100},"durationSeconds":{"type":"number","exclusiveMinimum":0},"resolution":{"type":"string","maxLength":100},"quality":{"type":"string","maxLength":100}},"additionalProperties":false},"maxCredits":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The maximum Pascal credits approved for this operation. Read list_pascal_services for the price; the gateway refuses a higher charge."}},"required":["pluginId","modelId","maxCredits"],"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true}},{"name":"get_studio_generation","title":"Read project media generation","description":"Poll or reopen a media job created for this project and plugin without another charge. May reconcile provider completion or failure and its refund. Does not submit another generation or edit the scene.","inputSchema":{"type":"object","properties":{"pluginId":{"type":"string","pattern":"^[a-z0-9-]+:[a-z0-9-]+$","description":"The installed Pascal plugin whose service grant this request uses. Read get_scene.installedPlugins or the host's plugin list; an ID is not permission."},"projectId":{"description":"The hosted project to use. Omit only when the host has an active hosted project. Local scene IDs do not authorize hosted services.","type":"string","pattern":"^project_[A-Za-z0-9_-]+$"},"id":{"type":"string","minLength":1,"maxLength":128}},"required":["pluginId","id"],"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},{"name":"list_captures","title":"List room scans","description":"List the room scans (captures) uploaded by the Pascal Capture iOS app to projects you can edit. Newest first. Returns per-scan conversion status, upload status and RoomPlan counts; call get_capture for detail or open_capture_as_project to start editing one.","inputSchema":{"type":"object","properties":{"projectId":{"description":"Only scans belonging to this project. Requires edit access to it.","type":"string","minLength":1,"maxLength":64},"status":{"description":"Only scans in this conversion state.","type":"string","enum":["pending","processing","processed","failed"]},"limit":{"description":"Newest first. Default 50, maximum 200.","type":"integer","minimum":1,"maximum":200}},"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"get_capture","title":"Get room scan","description":"Describe one room scan on its three independent axes: upload (archive) progress, conversion into scene geometry, and mesh/splat fusion. Also reports RoomPlan counts and, on request, scan measurements. Read-only: it never starts a fusion or a conversion.","inputSchema":{"type":"object","properties":{"captureId":{"type":"string","minLength":1,"maxLength":64,"description":"The scan to describe."},"includeScanMetrics":{"description":"Also report scan duration, distance walked, frame and point counts.","type":"boolean"}},"required":["captureId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"open_capture_as_project","title":"Open a room scan for editing","description":"Bind the project that owns a processed room scan to this session, so the scene tools act on the scanned building. Creates nothing: the scan already lives in a project, and this loads that project's current draft. Call it again for the same scan and you get the same project.","inputSchema":{"type":"object","properties":{"captureId":{"type":"string","minLength":1,"maxLength":64,"description":"A processed scan to start editing."}},"required":["captureId"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false}}],"resources":[{"uri":"pascal://agent-guide","name":"agent-guide","title":"Pascal MCP agent guide","description":"Short MCP-first project creation, save/publish, validation, and output workflow for external agents.","mimeType":"text/markdown"},{"uri":"pascal://agent/guide","name":"agent-guide-legacy","title":"Pascal MCP agent guide","description":"Legacy URI for the Pascal MCP agent guide. Prefer pascal://agent-guide.","mimeType":"text/markdown"},{"uri":"pascal://scene/current","name":"scene-current","title":"Current scene","description":"Complete snapshot of the live Pascal scene: nodes dict, rootNodeIds, collections.","mimeType":"application/json"},{"uri":"pascal://scene/current/summary","name":"scene-summary","title":"Scene summary (markdown)","description":"Markdown overview: sites/buildings/levels, per-level node counts, zone floor areas, scene bbox.","mimeType":"text/markdown"},{"uri":"pascal://catalog/items","name":"catalog-items","title":"Item catalog","description":"Dependency-free catalog subset of placeable items available in standalone MCP mode.","mimeType":"application/json"},{"uri":"pascal://schema/design","name":"design-schema","title":"Design schema","description":"JSON Schema, limits, rules and an example for Pascal designs (procedural items). Author a design against it, then call validate_design, which is the authority.","mimeType":"application/json"}],"prompts":[{"name":"from_brief","title":"Generate a Pascal scene from a brief","description":"Generate a Pascal 3D scene from a natural-language brief. Binds an active scene, then produces semantic tool calls to realise the design.","arguments":[{"name":"brief","required":true},{"name":"constraints","required":false}]},{"name":"iterate_on_feedback","title":"Iterate on a scene from user feedback","description":"Produces a minimal-diff plan of apply_patch calls in response to user feedback on the current scene.","arguments":[{"name":"feedback","required":true}]},{"name":"renovation_from_photos","title":"Plan a renovation from photos","description":"Plan a minimal-patch renovation given current-state photos, reference-state photos, and free-form goals.","arguments":[{"name":"currentPhotos","required":true},{"name":"referencePhotos","required":true},{"name":"goals","required":true}]}],"_meta":{"app.pascal/registry-name":"io.github.pascalorg/editor","app.pascal/credential":{"header":"Authorization","format":"Bearer sk_live_...","envVar":"PASCAL_API_KEY","createUrl":"https://editor.pascal.app/settings"},"app.pascal/documentation":{"mcp":"https://editor.pascal.app/docs/developers/mcp","agentSkills":"https://editor.pascal.app/docs/developers/agent-skills"}}}