The standard loop for letting an agent write
The order of work when an agent edits .muro files.
model_summary → layers → write_layer → check ──errors──→ fix, write_layer again
│
└──green──→ doors / light / site confirm the consequences
The shape is the same as working in git: grasp it, read it, write it, pass the gatekeeper, confirm the consequences. Agents go wrong in one of two ways — writing without reading, or moving on without passing the gatekeeper.
Registering the server is not covered here; it is on Register the MCP server with a client. Each tool's arguments and return shape are on Read / Write / Verify / Ask.
Every output below was actually run. Absolute paths are abbreviated to <dir>/.
The subject
A single-storey pair of rooms. There is one entrance door, and nothing at all between the two rooms.
muro 1.5
name 平屋
unit mm
grid X 0 3600 7200
grid Y 0 4500
level L1 0 h:2400 slab:150
import ./L1.muro
space /L1/a room X1..X2 Y1..Y2 name:居室A
space /L1/b room X2..X3 Y1..Y2 name:居室B
space /out name:外部 outside:1
boundary /L1/b /out t:150 spec:EW edge:S
door w:900 h:2100 name:玄関
0. Commit
git add . && git commit -m "before the agent edits"
write_layer replaces a layer whole and has no undo. Skip this and there is no way back.
1. model_summary — grasp the building
One call returns the layer composition, the levels, the zones, the areas and the check counts. This is what decides which file to read next.
{
"name": "平屋",
"unit": "mm",
"layers": [
"<dir>/main.muro",
"<dir>/L1.muro"
],
"levels": [
{
"name": "L1",
"z": 0,
"h": 2400,
"slab": 150
}
],
"spaces": 3,
"boundaries": 2,
"zones": [],
"assets": [],
"totalFloorM2": 32.4,
"semiOutdoorM2": 0,
"floorsM2": {
"L1": {
"rooms": 2,
"subtotalM2": 32.4
}
},
"check": {
"errors": 0,
"warnings": 0
}
}
(The one-line hint field is omitted.)
0 / 0 on check, and the building is still unusable. There is no way out of room A. The summary only says that what is written does not contradict itself.
boundaries counts the composed model, including the walls derived by default. It does not match the number of boundary lines in the source.
2. layers — read the original
Every layer taking part in the composition comes back in full as {file, source}. import is followed automatically, and a file nobody imports is not returned.
What the agent must read before editing is this, not the summary. The summary carries structure only — spelling, ordering and comments are not in it.
3. write_layer — write
Three arguments: the entry (file), the target layer (layer), and the whole text (content). Not a diff.
Hanging a door between room A and room B therefore means rewriting L1.muro in full.
space /L1/a room X1..X2 Y1..Y2 name:居室A
space /L1/b room X2..X3 Y1..Y2 name:居室B
space /out name:外部 outside:1
boundary /L1/a /L1/b t:120 spec:PW
door w:800 h:2000 name:D-中扉
boundary /L1/b /out t:150 spec:EW edge:S
door w:900 h:2100 name:玄関
The return carries the check result from immediately after the write. Editing and verifying cost one round trip together.
{
"written": "<dir>/L1.muro",
"ok": true,
"spaces": 3,
"errors": [],
"warnings": []
}
The gatekeeper stands before the write
write_layer composes the replacement virtually first. Content that cannot be parsed never touches the original.
{
"written": false,
"target": "<dir>/rooms.muro",
"ok": false,
"parseError": "<dir>/rooms.muro:line 1: Undefined grid line name: X9"
}
Content that parses but fails check is written. That is deliberate: it lets an edit that spans several layers proceed in steps. written carries the path and ok is false. Fix it on the next call.
{
"written": "<dir>/rooms.muro",
"ok": false,
"spaces": 2,
"errors": [
"<dir>/rooms.muro:line 3: References an undefined space: /L1/c"
],
"warnings": []
}
The restrictions on where it may write, and its atomicity, are on Write — write_layer / new_uids. When creating a new layer, put the added import line in the same unit of work — a file nobody imports never joins the composition, so its contents are never checked.
4. check — pass the gatekeeper
It rides along on the write_layer return, so a green result moves straight on. An error means fix it and write again. A workflow that walks past this point produces drawings from a broken building.
The diagnostics array on the return is the structured, coded form. Human-readable check output carries no codes, but the MCP return has them from the start. The table from code to fix is the diagnostic code index.
5. Confirm the consequences
This is the step people skip. check looks at neither circulation nor daylight nor the site. That an edit had the intended consequence has to be established by a separate question.
Before the edit, room A could not reach the outside.
{
"unreachable": true
}
After one door, the same question answers:
{
"doors": 2,
"path": [
"/L1/a",
"/L1/b",
"/out"
]
}
And check keeps saying what silence decided. The building above is enclosed — every side of both rooms is a wall — but only one of those walls was written, so the rest carry no name.
{
"code": "BND08",
"severity": "warning",
"message": "A default wall was derived where /L1/a faces the outside: S 3600mm / N 3600mm / W 4500mm (11700mm over 3 runs) — write a boundary to say which outside it faces",
"line": 5,
"path": [ "/L1/a" ]
}
{
"code": "BND08",
"severity": "warning",
"message": "A default wall was derived where /L1/b faces the outside: E 4500mm / N 3600mm (8100mm over 2 runs) — write a boundary to say which outside it faces",
"line": 6,
"path": [ "/L1/b" ]
}
This is the one warning an agent should not skip. The shape is complete either way; what the message asks for is the decision silence could not make — whether that face looks at a road, a neighbour or a garden. Frontage, daylight and specification all read it. boundary /L1/a /out answers it in one line.
validate answers a different set of questions and takes profile and asOf as well as file, inferring neither. Against this building it returns no findings at all: both rooms reach the outside through the front door.
Which question to ask is decided by what was edited.
| What changed | The question to ask |
|---|---|
| Partitions, doors | doors — reachability and door count |
| Windows, room types | light — floor area and effective window area |
| Regions, levels | site — coverage and floor-area ratios |
| Anything at all | validate — the architectural findings |
The rules to hand the agent
Put these in the instructions and the accidents mostly stop.
- Read with
layersbefore writing. Writing from the summary loses spelling and comments. write_layeris a whole-file replacement. Thecontentit sends must be the complete text of the edited file.- When
ok: falsecomes back, the only next action is to fix it and write again. - Never claim it works because it is green. A building with no doors is sealed and
checkis happy. - Nothing mints a
uidon its own. Callnew_uidsonly when a space must be pointed at across renames, and runcheckafterwards. - Form is generated. A plan cannot be "written".
plan_svgderives and returns it.
Related
- Register the MCP server with a client — getting connected
- Drive the MCP server by hand over stdio — take the agent out and inspect behaviour
- Lay measurements over the plan — overriding without rewriting the original
- koyu-mcp — statelessness and the tool ledger
- The scope of the promise — what a green
checkmeans