{"ok":true,"site":{"host":"alza-dev.pages.dev","url":"https://alza-dev.pages.dev/","type":"live","desc":"Draw floor plans in 2D, walk them in 3D, and let your AI agent co-edit the same live page through WebMCP. Built for the WebMCP Challenge.","tools":[{"name":"get_model","kind":"answer","impl":"imperative","description":"Read the full floor plan: walls (endpoints, thickness, height), openings (doors/windows with position along their wall), rooms (metric rects with labels), furniture, and plan name. All units are meters.","title":"Read the whole plan","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_issues","kind":"answer","impl":"imperative","description":"Run the constraint checker over the plan. Detects: too-short walls, loose ends, collinear overlaps, mid-span crossings, openings overflowing their wall or overlapping each other, walls ending inside an opening, floating/overlapping/doorless rooms, furniture crossing walls, blocking doors/windows, or colliding. Use it after editing to self-repair.","title":"Check the plan for problems","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_item_catalog","kind":"answer","impl":"imperative","description":"List the furniture catalog: kind, label, footprint (width × depth in meters) and height.","title":"Furniture catalogue","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_editor_state","kind":"answer","impl":"imperative","description":"Read what the human is doing right now: 2D/3D view, camera mode, selected wall/item/room, current draw mode.","title":"What the human is doing right now","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"measure","kind":"act","impl":"imperative","description":"Measure a distance: between two points (x1,y1)-(x2,y2), or the length of a wall by id. Returns meters.","title":"Measure a distance","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"x1":{"type":"number"},"y1":{"type":"number"},"x2":{"type":"number"},"y2":{"type":"number"},"wallId":{"type":"string"}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_underlay","kind":"answer","impl":"imperative","description":"Read the blueprint underlay: whether a reference plan image is loaded, the world rectangle it covers (metres), the mapping from image fractions to world coordinates — and the PROTOCOL for tracing it. Call this FIRST whenever the human gives you a plan to reproduce. Tracing needs the drawing in two places and this tool tells you which are missing: uploaded on the page (carries the scale) and pasted into your conversation (the only way you can see it). This tool never returns the pixels — it returns where the drawing sits in world metres so you can convert what you measure on your own copy. Measure in fractions of the image, not pixels: the page holds a resized copy.","title":"Blueprint underlay + tracing protocol","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"calibrate_underlay","kind":"act","impl":"imperative","description":"Scale the loaded blueprint image to real-world meters. Give two points on the image as fractions (u, v in 0..1; u = right, v = down) and the REAL distance between them in meters (e.g. a dimension printed on the plan). The underlay is resized preserving aspect, keeping the first point fixed. Call after the human uploads the plan, before tracing walls over it.","title":"Set the drawing's true scale","inputSchema":{"type":"object","properties":{"u1":{"type":"number"},"v1":{"type":"number"},"u2":{"type":"number"},"v2":{"type":"number"},"meters":{"type":"number"},"opacity":{"type":"number"}},"required":["u1","v1","u2","v2","meters"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_wall","kind":"act","impl":"imperative","description":"Add a wall segment from (ax,ay) to (bx,by) in meters. Coordinates snap to a 5 cm grid. Default thickness 0.15 m (use 0.1 for interior), height 2.7 m.","title":"Add a wall","inputSchema":{"type":"object","properties":{"ax":{"type":"number"},"ay":{"type":"number"},"bx":{"type":"number"},"by":{"type":"number"},"thickness":{"type":"number"},"height":{"type":"number"}},"required":["ax","ay","bx","by"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"edit_wall","kind":"act","impl":"imperative","description":"Move endpoints or change thickness/height of an existing wall (by id). Endpoints snap to 5 cm.","title":"Edit a wall","inputSchema":{"type":"object","properties":{"id":{"type":"string"},"ax":{"type":"number"},"ay":{"type":"number"},"bx":{"type":"number"},"by":{"type":"number"},"thickness":{"type":"number"},"height":{"type":"number"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"remove_wall","kind":"act","impl":"imperative","description":"Remove a wall by id. Its doors and windows are removed too.","title":"Remove a wall","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_door","kind":"act","impl":"imperative","description":"Add a door on a wall. t is the position of the door CENTER along the wall (0..1). Default width 0.9 m, height 2.1 m; a door wider than 1.2 m is built as a double door with two leaves. The vano is clamped so it always fits inside the wall; too-wide doors are rejected with the wall length. hinge picks the jamb the hinges sit on (\"a\" = the wall's A end, the default; \"b\" = the B end) and side picks which way the leaf swings, as seen walking the wall from A to B: \"right\" (default) or \"left\". Match these to the swing arc drawn on the plan.","title":"Add a door","inputSchema":{"type":"object","properties":{"wallId":{"type":"string"},"t":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"},"hinge":{"type":"string","enum":["a","b"]},"side":{"type":"string","enum":["left","right"]}},"required":["wallId","t"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"set_door_swing","kind":"act","impl":"imperative","description":"Change how an existing door opens: hinge (\"a\" | \"b\" — which jamb carries the hinges) and/or side (\"left\" | \"right\" — which way the leaf swings, walking the wall from its A endpoint to its B endpoint). Re-run build_3d to see it.","title":"Change how a door opens","inputSchema":{"type":"object","properties":{"id":{"type":"string"},"hinge":{"type":"string","enum":["a","b"]},"side":{"type":"string","enum":["left","right"]}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_window","kind":"act","impl":"imperative","description":"Add a window on a wall. t is the position of the window CENTER along the wall (0..1). Default width 1.2 m, sill 0.9 m, height 1.2 m. The vano is clamped to fit.","title":"Add a window","inputSchema":{"type":"object","properties":{"wallId":{"type":"string"},"t":{"type":"number"},"width":{"type":"number"},"sill":{"type":"number"},"height":{"type":"number"}},"required":["wallId","t"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"move_opening","kind":"act","impl":"imperative","description":"Move a door or window along its wall to a new center position t (0..1). Clamped to fit.","title":"Move a door or window","inputSchema":{"type":"object","properties":{"id":{"type":"string"},"t":{"type":"number"}},"required":["id","t"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"remove_opening","kind":"act","impl":"imperative","description":"Remove a door or window by id.","title":"Remove a door or window","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_room","kind":"act","impl":"imperative","description":"Add a room: a metric rectangle (x, y = top-left corner, w, h in meters) with a label and floor finish (oak | tile | carpet | concrete).","title":"Add a room","inputSchema":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"w":{"type":"number"},"h":{"type":"number"},"label":{"type":"string"},"floor":{"type":"string"}},"required":["x","y","w","h","label"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"update_room","kind":"act","impl":"imperative","description":"Update a room by id or label (e.g. \"bedroom\"): move, resize, relabel, or change floor finish.","title":"Update a room","inputSchema":{"type":"object","properties":{"id":{"type":"string"},"x":{"type":"number"},"y":{"type":"number"},"w":{"type":"number"},"h":{"type":"number"},"label":{"type":"string"},"floor":{"type":"string"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"remove_room","kind":"act","impl":"imperative","description":"Remove a room by id or label.","title":"Remove a room","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"place_item","kind":"act","impl":"imperative","description":"Place furniture from the catalog at (x, y) = center in meters. rotation (degrees, counterclockwise) sets which way the piece FACES: 0 = faces +y (down/south on the plan), 90 = faces +x (right/east), 180 = faces -y (up/north), 270 = faces -x (left/west). The back of a sofa, bed headboard, wardrobe or counter is opposite the facing direction — so a sofa against the SOUTH wall needs rotation 180, and one against the WEST wall needs rotation 90. To sit a piece flush against a wall, offset its center by depth/2 from the wall FACE (centerline ± thickness/2), not from the centerline. Use get_item_catalog for footprints; get_issues reports crossings and blocked doors.","title":"Place furniture","inputSchema":{"type":"object","properties":{"kind":{"type":"string"},"x":{"type":"number"},"y":{"type":"number"},"rotation":{"type":"number"}},"required":["kind","x","y"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"define_item_kind","kind":"act","impl":"imperative","description":"Create a piece of furniture that is NOT in the catalogue, then place it with place_item. Use this whenever the plan draws something the catalogue does not have, or draws it at a different size — a corner bath, an L-shaped sofa, a kitchen island, a piano. Do NOT approximate with the nearest stock item when the plan shows something specific: define the real thing. Required: kind (a stable snake_case id), label, and the true footprint w × d and height h in METRES. Optional `parts` models it in 3D from primitives; without it the piece is blocked out from its footprint. Each part is { shape: \"box\" | \"cylinder\" | \"sphere\", x, y, z, w, h, d, color, rotation }, in the piece's OWN local frame: x runs along its width, z along its depth, y is height above the floor and is the part's BOTTOM (a 0.4 m tall seat resting on the floor is y:0, h:0.4). The piece faces +z, so a backrest sits at negative z and the front is positive z — that keeps it consistent with the rotation convention in place_item. For a cylinder, w is the diameter and d makes it an ellipse. Sizes are metres, colours are hex.","title":"Model a new piece of furniture","inputSchema":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"w":{"type":"number"},"d":{"type":"number"},"h":{"type":"number"},"color":{"type":"string"},"category":{"type":"string","enum":["living","bedroom","kitchen","bath","office","decor"]},"parts":{"type":"array","items":{"type":"object","properties":{"shape":{"type":"string","enum":["box","cylinder","sphere"]},"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"},"w":{"type":"number"},"h":{"type":"number"},"d":{"type":"number"},"color":{"type":"string"},"rotation":{"type":"number"}},"required":[],"additionalProperties":false}}},"required":["kind","label","w","d","h"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"move_item","kind":"act","impl":"imperative","description":"Move or rotate a furniture item by id or kind (e.g. \"sofa\"). Same rotation convention as place_item: 0 faces +y, 90 faces +x, 180 faces -y, 270 faces -x.","title":"Move or rotate furniture","inputSchema":{"type":"object","properties":{"id":{"type":"string"},"x":{"type":"number"},"y":{"type":"number"},"rotation":{"type":"number"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"remove_item","kind":"act","impl":"imperative","description":"Remove a furniture item by id or kind.","title":"Remove furniture","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"set_plan_name","kind":"act","impl":"imperative","description":"Rename the plan.","title":"Rename the plan","inputSchema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"clear_model","kind":"act","impl":"imperative","description":"Clear the whole plan (walls, openings, rooms, furniture, notes). Use when the user asks to start over.","title":"Erase the whole plan","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"build_3d","kind":"act","impl":"imperative","description":"Raise the plan into 3D: extrudes walls with real openings, resolves corner joints, builds furniture, and switches the human's view to the 3D scene. Call after drawing so the user sees the result.","title":"Raise the plan into 3D","inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"set_camera","kind":"act","impl":"imperative","description":"Move the 3D camera: \"orbit\" (default 3/4 view), \"top\" (plan view from above), \"walk\" (first-person at 1.6 m — the user walks with WASD). Implies switching to 3D.","title":"Move the 3D camera","inputSchema":{"type":"object","properties":{"mode":{"type":"string","enum":["orbit","top","walk"]}},"required":["mode"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"set_doors","kind":"act","impl":"imperative","description":"Swing the door leaves in the 3D scene open or shut (build_3d must have run). state: \"open\", \"closed\" or \"toggle\". Pass an opening id to act on one door, or omit it to act on every door in the plan. The human can do the same by clicking a leaf in the 3D view.","title":"Open or shut the doors in 3D","inputSchema":{"type":"object","properties":{"state":{"type":"string","enum":["open","closed","toggle"]},"id":{"type":"string"}},"required":["state"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_supplier_catalog","kind":"answer","impl":"imperative","description":"Read the furniture catalogue published by the PARTNER ORIGIN (Nordika) as its own WebMCP tools — sku, name, category, real footprint in metres, price and stock. Alza discovers those tools with getTools({fromOrigins}) and calls them with executeTool(), so the data crosses origins in the browser with no server in between. Filter with category (living | bedroom | office | dining), maxPrice, maxWidth (metres) or inStock. Pair it with place_supplier_product to drop a real product into the plan at its true size.","title":"Supplier catalogue (cross-origin)","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"type":"object","properties":{"category":{"type":"string"},"maxPrice":{"type":"number"},"maxWidth":{"type":"number"},"inStock":{"type":"boolean"}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"place_supplier_product","kind":"act","impl":"imperative","description":"Buy-in-place: fetch one product from the partner origin by sku, add it to this plan's catalogue at the supplier's REAL dimensions, and place it at (x, y) = centre in metres with the usual rotation convention. The constraint checker then treats it like any other piece, so a sofa that does not fit is caught before anyone orders it.","title":"Place a supplier product","inputSchema":{"type":"object","properties":{"sku":{"type":"string"},"x":{"type":"number"},"y":{"type":"number"},"rotation":{"type":"number"}},"required":["sku","x","y"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"leave_note","kind":"act","impl":"imperative","description":"Leave a note on the plan for the human (or read theirs): design rationale, questions, measurements to verify. Notes are visible in the Notes panel.","title":"Leave a note on the plan","inputSchema":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_notes","kind":"answer","impl":"imperative","description":"Read all notes left on the plan by the human and by agents.","title":"Read the notes on the plan","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"}],"favicon":"/favicons/alza-dev.pages.dev.svg","apiSurface":"spec","category":"Developer Tools","toolCount":31}}