Connect to the planner tab
Use the same browser tab the human is viewing. The planner registers native tools on document.modelContext; this guide page does not. Use your runtime’s documented browser WebMCP discovery and invocation capability on the planner tab. This is not a remote MCP endpoint.
Do not guess commands such as webmcp_list_tools. A “WebMCP · registered” badge only confirms page registration, not that your agent runtime can invoke the tools.
If your agent runtime cannot discover or call native WebMCP tools, report the connection blocker and stop room edits. Do not silently fall back to the manual planner or claim WebMCP was used.
Create a new room
- Discover the planner tab’s native tools. Confirm
generateRoomis available and read its schema. - Call
generateRoomwith dimensions in centimetres, room profile and openings, including an entrance. A 3 × 4.5 m room iswidthCm: 300, depthCm: 450. Ask about missing measurements or clearly agree provisional assumptions with the user. - The page creates a separate furnished proposal, preserves the previous room, and shows the new room type and dimensions. Inspect
operationSucceeded,validation,briefandomitted; successful execution does not mean a complete or valid layout. - Verify with
getRoomState,listFurnitureandcheckLayout, each withwhich: "proposal". Use the returned proposal ID and latest revision for edits. Do not change views simply to read a different snapshot. - Leave the furniture in Proposal. Confirm the human-facing planner tab shows the requested room heading, dimensions and proposal before saying it is visible. Report warnings and omissions, and invite the human to review and Apply.
Recreate an L-shape or nook from a drawing
Interpret the user’s dimensioned drawing into an ordered floorPlan.points list. The origin is the bounding box’s top-left; x runs east and y runs south. Walk around the perimeter in either direction, record every corner once, and do not repeat the first point at the end. Every edge must be horizontal or vertical. The declared widthCm and depthCm must equal the point list’s bounding box.
Floortris derives stable segments in list order: the first edge is wall-1, the next is wall-2, and so on. For each door, window, or wall-mounted TV on a custom outline, use the returned segment ID plus its outward wall direction. Segment offsets start at that segment’s normalized top/left endpoint, independent of which direction you traced the polygon. Call getRoomState and read wallSegments rather than guessing. Floortris rejects diagonals, crossed walls, missing segment IDs, furniture in cut-out areas, and routes through those areas.
This example is a 5 × 5 m envelope with its lower-right 2 × 2 m corner removed. Replace every number with the drawing’s measured dimensions:
{
"name": "Example measured L room",
"widthCm": 500,
"depthCm": 500,
"floorPlan": {
"kind": "rectilinear",
"points": [
{
"xCm": 0,
"yCm": 0
},
{
"xCm": 500,
"yCm": 0
},
{
"xCm": 500,
"yCm": 300
},
{
"xCm": 300,
"yCm": 300
},
{
"xCm": 300,
"yCm": 500
},
{
"xCm": 0,
"yCm": 500
}
]
},
"profile": {
"kind": "lounge"
},
"openings": [
{
"id": "entrance",
"kind": "door",
"wall": "south",
"segmentId": "wall-5",
"offsetCm": 20,
"widthCm": 100,
"hinge": "start",
"swing": "in",
"angle": 90,
"mechanism": "hinged",
"entrance": true
}
],
"idempotencyKey": "replace-with-a-unique-request-key"
}Use native WebMCP for agent room edits. Leave Apply, Discard, Unlock and room-input confirmation to the human; do not click those controls on their behalf. Do not use “Try a proposal,” “Try again,” room forms, DOM scripts or direct storage edits as substitutes for native tools. Human editing stays available.
Keep the room visible to its owner
Rooms are saved in this browser’s local storage. A ?room=… link selects an existing local room; it does not transfer a room to another browser, device or isolated agent session. If the user cannot see the room, verify the same browser session and active document before claiming it is open.
Edit the existing room
Read getRoomState first. Reuse its active layout proposal; if none exists, call createProposal with the accepted revisions. Use proposeLayout, findPlacements and furniture tools inside that draft. Prefer the first checked candidate: results rank functional geometry (door/TV safety, usable clearances, real furniture backs and sofa-table spacing) above decorative distribution. Verify checkedRules, frontFacing, backWall and backGapCm instead of inferring orientation from a 2D rotation value. Changes to accepted room geometry, openings or rules require a setup proposal and human confirmation.
Create one-off measured furniture
Use createCustomFurniture only inside an active layout proposal. Supply a human-readable label, one closed floor-furniture kind, exact width/depth/height and top-left position in centimetres, a quarter-turn rotation, a listed local furniture palette, and a unique idempotency key. The position is the top-left of the rotated plan envelope. The command rejects any new blocking boundary, collision, height, door, window, radiator, walking, use-zone or relationship issue rather than leaving an invalid custom object in the draft.
The supported kinds are sofa, chair, table, coffee table, desk, storage, bed and plant. They are solid floor occupants and activate the same rules as catalogue objects. Rugs and other non-solid floor layers are deliberately excluded from custom creation. The tool does not accept tags, HTML/SVG, code, remote model URLs, arbitrary forms, rule overrides, fixed fixtures or wall/ceiling mounts. A custom chair may name an existing desk with linkedDeskId; the engine verifies that relationship. Custom beds do not claim a human sleep-size classification, and custom storage does not claim wardrobe, bedside or office-storage roles, so agent-authored labels cannot complete those briefs.
The result repeats the measured envelope, closed provenance and review.requiresHumanApply. Read the object back with listFurniture, inspect it in 2D/3D under its CUSTOM identity, and use checkLayout. updateFurniture may move or rotate it and setAppearance may change its local palette, but its dimensions, kind and provenance stay fixed. Remove and recreate it to change those facts. It remains a one-off record inside this local room document; there is no remote asset library or commerce.
Create a measured L or U sectional
For a custom sofa, add geometry with type: sectional, a cardinal primaryFacing, and 2–12 modules. Every module needs a unique safe local ID, a closed seat, corner or chaise type, exact local xCm/yCm, width, depth, height and cardinal facing. Coordinates are local to the unrotated parent envelope and must start at x=0 and y=0. Parent width, depth and height must exactly match the derived module envelope.
The modules must be one connected edge-sharing assembly with no positive-area overlap or disconnected island. Floortris persists it as one custom object and checks the exact module union rather than its empty bounding-box corners. Internal joined spans are removed from seating-front checks; all remaining exposed fronts must be usable. primaryFacing alone controls the TV and coffee-table relationship. Move or quarter-turn the parent to transform the whole assembly; module topology is immutable. Do not send polygons, paths, tags, markup, code, URLs, remote models or custom rule data.
Add window treatments and lights
Call listCatalogue to get the typed fixture variants, then place them in the active layout proposal. Curtains and blinds are not free-standing furniture: pass attachedOpeningId for a window returned by getRoomState. Floortris derives their wall segment, position and measured fit. Curtains include a shallow 15 cm room-side projection checked against doors, radiators and furniture; blinds fit the glazing and reserve no floor cells.
Ceiling lights use kind: ceiling_light and an originCell inside the actual ceiling outline, including an L-shaped room’s missing corners. Choose pendant, flush, track or recessed variants. The engine derives their mount height and reports a pendant below its documented 200 cm Floortris planning head-clearance assumption. That assumption is layout guidance, not an electrical, accessibility or building-code claim.
Wall lights require a real wallAnchor and a measured elevationCm. For a custom room, include the segmentId returned in wallSegments; height, windows, doors and nearby tall furniture are checked. Floor lamps use ordinary floor collision and walking rules. Table lamps require supportObjectId naming an existing table, desk or cabinet; Floortris centres the lamp on the measured support surface and rejects overhang or a stale link.
Lights may set lightingZone to ambient, seating, reading or circulation. The returned zone warnings are simple proximity and entrance-route guidance, not lux, glare, wiring or code analysis. Always read checkLayout after placement and leave the exact reviewed revision for the human to Apply.
Choose wallpaper and flooring
Call listCatalogue and read palettes.wall and palettes.floor. Texture entries include stable IDs, names, descriptions, tags and repeat sizes. Use those IDs in generateRoom.appearance or setAppearance with the active proposal ID and latest revision. Read back getRoomState.appearance to verify the selection. Finishes do not change dimensions, locks or rule flags. These are concept visuals, not real product listings or permission to decorate rented rooms. Wallpaper is visible in 3D; floor textures also appear in the 2D furniture view.
Example request, not a measured room
This example assumes a double bed, two bedside tables, storage, no workspace, no windows and an 80 cm south-wall entrance. Replace those assumptions with the user’s actual room. Discover the current schema before calling generateRoom; use a unique request key and keep it for retries.
{
"name": "Example 3 × 4.5 m bedroom",
"widthCm": 300,
"depthCm": 450,
"profile": {
"kind": "bedroom",
"sleeping": "double",
"workspace": false,
"storage": true,
"bedsideQuantity": 2
},
"openings": [
{
"id": "entrance",
"kind": "door",
"wall": "south",
"offsetCm": 20,
"widthCm": 80,
"hinge": "start",
"swing": "in",
"angle": 90,
"mechanism": "hinged",
"entrance": true
}
],
"idempotencyKey": "replace-with-a-unique-request-key"
}