3D New Era AI
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| NEWERA_LOG | No | Log filter | |
| NEWERA_ADDR | No | The address the server binds to (default 127.0.0.1:7878) | |
| NEWERA_TOKEN | No | Token required to listen beyond loopback | |
| NEWERA_TELEMETRY | No | Set to 0 to turn off telemetry for a single run | |
| NEWERA_RENDER_THREADS | No | Number of threads for photo renders (default half the cores) |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
| resources | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| acceptA | Mark findings of layout, ergonomics, electrical or plumbing as looked at, by the key the review gives: accept=[[key, reason]] — they stay in their review with the reason and stop counting (score, pendencies, dry runs); an empty reason takes one back. prune=true drops every acceptance whose finding is gone (the reviews list them as orphaned). One undoable step. Reply ok, with accepted and removed counts; read the review again to see it. |
| annotationsB | Plan annotations, read. stale=true lists what no longer matches: notes whose numbers differ from their piece, piece names whose sizes ( |
| arrangeA | Arrange elements in one undo step. array {ids,n,dx,dy,dz} adds n copies stepping by dx/dy/dz cm (rafters, columns); align {ids,axis,edge:low|middle|high,value} lines pieces up by an edge — backs on one line, fronts on another — instead of by centers you work out yourself; distribute {ids,axis,gap?} sets them side by side in the order given, from where the first one is (a run of joinery); flip {ids} turns a piece back to front, rebuilding what is inside it (mirror only swaps left and right and leaves the front where it was); rotate {ids,angle clockwise,about?,copy?}; mirror {ids,a,b,copy?} across the line a-b; group {ids,name?} joins pieces into one box that moves/hides together, ungroup {ids:[group]}; front/back {ids} draws rooms or pieces on top/underneath (pool over deck, rug under sofa). Returns new ids. |
| backgroundA | Walls detected in the scanned plan under the drawing ( |
| cabinet_runA | Fill a wall with cabinets sized for it: measures the free stretches between corners, doors, windows, fridge and stove, splits each into even modules (30-90 cm, no useless leftovers; 15-30 cm pull-outs, fillers under 15; the defaults target=60 max=90 sink_w=80 cooktop_w=60 are the nominal widths of EN 1116, which is what appliances and hardware are made for — bespoke widths in between are fine for a run that receives none), drawer unit beside the stove, countertop on base rows, cabinet over the fridge and hood gap on wall rows, wardrobes (hanging rails, shelves, drawers) on tall rows facing bedrooms; p.sink/p.cooktop place those cabinets and cutouts. Replaces the cabinets already there (keep ids stay). Reply {modules:[[id,role,from,w]],removed,notes}; dry plans only. Change one module afterwards with joinery id. |
| camerasA | Stored points of view: {active: visitor|aerial, rows [i,name,x,y,z,yaw,pitch,fov]}, cm and degrees. Change them with edit_cameras. |
| catalogC | Find catalog items: rows [id,name,w,d,h,front?] in cm. front, when the item has one, is |
| createA | Create walls (polylines; hs = height per point for gables), rooms (pts, or at=[x,y] to detect from walls; ceiling_flat=false follows wall profiles instead of the default flat ceiling), dims (a+b or wall id), labels, roofs (rectangle pts, gable|shed, pitch or ridge_h, eave h, overhang, gables=true closes the ends, skylights [{at,w,d}] cut glazed openings) and solids (pts outline raised by h at elev: slabs/mezzanines of any shape; or profile [[u,z]] swept from a to b: gables, ramps) in one atomic step. |
| cut_listB | Cut list of joinery builds, and of groups drawn by hand: each part of a drawn group up to 50 mm thick is a board at the size it was drawn (board is its finish and thickness, no edge banding), the groups are named in drawn and the parts that are not boards in skipped [[id,name]]. Rows [part,board,qty,length,width,thickness mm,edge long+short,cutouts [x,y,w,d] mm?] merged by size, hardware, sheets per board. export what=cut_list writes it as .csv, or .dxf/.svg (boards laid out on sheets). sources names the panel standards behind the boards: MDF is a dry-process fibreboard (NBR 15316), MDP a particleboard of 551 to 750 kg/m³ that holds screws better (NBR 14810). |
| deleteA | Delete (remove) walls, rooms, pieces or any element by id, atomically. Labels left pointing at a deleted piece — about it, or standing on it — are named in the reply as labels_left [[id, text]], since an index code over what is now another piece is found by nobody; labels=true deletes them in the same step. |
| disciplinesA | Electrical and plumbing projects and plan layers over the plan. active (default): {active, hidden, layers {key:{pieces, hidden}}} — the discipline new symbols go to, what is hidden, and the layers (lighting, appliances, joinery); home gives each piece its plan_layer. quantities: {electrical:[[kind,count,names?]], plumbing:[...], lines_cm:{...}} — grouped by catalog kind (every low outlet together), with the names of the points as the detail. Change what is shown with edit_disciplines. |
| edit_annotationsA | Change the plan annotations. anchor=true ties every straight dimension to what its ends touch now, so it is measured again on every change instead of drifting (tie them while the numbers are right); one already off its face is not tied and comes back in left [[id, written, measured, near]], and one whose anchor died with a deleted piece is tied again or released. Switch dims (dimension chains), refs (the room reference schedule, whose tags stay with their pieces; renumber=true numbers them again in reading order), details (brand/model/link in refs) and legend (symbols with counts): a switch answers with the modes, changed, and what it shows — chains [[from,to,cm]] for dims, symbols {discipline:[[name,count]]} for legend; refs=true returns the schedule {dims,refs,details,legend,rooms:[[room,[[tag,name,w,d,h,brand?,model?,url?]]]]}. bake=true turns the automatic chains into editable dimensions (ids returned). |
| edit_backgroundA | Put a scanned plan under the drawing at real scale, or turn it into walls. set {path, then cm_per_px (+cm_per_px_y), calibrate {a,b px, cm} or calibrations [{a,b,cm}…] to fit X/Y scales; angle (clockwise °); offset, opacity, visible; clear=true removes it}. trace {threshold?, min_len?, t_min?, t_max?, max_gap?, region?, h?} creates, in one undo step, the walls |
| edit_camerasA | Change the points of view. view {i} shows stored view i in the 3D window; aerial returns to the orbit view; store {name?,x?,y?,z?,yaw?,pitch?,fov?,look_at?:[x,y,z]} saves one (missing values from the visitor; yaw 0 looks toward +y/plan bottom, 90 toward -x; pitch positive looks down); delete {i}. cm and degrees. The list is the cameras tool. |
| edit_disciplinesA | Change what the plan shows. select {d: electrical|plumbing|architecture}: new symbols (catalog cat electrical/plumbing) and lines go there and the rest is dimmed; select architecture hides the electrical and plumbing projects (show brings one back); select a project shows it with the architecture dimmed. show/hide {d}, where d can also be a layer of the plan — lighting (lamps, spots, LED), appliances (fridge, stove, oven, hood, washer…) or joinery (cabinets, wardrobes, countertops): hidden from the plan, its exports and the 3D. The 3D follows the plan unless show_all_3d=true, which makes it show everything. Pieces are in a layer by what they are; update(layer=lighting|appliances|joinery|none) overrides it. What is shown now is the disciplines tool. |
| edit_electricalA | Change the electrical and telecom project ( |
| edit_historyA | Undo, redo, or name where the plan is now and come back to it. undo and redo step through changes, whoever made them, and the reply names what changed. checkpoint {label} remembers this point; revert {label} undoes back down to it, keeping every id — which duplicating a version cannot do, since a copy renumbers. A checkpoint lives with the project and survives saving; |
| edit_homeA | Rename the project, set or remove project properties (properties {key: value|null}, or a storey's with level), set the compass (north), set the city whose building code applies (city=sao-paulo), and say who lives there (people {occupants, children, elderly, wheelchair, stature cm, scope}). City and people belong to the project: |
| edit_levelsA | Change the storeys. add {name?,h?,elev?} adds one on top (or at elev cm) and selects it; select {id}; delete {id} removes it and its content. update {id, elev?|h?|name?|reference?}: elev raises a storey with its walls, floors and openings (houses on stilts); reference=true marks it a tracing layer (imported plan, older version) that checks and ergonomics skip, which is what you want when two storeys share an elevation. Other tools act on the selected storey. The list is the levels tool. |
| edit_libraryA | Publish to the asset library: action=publish keeps a piece's model file — with its companion files, reference pictures, measured sizes, unit, box, brand and source link — as the next version of name, with notes on what changed and fidelity (what was estimated). A version never changes after; library lists them. |
| edit_lightingA | Fill a room with a grid of one fixture (downlight, led-panel, light-ceiling, pendant) until the work plane reaches the room's ABNT NBR ISO/CIE 8995-1 reference, or lux; the grid is checked by the same photometry as |
| edit_modelA | Change the imported model of placed pieces (ids), one undo step each; rev refuses a change if the plan moved on. replace swaps the file (library:@ too; every=true: on every piece using it), keeping id, place, size (size=natural takes the file's), finish, overrides and links; the new file is read first, and the reply gives the version loaded, overrides left matching nothing and what the file leaves out. material changes one material by the name model lists: color, mat (an image), repeat (texture scale: 2 halves it), clear. part hides, moves (offset cm: x across, y to the front, z up) or resizes one part by name — the back cushion alone. lod adds a lighter file drawn beyond a camera distance (beyond cm, default 400); detail=always holds the full one. reference keeps a product photo (file, view, part, source) that render_3d ref= shows beside the model; measure keeps a product size (dimension: width|depth|height|.height, cm, source, confirmed) that model compares with the drawing. model inspects; place imports. |
| edit_plumbingA | Lay a pipe run of the plumbing project ( |
| edit_variantsA | Change the plan versions (tabs): duplicate {name?} copies the active one, new {name?} starts an empty one — both switch to it; switch {i}; rename {i,name}; delete {i}. Edits apply to the active version. The list is the variants tool. |
| edit_videoA | Change the video camera path, or render it. add {cam? | x?,y?,z?,yaw?,pitch?,fov?, i?} appends a keyframe (missing values from the visitor); delete {i}; clear; orbit {z?,n?} replaces the path with an aerial tour; set {fps?,speed?}; render {path .avi, w?,h?} writes a Motion-JPEG video. cm, degrees, m/s. The path itself is the video tool. |
| edit_wallsA | Split a wall in two, or join walls into one. split {id, t?} cuts a wall into two joined walls at t (0..1, default 0.5). merge {ids} joins walls running along one line — the wall a partition interrupted, a stretch imported as many segments, the same wall drawn twice: the longest keeps its id and its build (thickness, height, type, finishes) and spans them all, the others are deleted, doors and windows stay where they are, and a gap between them is closed. The reply names the ids that result. |
| electricalA | Electrical and telecom review, NBR 5410 and NBR 16264: points are the catalog's electrical pieces (outlets, switches, lighting points, panels, RJ45, TV, Wi-Fi, automation) plus every fixture that lights. action check (default) answers {points:{kind:count}, findings:[[sev, place, msg, src, key, accepted?]], pending, orphaned, sources}: outlets and lighting per room, circuits, DR, the panel, voltage drop, access points' cables. circuits answers the load schedule, rows [name, kinds, points, VA, V, A, wire mm², breaker A, DR], with main_breaker {a, phases, load_a_per_phase} and panel {devices, modules, dps, earthing, icn_ka, selective}. wifi {band?} answers access_points, coverage per room and band [[room, band, median dBm, worst dBm, share at -67 dBm or better, grade]] and suggested ceiling points. What each check demands: |
| embedA | Embed an item into joinery with an exact fit: a sink bowl or cooktop into a countertop (cutout from the item's size, generic fixture not drawn; the wooden top of a joinery cabinet under it is cut too), an oven, microwave or other appliance into a cabinet niche (doors above and below, boards around it), a TV onto a slatted panel at seated eye level (z = screen center). The item becomes part of the host and moves with it. item: id in the plan, or cat (+w/d/h) for a new one. Errors say what to change (e.g. use w = 61 no armário). Reply {host, item, kind, cutout|niche, x|bottom, cut_below, notes}. |
| ergonomicsA | Ergonomics and habitability review for the people living there: room to walk beside beds and in front of kitchen equipment, beds, seats, bathrooms and wardrobes per person, the kitchen (work triangle, counter heights and lengths, work zones, sockets, gas ventilation, extraction), doors, ceiling heights, windows, minimum furniture, wheelchair turning. It advises and never blocks. Answers {score, score_basis, scope, scores:{architecture,electrical,plumbing}, layout, coverage, capacity, findings:[{sev, place, msg, key, weight, discipline, in_scope, src?, fix?, accepted?}], sources:{src:[title, tier, url]}}. weight is what the score gains if that finding goes; fix is a checked change as |
| exportA | Export to a file, by its extension. what=plan (default): the plan as .pdf (A3; scale=50/100 or fit), .svg (true scale) or .png, or the 3D model as .glb or .obj. what=cut_list: the joinery's cut list (and that of groups drawn by hand) as .csv (spreadsheet), .dxf (boards laid out on sheets, for CNC) or .svg (sheets to view); |
| feedbackA | Report to the developers what a tool could do better, as it happens: a reply that answered less than was asked, a detour, a wrong conclusion reached before the right one, a fix that would distort the plan, something missing. It is a report the fix will be made from, so give the whole case — a vague note gets fixed in a way that breaks what already works. Every part is required: goal (the task and the situation: pieces, rooms, numbers), tried (the exact call with arguments), got (the literal reply), expected (what was true and how you found out), cost (the workaround, extra calls, wrong conclusion), would_help (the concrete change), must_keep (what works today and must not get worse with that change; "none known" is an answer). kind friction|bug|idea, tool. Kept on this machine and, while the user's telemetry is on, sent to the project. Reply {kept, sent}; then carry on with the task. |
| fileA | Start, open or save the project. new: an empty project in place of this one. open {path}: a project (.newera), or a Sweet Home 3D file (.sh3d) imported, in place of this one. save {path?}: writes the project (.newera); path is optional once it was saved. |
| fit_roofA | Fit walls, glass and panels to the roof above them: under an A-frame or shed roof a wall gets a sloping top and is split at the ridge, a panel becomes a triangle or trapezoid (a glass gable with no math); a joinery slatted panel gets its slats cut to the roof line. They keep following the roof when it changes, in the same undo step; off stops that. Reply ok with the count. |
| historyA | The checkpoints |
| homeA | Home state. detail=summary is cheapest. Ask for less instead of reading everything: ids=[…] resolves ids (group parts included), room=<id|name> and rect=[[x0,y0],[x1,y1]] read one place, kinds=[walls|rooms|dims|labels|furniture|polylines] and fields=[…] trim each row, parts=true opens groups, ndjson=true prints one element per line so a long answer can be read a slice at a time. Every piece carries bounds (plan box with angle applied) and faces (the side it opens toward). Ids share one counter per version (w1, r2, f3…) and are never reused, so a new version may start at any number. |
| joineryA | Parametric joinery and interiors: the server computes every board, clearance and rule and answers {id,name,size,parts,hardware,notes}. Workshop advice (a board nobody stocks, a sagging shelf, a short drawer front) comes back in notes and the piece is built anyway; only what has no geometry fails, saying why. kind + p: cabinet {w,h,d cm; t 15|18|25 mm; back mm; door hinged|sliding|drawers|none; doors; shelves; drawers; dividers; plinth; cooktop; top_cutout [x,y,w,d] cm from the left-back; color; front finish} · slats {w,h cm; slat, thickness, gap mm; orientation vertical|horizontal; backing; finish} · countertop {length,depth,height,thickness cm; material; support none|legs|brackets; cutouts [{kind sink|cooktop|grommet, x, w?, d?}]} · cove {room or pts; type open|closed|inverted; ceiling, width, drop, slot cm; led} · shadow_gap {room or pts; ceiling, gap, depth cm; led} · sofa {length,depth,seat,back cm; arms straight|rounded|none; modules; color}. Place with at|wall(+along), angle, elev. Change a build with id + p holding only the new values; a new size keeps its back (anchor back|front|left|right|bottom|top|+x|-x|+y|-y|center). dry=true validates only. |
| layoutA | Layout problems on a storey — pieces colliding or clashing, blocked doors and windows: overlap, blocked, in_wall, blocks_door, blocks_window, no_door, backwards, turned, unrated_light, unclear_front, loose_opening, outgrew_niche, outside_rooms, loose and above_ceiling, as {kind: [rows]}; {} means none. Every row is an object with the |
| levelsA | Storeys (floors, levels) of the project: rows [id,name,elev,h,selected,layout_index,viewable,reference]. Other tools act on the selected storey; change them with edit_levels. |
| libraryB | Versions kept in the asset library: approved revisions of model files, reusable in any project with place model=library: or library:@; edit_model replace file=library:@ goes back to an older one. Rows [name, latest, title, brand, versions]; name= lists its versions [v, title, size, unit, source, fidelity, notes, pictures, sizes]. edit_library publishes. |
| lightingA | Lighting review: lux (illuminance) per room by photometry. Every fixture's flux (lm, or W × lamp efficacy), color temperature and distribution (bulb, spot beam, LED panel/strip) lights the work plane by the inverse-square cosine law, walls casting shadows, plus interreflection (split flux). Reply rooms [[id,name,m²,avg lx,min lx,uniformity,reference lx,fixtures,W/m²,verdict]] against ABNT NBR ISO/CIE 8995-1 residential references. edit_lighting places the fixtures a room needs. Set a piece's light with place/update light {lm|w,lamp,k,beam,area}. |
| materialsA | Wall types [id,name,t] (drywall, masonry, concrete…) and finish patterns [key,label,color,tile]. |
| measureA | Tape measure over the plan, in cm. from= alone: free floor on all four sides, {clear:{"+y":[cm,id,name]}} — dirs picks sides (+x -x +y -y, or front/back/left/right of the piece). from+to (ids or [x,y]): the distance between them, {cm}, or the gap along axis. axis+at: what a straight probe runs into, {spans:[[from,to,id,name]]} with id null for free floor — the answer to "how wide is the corridor here, and between what". Add gap= and grow= and it also answers how big that piece can be along the axis before the gap is broken, and what stops it there: {fit:{free,max,blocked_by}} — the question a layout decision actually ends in, without trying a size and undoing it. z limits the height band that counts (default 0-200). |
| modelA | Inspect an imported 3D model, a piece's (id) or a file before placing it: the unit taken (guessed from its size unless unit= or the piece says), natural size, triangles and where they go, materials with their images, named parts (OBJ objects and groups, glTF nodes: arms, seat, frame) with bounds and materials, and warnings for what is not drawn as the file says (a texture or material library not found, normal and roughness maps, extensions). For a piece that looks wrong after place model=…, instead of reading the file. part= keeps the parts named so; check=clashes finds parts passing through one another (a cushion through a rail), not those that touch. Reply {file, format, unit, raw, size cm, tris, materials [[name, color, image, tris]], parts [[name, tris, min, max, materials, "hidden" when hidden]] in cm of the piece (x across, y to the front, z up), cost {tris, heaviest [[part, tris, %]], copies, in_project}, warnings}; with id also piece {size, scale, far}; check adds clashes [[part, part, pairs, at, depth cm]], degenerate and checked (what was looked at). place model=… imports one. |
| moveB | Move elements by dx,dy cm, or to=[x,y]: the first id (a piece or a label) goes there and the rest keep their offsets. dry=true answers what it would do without writing anything, dry="summary" answers it short; see |
| placeA | Place catalog items, or copy= of a piece already in the project (model, finish and parts; |
| pluginsA | Plugins (external programs editing through the HTTP API): rows [name,title,description]. Run one with run_plugin. |
| plumbingA | Plumbing review, NBR 5626 (cold and hot water) and NBR 8160 (sewer): points are the catalog's plumbing pieces (water and sewer points, valves, drains, traps, grease trap, inspection box, meter, gas, vent) and fixtures are the pieces that use water. Answers {points:{kind:count}, pipes_m, findings:[[sev, place, msg, src, key, accepted?]], pending, orphaned, sources}: water and sewer points by every fixture, discharge diameters, floor drains and traps in wet rooms, grease trap, where the water comes from and the sewer goes, points no pipe reaches. The drain models and what each check demands: |
| render_3dA | PNG of the home in 3D (software render with outlines, no GPU needed). view: front|back|left|right|top orthographic elevations — front looks from the plan's bottom edge (large y) toward y=0, back from y=0 toward large y, left from x=0, right from large x; cut=cm makes a section keeping only what is beyond that plane from the viewer (front cut=200 keeps y<200, so the wall at y=0 stays as the backdrop; to remove it look from back), aerial (default; frames the whole building; yaw degrees: 0 from east/+x, 90 from south/plan bottom (default 60); pitch down; zoom >1 farther), visitor (current visitor camera) or cam=i (stored point of view). walls=cutaway drops the walls between the eye and a room to 40 cm, walls=down drops them all; either hides ceilings, roofs and the doors, windows and wall pieces of lowered walls, to see the furniture from the side. piece= draws it alone, seen from its own side (view; aerial is three-quarter), framed the same wherever it stands; part= frames one part of its model; ref=i puts its reference photo beside it, from the photo's side. Keep w/h small. |
| render_photoA | Realistic photo (path traced: sun from compass location and time, lamps, glass). cam=i stored view, or view=visitor/aerial (yaw,pitch). quality draft (~10 s) | good | best; hour = local solar time (e.g. 9, 15.5, 20); w/h small. Returns PNG. One runs at a time, on at most half the cores (4): threads lowers that, max_s stops it past a deadline, estimate=true answers the budget and the work (pixels, rays, triangles, memory) without rendering, cancel=true stops the one running (sessions shows it). |
| render_planA | PNG of the floor plan, exactly as the user sees it. region=[[minx,miny],[maxx,maxy]] frames a place — it is fitted to the image's aspect by growing the short side, never by cropping, so everything asked for is in the picture — or room=<id|name> with pad cm (default 30) frames a room without working the rectangle out. bg=0..1 overlays the background image to compare with the reference. Keep w/h small to save tokens. |
| rulesA | The rules behind a review, as Markdown: what |
| run_pluginA | Run a plugin (an external program editing through the HTTP API) by name, with args as JSON: {ok,code,stdout,stderr,edits,revision,usage {peak_mb,cpu_s,measured},limits,not_applied}. A plugin as heavy as a modeler gets the limits its plugin.json declares (cores, memory, priority, time) that this system can apply, not_applied naming the rest, and runs alone, never beside a photo render. The plugins tool lists them. |
| sessionsB | People and agents on this project now: rows [id,name,cursor,selection,edits]; render, while one runs: {tool, seconds, phase, done, total, threads} (render_photo cancel=true stops it). |
| show_planA | Show the plan to the user as an interactive viewer (pan, zoom, 3D view, open in the editor) where the chat client can display one. Reply: a one-line summary for you; the drawing goes to the viewer. To look at the plan yourself use render_plan. |
| updateA | Change fields of elements by id; each field applies only to the kinds that have it (furniture mat/opacity/pitch, wall h_end, room auto/ceiling_flat, polyline divider). A part of a group takes name, brand, model_name and url on its own; its size and place belong to the group. anchor on a resize holds one face still instead of growing around the center; stretch=[part ids] on a group resize grows only those parts and moves the rest along (uprights keep 5.8 cm while the opening grows). dry=true answers what it would do without writing: changed fields, clearances around each piece it touches (negative: cm it would sit inside what it faces), issues_resolved/issues_new [{ids, kind, extent|cm|over}] with the kinds |
| variantsA | Plan versions (tabs): rows [i,name,active,walls,rooms,m2,furniture,issues]. Edits apply to the active version; change them with edit_variants. |
| videoA | The video camera path: {fps,speed,secs,rows [i,x,y,z,yaw,pitch,fov]}, cm, degrees, m/s. Change it and render it with edit_video. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| plan-viewer | Interactive view of the floor plan, with a 3D view on demand. |
| rules-electrical | |
| rules-plumbing | |
| rules-layout | |
| rules-ergonomics |
TDQS
Scored across 57 tools
The read/edit pairs (e.g., cameras/edit_cameras, electrical/edit_electrical) follow a clear pattern, and domain tools like joinery vs cabinet_run are distinguishable. However, the large set of generic element manipulators (create, place, update, move, arrange, delete) sits alongside specialized builders, creating some boundary risk for an agent.
Names are consistently snake_case and use a predictable read=noun / write=edit_noun convention for most resources. Deviations include bare verbs (create, place, delete, move, arrange) and compound verb_noun names (render_plan, show_plan), so it is not a single uniform pattern.
57 tools far exceeds the typical 3–15 range and the 16–25 'heavy' threshold, placing a large selection burden on an agent. While each tool covers a real capability in a broad CAD/architecture domain, the count is still excessive for practical coherence.
The surface spans CRUD for geometry, reviews (electrical, plumbing, lighting, ergonomics, layout), rendering, export/import, versions, history, plugins, and collaboration. Only minor gaps exist, such as no direct edit_materials for wall types/finish patterns, but overall lifecycle coverage is very strong.