@pascal-app/mcp 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/README.md +166 -62
- package/dist/bin/pascal-mcp.js +1 -1
- package/dist/bridge/agent-session.d.ts +18 -0
- package/dist/bridge/agent-session.d.ts.map +1 -0
- package/dist/bridge/agent-session.js +15 -0
- package/dist/bridge/index.d.ts +1 -1
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.d.ts +3 -0
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +7 -0
- package/dist/lib/safe-fetch.d.ts +22 -4
- package/dist/lib/safe-fetch.d.ts.map +1 -1
- package/dist/lib/safe-fetch.js +233 -134
- package/dist/operations/scene-operations.d.ts +18 -0
- package/dist/operations/scene-operations.d.ts.map +1 -1
- package/dist/operations/scene-operations.js +11 -0
- package/dist/prompts/from-brief.d.ts +1 -1
- package/dist/prompts/from-brief.d.ts.map +1 -1
- package/dist/prompts/from-brief.js +3 -3
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/iterate-on-feedback.d.ts +1 -1
- package/dist/prompts/iterate-on-feedback.d.ts.map +1 -1
- package/dist/prompts/iterate-on-feedback.js +2 -2
- package/dist/prompts/renovation-from-photos.d.ts +1 -1
- package/dist/prompts/renovation-from-photos.d.ts.map +1 -1
- package/dist/prompts/renovation-from-photos.js +2 -2
- package/dist/prompts/scene-guidance.d.ts.map +1 -1
- package/dist/prompts/scene-guidance.js +10 -3
- package/dist/resources/agent-guide.d.ts +1 -1
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +52 -44
- package/dist/resources/catalog-items.d.ts +1 -1
- package/dist/resources/catalog-items.d.ts.map +1 -1
- package/dist/resources/constraints.d.ts +1 -1
- package/dist/resources/constraints.d.ts.map +1 -1
- package/dist/resources/constraints.js +1 -1
- package/dist/resources/design-schema.d.ts +1 -1
- package/dist/resources/design-schema.d.ts.map +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/scene-current.d.ts +1 -1
- package/dist/resources/scene-current.d.ts.map +1 -1
- package/dist/resources/scene-summary.d.ts +1 -1
- package/dist/resources/scene-summary.d.ts.map +1 -1
- package/dist/server.d.ts +7 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +59 -21
- package/dist/storage/disk-artifact-store.d.ts.map +1 -1
- package/dist/storage/sqlite-scene-store.d.ts +1 -1
- package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
- package/dist/storage/types.d.ts +4 -4
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/templates/empty-studio.d.ts +3 -3
- package/dist/templates/empty-studio.d.ts.map +1 -1
- package/dist/templates/garden-house.d.ts +3 -3
- package/dist/templates/garden-house.d.ts.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/two-bedroom.d.ts +3 -3
- package/dist/templates/two-bedroom.d.ts.map +1 -1
- package/dist/tools/add-column.d.ts +1 -1
- package/dist/tools/add-column.d.ts.map +1 -1
- package/dist/tools/add-column.js +3 -2
- package/dist/tools/add-object.d.ts +19 -3
- package/dist/tools/add-object.d.ts.map +1 -1
- package/dist/tools/add-object.js +66 -35
- package/dist/tools/annotations.d.ts.map +1 -1
- package/dist/tools/apply-patch.d.ts +6 -2
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +13 -8
- package/dist/tools/asset-catalog.d.ts.map +1 -1
- package/dist/tools/asset-catalog.js +23 -0
- package/dist/tools/author-scene.d.ts +10 -0
- package/dist/tools/author-scene.d.ts.map +1 -0
- package/dist/tools/author-scene.js +140 -0
- package/dist/tools/check-collisions.d.ts +6 -6
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +6 -6
- package/dist/tools/clear-scene.d.ts +1 -1
- package/dist/tools/clear-scene.d.ts.map +1 -1
- package/dist/tools/clear-scene.js +3 -3
- package/dist/tools/collections.d.ts +1 -1
- package/dist/tools/collections.d.ts.map +1 -1
- package/dist/tools/collections.js +3 -2
- package/dist/tools/construction-tools.d.ts +6 -49
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +9 -158
- package/dist/tools/create-unit.d.ts +3 -3
- package/dist/tools/create-unit.d.ts.map +1 -1
- package/dist/tools/create-unit.js +2 -2
- package/dist/tools/errors.d.ts +5 -5
- package/dist/tools/errors.d.ts.map +1 -1
- package/dist/tools/errors.js +4 -4
- package/dist/tools/export-glb.d.ts +1 -1
- package/dist/tools/export-glb.d.ts.map +1 -1
- package/dist/tools/export-glb.js +2 -2
- package/dist/tools/export-json.d.ts +1 -1
- package/dist/tools/export-json.d.ts.map +1 -1
- package/dist/tools/export-json.js +2 -2
- package/dist/tools/get-scene.d.ts +1 -1
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +2 -2
- package/dist/tools/hosted-services.d.ts +1 -1
- package/dist/tools/hosted-services.d.ts.map +1 -1
- package/dist/tools/hosted-services.js +13 -3
- package/dist/tools/index.d.ts +4 -3
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +8 -13
- package/dist/tools/level-height-guard.d.ts +2 -0
- package/dist/tools/level-height-guard.d.ts.map +1 -0
- package/dist/tools/level-height-guard.js +11 -0
- package/dist/tools/list-units.d.ts +2 -2
- package/dist/tools/list-units.d.ts.map +1 -1
- package/dist/tools/list-units.js +2 -2
- package/dist/tools/live-sync.d.ts +1 -1
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +4 -4
- package/dist/tools/measure.d.ts +1 -1
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +7 -7
- package/dist/tools/node-world-point.d.ts +1 -1
- package/dist/tools/node-world-point.d.ts.map +1 -1
- package/dist/tools/normalize-schema-dialect.d.ts +1 -1
- package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
- package/dist/tools/normalize-schema-dialect.js +2 -3
- package/dist/tools/patch-guards.d.ts +6 -4
- package/dist/tools/patch-guards.d.ts.map +1 -1
- package/dist/tools/patch-guards.js +5 -2
- package/dist/tools/photo-to-scene/index.d.ts +1 -1
- package/dist/tools/photo-to-scene/index.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts +3 -3
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +7 -7
- package/dist/tools/project-files.d.ts +4 -0
- package/dist/tools/project-files.d.ts.map +1 -0
- package/dist/tools/project-files.js +30 -0
- package/dist/tools/redo.d.ts +2 -2
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +2 -2
- package/dist/tools/retired-tools.d.ts +10 -0
- package/dist/tools/retired-tools.d.ts.map +1 -0
- package/dist/tools/retired-tools.js +22 -0
- package/dist/tools/roof-level-guard.d.ts +2 -0
- package/dist/tools/roof-level-guard.d.ts.map +1 -0
- package/dist/tools/roof-level-guard.js +22 -0
- package/dist/tools/room-tools.d.ts +47 -46
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +6 -5
- package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +5 -5
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +6 -6
- package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.js +6 -6
- package/dist/tools/scene-lifecycle/index.d.ts +1 -1
- package/dist/tools/scene-lifecycle/index.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.js +4 -4
- package/dist/tools/scene-lifecycle/load-scene.d.ts +2 -2
- package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/load-scene.js +5 -5
- package/dist/tools/scene-lifecycle/metadata.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.js +6 -6
- package/dist/tools/scene-lifecycle/save-scene.d.ts +5 -3
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +80 -12
- package/dist/tools/scene-lifecycle/test-utils.d.ts +1 -1
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
- package/dist/tools/set-unit-members.d.ts +2 -2
- package/dist/tools/set-unit-members.d.ts.map +1 -1
- package/dist/tools/set-unit-members.js +6 -6
- package/dist/tools/set-zone.d.ts +2 -2
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +30 -18
- package/dist/tools/shared-tools.d.ts +15 -5
- package/dist/tools/shared-tools.d.ts.map +1 -1
- package/dist/tools/shared-tools.js +242 -29
- package/dist/tools/structure-tools.d.ts +3 -3
- package/dist/tools/templates/create-from-template.d.ts +2 -2
- package/dist/tools/templates/create-from-template.d.ts.map +1 -1
- package/dist/tools/templates/create-from-template.js +6 -6
- package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
- package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
- package/dist/tools/templates/create-house-from-brief.js +6 -6
- package/dist/tools/templates/index.d.ts +1 -1
- package/dist/tools/templates/index.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.d.ts +1 -1
- package/dist/tools/templates/list-templates.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.js +2 -2
- package/dist/tools/undo.d.ts +2 -2
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +2 -2
- package/dist/tools/unit-members.d.ts.map +1 -1
- package/dist/tools/unit-members.js +6 -6
- package/dist/tools/variants/generate-variants.d.ts +6 -6
- package/dist/tools/variants/generate-variants.d.ts.map +1 -1
- package/dist/tools/variants/generate-variants.js +6 -6
- package/dist/tools/variants/index.d.ts +1 -1
- package/dist/tools/variants/index.d.ts.map +1 -1
- package/dist/tools/view-scene.d.ts +8 -2
- package/dist/tools/view-scene.d.ts.map +1 -1
- package/dist/tools/view-scene.js +17 -5
- package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
- package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
- package/dist/tools/vision/analyze-floorplan-image.js +7 -7
- package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
- package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
- package/dist/tools/vision/analyze-room-photo.js +7 -7
- package/dist/tools/vision/index.d.ts +1 -1
- package/dist/tools/vision/index.d.ts.map +1 -1
- package/dist/transports/http.d.ts +8 -2
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +9 -3
- package/dist/transports/stdio.d.ts +15 -9
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +18 -11
- package/examples/coordinate-conventions-demo.md +12 -13
- package/examples/embed-in-agent.ts +5 -6
- package/examples/generate-apartment.md +4 -4
- package/examples/photo-to-scene.md +2 -3
- package/examples/renovate-from-photos.md +3 -5
- package/package.json +13 -9
- package/dist/tools/describe-node.d.ts +0 -17
- package/dist/tools/describe-node.d.ts.map +0 -1
- package/dist/tools/describe-node.js +0 -99
- package/dist/tools/find-nodes.d.ts +0 -16
- package/dist/tools/find-nodes.d.ts.map +0 -1
- package/dist/tools/find-nodes.js +0 -204
- package/dist/tools/place-design.d.ts +0 -34
- package/dist/tools/place-design.d.ts.map +0 -1
- package/dist/tools/place-design.js +0 -91
- package/dist/tools/validate-design.d.ts +0 -136
- package/dist/tools/validate-design.d.ts.map +0 -1
- package/dist/tools/validate-design.js +0 -28
- package/dist/tools/validate-scene.d.ts +0 -19
- package/dist/tools/validate-scene.d.ts.map +0 -1
- package/dist/tools/validate-scene.js +0 -28
package/README.md
CHANGED
|
@@ -101,8 +101,8 @@ editor reads and writes the same database-side store.
|
|
|
101
101
|
Local MCP compilation is opt-in with `PASCAL_SERVER_SCRIPT_COMPILE=1`. It runs in a separate
|
|
102
102
|
process without inherited environment variables, with a deadline; this is not a sandbox for
|
|
103
103
|
untrusted code. Blender (`bpy`) modules run in the Blender binary `PASCAL_BLENDER` names
|
|
104
|
-
(`blender -b`), with the same caveat
|
|
105
|
-
compilation.
|
|
104
|
+
(`blender -b`), with the same caveat; [Blender geometry scripts](../../wiki/blender-geometry-scripts.md)
|
|
105
|
+
walks through one. `get_source` can read saved scripts without enabling compilation.
|
|
106
106
|
|
|
107
107
|
## Live editor updates
|
|
108
108
|
|
|
@@ -113,7 +113,6 @@ against a loaded saved scene are persisted to SQLite and recorded in a local
|
|
|
113
113
|
apply scene graph snapshots as the agent edits the scene.
|
|
114
114
|
|
|
115
115
|
The flow is intentionally local and lightweight:
|
|
116
|
-
|
|
117
116
|
1. Open or create a scene in the editor so it is saved in the local database.
|
|
118
117
|
2. Load that scene through MCP with `load_scene`.
|
|
119
118
|
3. Run MCP mutation tools such as `create_room`, `add_door`, `furnish_room`,
|
|
@@ -232,8 +231,7 @@ for agent frameworks and tests.
|
|
|
232
231
|
|
|
233
232
|
```ts
|
|
234
233
|
import { createPascalMcpServer, SceneBridge } from '@pascal-app/mcp'
|
|
235
|
-
import { Client } from '@modelcontextprotocol/
|
|
236
|
-
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
|
|
234
|
+
import { Client, InMemoryTransport } from '@modelcontextprotocol/client'
|
|
237
235
|
|
|
238
236
|
const bridge = new SceneBridge()
|
|
239
237
|
bridge.loadDefault()
|
|
@@ -253,6 +251,39 @@ console.log(scene)
|
|
|
253
251
|
See [`examples/embed-in-agent.ts`](./examples/embed-in-agent.ts) for a
|
|
254
252
|
compilable version.
|
|
255
253
|
|
|
254
|
+
### Plugin tools and the person's floor
|
|
255
|
+
|
|
256
|
+
A plugin's `'operation'` tools register with the same runner as the shared
|
|
257
|
+
tools: it parses the input, applies the outcome as one history step and answers
|
|
258
|
+
with `achieved` and the refusals. The annotations are the host's, never read
|
|
259
|
+
from the plugin's contract.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { createSceneOperations } from '@pascal-app/mcp/operations'
|
|
263
|
+
import { registerOperationTool } from '@pascal-app/mcp/tools/shared-tools'
|
|
264
|
+
import { signageAgentTools } from 'acme-signage/agent-tools'
|
|
265
|
+
import { signageAgentOperations } from 'acme-signage/agent-operations'
|
|
266
|
+
|
|
267
|
+
const operations = createSceneOperations({
|
|
268
|
+
bridge,
|
|
269
|
+
// The floor the person is viewing: a tool call that names no level reads it.
|
|
270
|
+
// Leave it out on a host with no view, and the lowest storey is read.
|
|
271
|
+
activeLevelId: () => myHost.viewedLevelId(),
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
for (const contract of signageAgentTools.tools) {
|
|
275
|
+
if (contract.runsIn !== 'operation') continue
|
|
276
|
+
registerOperationTool(server, operations, {
|
|
277
|
+
contract,
|
|
278
|
+
operation: signageAgentOperations[contract.name],
|
|
279
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The contract shapes, versions and trust rules are in
|
|
285
|
+
[plugin authoring](../../wiki/architecture/plugin-authoring.md#agent-tools-and-chat-extensions).
|
|
286
|
+
|
|
256
287
|
## Coordinate conventions
|
|
257
288
|
|
|
258
289
|
Pascal is a **right-handed** scene where **X and Z form the ground plane and Y
|
|
@@ -282,7 +313,7 @@ panel rotates its content by the user's view rotation (north-aligned = 0°,
|
|
|
282
313
|
the 3-D "top-down" snap preserves the camera's current azimuth, so when invoked
|
|
283
314
|
from the iso default position, world and screen axes are offset by ~45° until
|
|
284
315
|
you orbit to an axis-aligned view. So a layout authored as if
|
|
285
|
-
|
|
316
|
+
_"Y = north, viewed top-down"_ — common in land surveys, north-up site plans,
|
|
286
317
|
and 2-D plotting libraries — will arrive **rotated** relative to its source
|
|
287
318
|
when viewed in Pascal (and possibly further reflected, depending on which
|
|
288
319
|
viewport and camera state you're in). The editor's own 2-D and 3-D tools are
|
|
@@ -328,72 +359,145 @@ wall-attached rotations are wall-local too.
|
|
|
328
359
|
All tools validate their inputs and outputs with Zod. Mutation tools are
|
|
329
360
|
captured by Zundo's temporal middleware as a single undoable step.
|
|
330
361
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
|
358
|
-
|
|
|
359
|
-
| `
|
|
360
|
-
| `
|
|
361
|
-
| `
|
|
362
|
-
| `
|
|
363
|
-
| `
|
|
364
|
-
| `
|
|
365
|
-
| `
|
|
366
|
-
| `
|
|
367
|
-
| `
|
|
368
|
-
| `
|
|
369
|
-
| `
|
|
370
|
-
| `
|
|
371
|
-
| `
|
|
372
|
-
| `
|
|
373
|
-
| `
|
|
362
|
+
Arguments a tool's input schema rejects come back as a tool error whose JSON
|
|
363
|
+
text is `{ error, code: "invalid_input" }`, as every other refusal names its
|
|
364
|
+
code: the arguments are the wrong shape or type, so read `error`, fix them and
|
|
365
|
+
call again. It is the schema's refusal, before the tool ran; the codes a tool
|
|
366
|
+
raises itself (`apply_patch`'s `invalid_update` and `invalid_parent`, a room's
|
|
367
|
+
`cannot_enclose`) come back as `{ error, code, ...details }`, and `apply_patch`
|
|
368
|
+
adds `patchIndex` and `id`. A level an agent writes without a `height` (a
|
|
369
|
+
`create` in `apply_patch`, or an update that clears it) is given the height the
|
|
370
|
+
editor's own load would derive from what the level holds: the top of its
|
|
371
|
+
tallest wall or ceiling, and for an empty level the default storey height, 2.5 m.
|
|
372
|
+
`apply_patch` says so in `notes`, one line per level filled. `add_level` takes
|
|
373
|
+
an optional `height` and uses the same default.
|
|
374
|
+
|
|
375
|
+
Every length and angle a shared tool takes reads one notation: a bare number is
|
|
376
|
+
in the unit the field states (metres, degrees, radians), and text carries its
|
|
377
|
+
own unit, inches, feet and millimetres included. A roof pitch is degrees or rise
|
|
378
|
+
over run:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{ "wallHeight": "9 ft", "overhang": "18 in", "pitch": "6:12" }
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`"26.6"`, `"26.6°"`, `"0.46 rad"`, `"6/12"`, `"6 in 12"` and `"6 on 12"` are
|
|
385
|
+
read too. Minutes and seconds are refused (`'6"'` is a builder's inches), and so
|
|
386
|
+
is a "pitch" fraction like `"1/4 pitch"`, which is rise over span.
|
|
387
|
+
|
|
388
|
+
| Name | Purpose | Key input | Output |
|
|
389
|
+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
390
|
+
| `get_scene` | Return the full scene graph. | — | `{ nodes, rootNodeIds, collections }` |
|
|
391
|
+
| `get_node` | Fetch a node by id, whole, with what the scene decides for it beside its stored fields (a wall's or ceiling's `resolvedHeight` and `heightIsExplicit`, a wall's `resolvedThickness`, a door's or window's `resolvedOpening`, a level's or floor plate's `displayName`), its ancestors parent first, and the scene materials its finishes use; a node its schema rejects comes back as stored, nothing derived. | `{ id }` | `{ node, ancestorIds, missingChildIds?, materials? }`, or refusal `node_not_found` |
|
|
392
|
+
| `find_by_type` | Find everything of one type: nodes of a kind (any core or plugin kind), items of a category, lights, and typed parts inside authored objects, windows and doors; narrowed by level, parent, zone (on its level, by plan point, holes left out) or import source id (`provenance.refs[].id` or legacy `metadata.sourceIds`, exact or prefix); without a type, every node the filters match. Each result is the node's id, name, kind, level and parent, with its metadata and provenance (one over 1 KB named in `omitted`). | `{ type?, levelId?, parentId?, zoneId?, sourceId?, sourceIdPrefix?, limit? }` | `{ type?, count, nodes, parts, results[], truncated?, hint? }`, at most `limit` (200) results listed; refusals `unknown_type`, `level_not_found`, `node_not_found`, `zone_not_found`, `not_a_zone` |
|
|
393
|
+
| `list_levels` | List every building's levels in floor order with their role (storey, roof-only, support), their standing against the ground line (`gradeStanding`, `isGroundLevel`) and, per building, the level at grade. | — | `{ activeSceneId, activeLevelId, levelCount, occupiedStoryCount, supportLevelCount, roofLevelIds, groundLevels[], levels[] }` |
|
|
394
|
+
| `get_level_summary` | Compact summary of one level: role, counts, walls with openings, zones with areas, slabs, ceilings, items, stairs, roofs, and everything else by type. Omit `levelId` for the lowest storey. | `{ levelId?, level? }` | `{ levelId, role, counts, walls, zones, slabs, ceilings, items, stairs, roofs, other }` |
|
|
395
|
+
| `get_walls` | Walls on a level with length, stored and resolved height, and child doors/windows. | `{ levelId?, level? }` | `{ levelId, walls[] }` |
|
|
396
|
+
| `get_zones` | Room/zone polygons with holes, areas (holes taken out), bounds and floor choices. | `{ levelId?, level? }` | `{ levelId, zones[] }` |
|
|
397
|
+
| `get_site` | The site a building stands on, in its level coordinates (a lot with no building yet, in site coordinates): the lot and its area with the footprint a house usually covers (30 to 45%), the buildable area inside the setbacks with its centre, area, each edge's frontage and depth and its front edge, the street side with the lot's frontage and depth, north, the ground (sloped, read flat or not read), address, parcel and zoning, whether the lot is the new-project placeholder, and the footprint's fit and yards. | `{ building?, site? }` | `{ siteId, buildingId, pose, lot, placeholder, buildable, street, ground, northDeg, address, parcel, zone, footprint, others, summary }`; refusals `no_site`, `building_required`, `site_not_found`, `not_a_site`, `building_not_on_site` |
|
|
398
|
+
| `place_building` | Stand a building on its site by moving the building node only (walls keep their level coordinates): its footprint, the outline about to be drawn or the walls standing, centred in the buildable area or at the yards given; a house standing keeps its rotation unless `rotationDeg` is given, one with no walls yet is squared to the street. | `{ building?, outline?, yards?: { front?, rear?, left?, right? }, rotationDeg? }` | `{ buildingId, position, rotationDeg, moved, footprint, yards, fit, overhang, movedWithIt?, message }`; refusals `no_site`, `needs_footprint`, `other_building_on_site`, `no_street`, `yards_conflict`, `yards_infeasible`, `building_required` |
|
|
399
|
+
| `measure` | Distance between two nodes' world-space reference points (hosted doors, windows and items resolved through their host; every node kind); area when applicable. | `{ fromId, toId }` | `{ distanceMeters, fromPoint?, toPoint?, areaSqMeters?, units: 'meters' }` |
|
|
400
|
+
| `view_scene` | Look at the finished building, every storey and the roof, from a viewpoint the agent picks (a compass side, a side as seen from the house's street, a street-height eye, a photo's camera, square-on, from above), in the scene's own light or a photo's, and get the picture back, with the photo's crop beside it when a region is given. `interior: { zoneId }` stands inside a room at eye height (1.6 m by default), a step in from a corner and looking across it; `interior: { levelId }` looks inside one floor: the other floors and its roofs and ceilings left out, everything on it (walls, openings, furniture) cut at 0.65 of the storey's height, for the picture only. Pictures are 1920 × 1200 unless `maxPx` (64 to 1920) asks for a smaller longest side. The server has no renderer: the host's `sceneViews` renders it (the hosted app, through the user's open editor tab). Read-only. | `{ target?, interior?, from?, elevation?, eyeHeight?, position?, fov?, projection?, camera?, lighting?, maxPx?, photo? }` | image content plus `{ status, camera, size, tab, capturedAt }`; refusals `view_unavailable`, `no_project`, `which_building` (a street side, no target, buildings on several sites), `no_current_camera` (`camera: 'current'`: the server has no view of the person's), `camera_and_viewpoint` (a camera with a viewpoint of the agent's own), `photo_crop_unavailable`, `interior_level_invalid`, `interior_hidden`, `interior_transform_unsupported`, `interior_target_elsewhere`; on the hosted server `editor_tab_required`, `editor_tab_hidden`, `editor_tab_busy` (a tab open but busy or still loading, no link: wait a few seconds and call again, do not ask the user), `editor_tab_timeout` (a tab claimed the view and then stopped answering); what the refusal says about opening the editor follows the person's "Agents may open the editor for me" setting (ask first, allow, never) |
|
|
401
|
+
| `search_assets` | Search the host's item library (the built-in list on a standalone server), several queries in one call; a query matches name, id, category or tags. A query that finds nothing gets a hint to build the item with `add_object`. | `{ queries: [{ query, category? }] }` | `{ groups: [{ query, total, results[] }], total, hint? }`; refusal `no_catalog` |
|
|
402
|
+
| `design_brief` | The request read as a brief, once for the whole request: `axes` (the request read axis by axis: `action`, `size`, `pieces`, `openings`, `style`, `materials`, `dressing`, each bounded — the person's own words as `quote`, the reading in a short line, `pieces` as a `minimum` or `exact` — or open with a design statement of at least three words; an absent axis is refused `axis_missing`, a quote not in the person's message in the chat `axis_quote_not_in_request`, an open axis without its design `axis_open_without_design`; over MCP an axis quote is required and not compared, there being no message), the eighth axis `source` (`open`, `catalog`, `brand` with its `brand`, `existing`), a `vision` for what is open, the dressing of the rooms layer by layer (`beyond`: textiles, light, windows, walls, floor, plants, objects, small furniture, each pieces or `{ none: why }`; an open `dressing` axis owes 6 of the 8, `beyond_too_thin`, a bounded one owes none, and the named pieces read as a minimum do not bound it, `dressing_bounded_by_minimum`; handed back as `dressing`), the person's words that set a source other than `open` (`sourceQuote`, required: at least two words naming the brand, the catalog or what is already there; not compared with a request here) and, with an open source, technical equipment placed as it is (`asIs`: sanitaryware, appliances, fixed services; each piece shown by `view_assets` first, each with a reason; furniture is made). Placing or making furnishings follows a brief: `place_items`, `furnish_room`, `place_in_room` (the catalog's) and `add_object`, `place_design` (made), inside `run_batch` too, are refused before one with `design_brief_required`.`search_assets` and `view_assets` stay open. Source `open` opens the made pieces, places technical equipment only when named in `asIs` and keeps `furnish_room` closed (`brief_source_open`); `catalog`, `brand` and `existing` open the catalog's tools, and creation for what it lacks. A batch holding a refused call is refused whole. The brief is the session's: a scene load or clear starts another, and the next `design_brief` replaces it. Changes nothing in the scene. |
|
|
403
|
+
| `view_assets` | Pictures of 1 to 6 catalog items by id, each labelled with its size, category and the names of its paintable surfaces (its model's `slot_<id>` materials; `paint` takes one as `role`), returned as image content. The picture and surfaces are read before the call (a hosted server fetches and resizes the thumbnail to at most 512 px; a standalone server's built-in list has no host to fetch from, so it answers sizes and says it has no picture). Opt-in: nothing changes when it is not called. | `{ ids: string[] (1 to 6) }` | `{ items: [{ id, name, category, dimensions, surfaces, picture, unavailable? }], shown, note? }` plus one labelled image per item shown; refusals `unknown_asset`, `no_catalog` |
|
|
404
|
+
| `search_materials` | Search or browse the material library, several queries in one call: words match as the library writes them (timber is wood, render is stucco), shades and surfaces; a query without words lists a kind, a surface or the whole library by name. A search lists 12 at a time and a browse 150, a long one saying how many more (`more`) and the offset that lists them (`next`). Reads Pascal's library and the materials the host registered (the editor registers its library; this server registers none). Read-only. | `{ queries: [{ query?, kind?, surface?, offset? }] }` | `{ groups: [{ query?, kind?, surface?, offset?, results: [{ ref, label, kind, surfaces, color? }], missing?, more?, next? }] }` |
|
|
405
|
+
| `paint` | Give walls, slabs, ceilings, roofs and every other part with surfaces (columns, fences, doors, windows, stairs, blocks, cabinets, shelves…) a finish in one call, as the editor's Paint does: a colour (the nearest library colour), a `library:` or `scene:` ref, a native material kept as a scene material (reused when an equal one exists), or erase. `role` picks the surface (a wall's `exterior`/`interior` faces, by its rooms or else its stored sides, refused when rooms stand on only part of a face, a slot by name, a roof's `top`/`edge`/`wall`, or its `fascia`, the fascia and rake boards, held by every segment; a glazed roof's `glazing-frame`, `glazing-glass` or `glazing-solid`, and without a role its frame and solid panels, the glass kept); a roof shows one finish per surface, so a segment's finish goes on its roof and a roof's clears its segments' own; a wall face is painted whole, over its room's wall finish, its painted parts keeping theirs (named in `note`). | `{ targets[], role?, color?, material?, materialName?, erase? }` (one finish) | `{ ok, painted[], finish, materialId?, material?, createdMaterial?, note?, achieved }`; refusals `target_not_found`, `unknown_role`, `side_unknown`, `unknown_material`, `materials_unavailable`, `not_paintable` |
|
|
406
|
+
| `create_story_shell` | Create one level-owned story shell from a footprint: the perimeter walls. The floor plate and ceiling are derived from the enclosed rooms; `createSlab` / `createCeiling` / `slabElevation` are recorded as room intent. Use once per story. | `{ levelId, footprint, wallHeight?, wallThickness?, createSlab?, createCeiling? }` | `{ wallIds, zoneIds, slabId, ceilingId, createdIds }` |
|
|
407
|
+
| `create_stair` | Create a straight, L or U stair as the editor's stair tool does: from a level to the next one above (made when there is none), owning the floor openings it cuts, or to no level (`toLevelId: null`).`(x, z)` is the middle of the bottom riser, `rotation` in degrees (0 climbs toward +Z). | `{ x, z, levelId?, toLevelId?, rotation?, layout?, turn?, lowerSteps?, width?, length?, height?, steps?, railingMode?, railingStyle?, railingMaterial? }` | `{ stairId, segmentIds, upperLevelId, createdUpperLevel, layout, stepCount, rise, length, footprint, arrival, slabHoleCut, openingIds? }`; refusals `roof_level`, `not_above`, `level_not_found`, `slab_not_found` / `ceiling_not_found`, `slab_not_on_level` / `ceiling_not_on_level`, `stair_layout`, `stair_layout_required`, `no_level_above` |
|
|
408
|
+
| `measure_stair` | Read actual risers, going at the walking line, slope, uniformity, arrival, headroom against floors/ceilings/stair bodies, and design-target diagnostics. | `{ stairId, available?: { width, length } }` | `{ measurements, layouts }` |
|
|
409
|
+
| `fit_stair` | Fit uniform risers or replace the chain with a straight/L/U preset with width-sized landings or quarter-turn winders in one undoable edit. | `{ stairId, fitRun?, targets?, layout?, turn?, width?, landingDepth?, turningStrategy?, innerGap?, walkingLineOffset?, division? }` | `{ stairId, measurements }` |
|
|
410
|
+
| `create_roof` | Cap the top storey with a roof over its footprint (its walls' outline, else its floor plates, else its rooms), one segment per rectangle, or over a rectangle of your own (`width`, `depth`, `center`). The roof goes on the building's roof level above the storey (reused, the empty level directly above when it is the only one, else made and named Roof) and stands on the storey's wall tops; the hand roof tool follows the same rule. `roofType: "barrel"` arches each segment (`depth` is the span, `barrelRise` at most half of it, `barrelSegments` facets from 4 to 32); `roofSystem: "glazed"` builds a gable, straight shed or barrel roof as panes and framing, set by `glazing` (see [glazed roofs](../../wiki/glazed-roofs.md)). | `{ levelId? (or level), roofType?, roofSystem? ('standard' \| 'glazed'), glazing?, barrelRise?, barrelSegments?, pitch? (degrees, or rise over run: "6:12"), overhang? (the eaves' reach past the walls in plan), wallHeight?, wallThickness?, width?, depth?, center?, materialPreset?, fasciaHeight?, fasciaMaterialPreset?, name? }` | `{ roofId, roofLevelId, createdRoofLevelId, coveredLevelId, segmentIds, roofType, achieved }`; refusals `storey_above`, `roof_exists`, `building_required` (several buildings, no level named), `storey_without_building`, `conflicting_level`, `no_levels`, `no_rooms`, `rectangle_incomplete`, `unknown_material` (the roof's or the fascia's finish) |
|
|
411
|
+
| `move_roofs_to_roof_level` | Move the roofs a storey holds on its own walls to the level the roof rule gives them (the building's roof level over the top storey, made when there is none; the storey above a lower one), as a roof's panel does by hand. Each roof stands where it stood, segments, finishes and id kept. For projects built before the roof level, when `verify_scene` reports `roof_mixed_in_storey`. | `{ levelId }` | `{ roofIds, roofLevelId, createdRoofLevelId, achieved }`; refusals `level_not_found`, `no_roof_on_walls` |
|
|
412
|
+
| `create_room` | Create a room from a polygon: one wall per edge (an edge a wall already runs along reuses it) and the room zone; its doors and windows can be declared in the same call by edge (`wallIndex`) and `t`, each placed with `add_door`'s and `add_window`'s rules or listed in `skippedOpenings`. The floor plate and ceiling are derived, never authored. `outdoor: true` makes a terrace: separators (the editor's Separator: a room boundary with no wall) where no wall runs, no walls of its own, no ceiling. Where every edge of the polygon already runs along a wall (nothing to build) and the polygon fits the room those walls make, the call names that room instead of stacking a second zone on it, and `zoneId` is that room's. If that room has a name of its own (not a number the editor gave it: any name but the editor's "Room N", whoever gave it) the call is refused (`room_named_by_person`, worded with the room's name): pass `rename: true` **only when the person asked for the rename; otherwise ask them**. A room is never renamed unasked, and the same name over it says there is nothing to create. A terrace (`outdoor: true`) drawn over a fully walled courtyard names it outdoor. The refusal is agent-only: the editor's own room tool behaves as before (a person sees and names rooms directly). | `{ name, polygon, levelId?, color?, wallHeight?, wallThickness?, outdoor?, rename?, doors?, windows? }` | `{ zoneId, slabId, ceilingId, wallIds, reusedWalls, areaSqMeters, doorIds, windowIds, skippedOpenings? }`; refusals `cannot_enclose`, `outdoor_room_overlap`, `room_named_by_person` |
|
|
413
|
+
| `add_door` | Add a door to a straight wall at `t` (0..1 along it) with the editor's placement rules, or a passage with no leaf (`openingKind: "opening"`); it slides to stay on the wall and reports `clamped`.`doorType`, `style` and the outline (`openingShape`) match a reference, `code` builds what they cannot, and `nodeId` rebuilds a scripted door. | `{ wallId, t?, width?, height?, openingKind?, hingesSide?, swingDirection?, doorType?, style?, openingShape?, code?, nodeId?, force? }` | `{ doorId, wallId, localX, t, clamped, achieved }`; refusals `wall_not_found`, `not_a_wall`, `curved_wall`, `wall_too_short`, `opening_overlap` (unless `force`) |
|
|
414
|
+
| `add_window` | Add a window as `add_door` places a door, centred vertically on the wall unless `sillHeight` (its bottom edge above the floor) is given; `windowType`, `columns` / `rows`, `style` (a Fixed window's panes; refused on another type, `style_needs_fixed_window`) and the outline match a reference. | `{ wallId, t?, width?, height?, sillHeight?, windowType?, columns?, rows?, style?, openingShape?, code?, nodeId?, force? }` | `{ windowId, wallId, localX, t, clamped, sillHeight, achieved }`; the same refusals as `add_door` |
|
|
415
|
+
| `furnish_room` | Furnish a room for its type from the host's item catalog: the main piece against the wall facing the door, the rest along a side wall. Nothing lands in a door's clear zone or on another item: a piece that does not fit is nudged, else skipped, and `skipped` says why. | `{ zoneId?, polygon?, levelId?, roomType, doorWallIndex? }` | `{ placed, itemIds, skipped, doorWallIndex, doorsDetected }`; refusals `room_required`, `roof_level`, `level_not_found`, `no_catalog` |
|
|
416
|
+
| `add_corner_window` | A window wrapping the corner where two walls meet: one window per wall, both running to the corner and joined, the glass fused (a fixed window) or at a thin post (the other window types, whose frames stay), at any angle from 10° to 170° (a square corner, a bay's 135°, an acute one). `corner` is the point the walls of `levelId` end at (default the floor in view: stacked storeys share their corners' points), `[x, z]` in the level's plan as `get_walls` gives their ends; `width` is how far the glass runs along each wall from the corner (default 1.2 m) and `widths` overrides it per wall id; `post` is `'none'` (the glass fused, a fixed window's default) or `'post'` (the other types' default and only choice); `height` defaults to 1.5 m and the window is centred on the wall unless `sillHeight` (its bottom edge above the floor) is given; `wallIds` (two) is needed only when more than two walls end at the corner; `windowType` and `style` are `add_window`'s (a style needs a Fixed window). By hand: select a window that ends at a corner and use the window panel's Corner section (Wrap the corner, This side and Other side, Fused glass or Corner post for a fixed window, Unwrap). | `{ corner: [x, z], levelId? (or level), wallIds?, width?, widths?, height?, sillHeight?, post?, windowType?, style? }` | `{ windowIds, wallIds, angle, post, achieved }`; refusals `no_corner` (no two walls end there), `corner_ambiguous` (more than two do: pass `wallIds`), `corner_angle` (under 10° or over 170°: use `add_window`), `corner_not_fused` (`post: 'none'` on a type other than fixed), `width_exceeds_wall`, `style_needs_fixed_window` |
|
|
417
|
+
| `merge_windows` | Make two windows that exist one window, as the editor's Merge windows button does; nothing is recreated. On two walls that meet at a corner, each window within 0.30 m of it, they become one corner window (the `add_corner_window` result): each is slid to the corner keeping its width, the glass fused (a fixed window's; other types meet at a post), and both take the first window's height, sill and type, so name first the window whose look you want; the corner is refused over 170° or under 10°. On one wall, two windows with at most 0.30 m between them and nothing between become one window spanning both (overlapping or one inside the other, their union), from the lower sill to the higher head, with the first window's type and style. Use it for two windows already built that open a corner or one wide opening (`verify_scene`'s `corner_unjoined` names a pair for the first); `add_corner_window` builds a new pair. By hand: select two windows and use Merge windows in the multi-selection panel, or place or drag a window near a corner where one waits and click or release (Alt places it plain). | `{ windowIds: [a, b] }` | `{ kind: 'corner' \| 'same_wall', windowIds, removedIds, achieved }`; refusals `window_not_found`, `not_a_window`, `same_window`, `window_not_on_wall`, `window_already_joined` (unwrap it first), `windows_already_joined`, `windows_not_adjacent`, `opening_between`, `walls_not_meeting`, `corner_angle`, `window_not_at_corner`, `height_exceeds_wall`, `opening_overlap` |
|
|
418
|
+
| `apply_patch` | Batched create/update/delete/move, validated and dry-run before commit. Batch-first is the default: send all create/update/delete ops for a build step in one atomic call (stable order, later ops may reference earlier created ids); do not loop one-op calls. A create with an id already in the scene is refused (`node_exists`); delete it earlier in the same patch to replace it. Updates cannot change `id` or `type` (`identity_change`), `object` or `children` (`immutable_field`), move a node under a missing or childless parent (`invalid_parent`), or add schema issues (`invalid_update`); default gutters and downspouts a delete regenerates are addressable only in a later call (`regenerated_default`). Refusals are tool errors with JSON text `{ code, patchIndex, id, message }`. A level created without a `height`, or an update that clears it, gets the height the editor's load derives from what the level holds (an empty level: 2.5 m), one line per level in `notes`. A roof the patch makes or moves onto the storey whose walls carry it is seated on the level above those walls, one line per roof in `notes`; a new storey at or above the roof level moves it up. Refused: storey content on a roof level (`roof_level`), declaring a level that holds storey content the roof level (`roof_level_holds_storey`), floor indexes that put the roof level under a storey (`roof_level_on_top`). | `{ patches: Patch[] }` | `{ applied: number, notes?: string[] }` |
|
|
419
|
+
| `add_level` | Add an empty level to a building, as the editor does: on top of its storeys, under its roof level (which moves up one), or below the lowest for a basement (every existing floor, the ground floor included, keeps its place). Omit `buildingId` for the scene's only building. | `{ buildingId?, position?, name?, height? }` | `{ levelId, buildingId, floorIndex, height, shiftedLevelIds? }`; refusals `building_required`, `building_not_found`, `no_building` |
|
|
420
|
+
| `set_ground_level` | Make a level the building's ground floor, as the level panel's "Set as ground floor": the building names it, is seated so that floor stands over grade as the building's foundation construction seats it, the top of the foundation (8 in, 0.203 m, on block: the default and the US; 0.15 m on cast concrete in Europe; height only, the plan stays), and the floor indexes are renumbered so it is 0 and the levels under it are the basements. | `{ levelId }` | `{ levelId, buildingId, floorElevation, aboveGrade, foundation { region, construction, basis }, movedBuildingBy, renumbered, unchanged? }`; refusals `level_not_found`, `not_a_level`, `no_building`, `not_a_storey` |
|
|
421
|
+
| `add_wall` | Add a wall to a level, straight or, with `curveOffset`, an arc. Omit `levelId` for the lowest storey. | `{ start, end, levelId?, thickness?, height?, curveOffset? }` | `{ wallId, levelId, length }`; refusals `roof_level`, `wall_too_short`, `level_not_found` |
|
|
422
|
+
| `add_fence` | Add a fence along points on a level, one fence per side; `closed` runs it back to the first point (a lot's boundary). Privacy panels unless `style` says otherwise. | `{ points, closed?, height?, style?, baseStyle?, materialPreset?, levelId? }` | `{ count, fenceIds, levelId, length, achieved }`; refusals `fence_too_short`, `roof_level`, `unknown_material`, `level_not_found` |
|
|
423
|
+
| `place_items` | Place catalog items in one call, each placed or refused on its own, at level `(x, z)`: on the level's floor, or on the host each names (`targetNodeId`): a wall (`y`, the height of its bottom, required), a ceiling, or an item standing on the floor. A floor item in a door's clearance is refused `blocks_door` (naming the door and a spot that clears every door), one its room cannot hold in any turn `too_large_for_room`. | `{ items: [{ assetId, x, z, y?, targetNodeId?, rotation? }], levelId? }` | `{ levelId, items: [{ ok, itemId?, hostId?, restingOn?, code? }] }`; per-item refusals `asset_not_found`, `outside_rooms`, `blocks_door`, `too_large_for_room`, `height_required`, `item_too_tall`, `unsupported_host`, `host_not_found`, `host_not_on_level` |
|
|
424
|
+
| `place_design` | Create one design (procedural item recipe, object or JSON string) that passes `validate_design`. Its mounting picks the host: level/slab/zone or a design surface (`surfaceId`), a straight wall face, or a ceiling. Create-only, one undo step, inline designs up to 24 KiB, with coded refusals. | `{ design, hostId, position, rotation?, side?, surfaceId?, parameters?, slots?, name?, id? }` | `{ designId, parentId, surfaceId }` |
|
|
425
|
+
| `set_zone` | Create a zone/room polygon on a level; over walls that already enclose a room, it is that room, never a second zone: one still named "Room N", or already named `label`, takes the label; one a person named otherwise is refused. | `{ levelId, polygon, label, properties? }` | `{ zoneId }` |
|
|
426
|
+
| `duplicate_level` | Copy a level as the editor does (units whose rooms are all on it included; plan references, scans and spawns left behind), above or below, shifting the floors past it. | `{ levelId, position?, name?, preset? }` | `{ newLevelId, name, floorIndex, shiftedLevelIds, copied, skipped, newNodeIds[] }` |
|
|
427
|
+
| `delete_node` | Delete a node with everything under it, as the editor's Delete does. | `{ id }` | `{ deletedIds: [] }` |
|
|
428
|
+
| `clear_scene` | Empty the project on purpose, back to the default site, building and level. The only way to empty a project: a write that would leave it empty is refused (`scene_wipe_blocked`) by a store that guards against accidental wipes, and the session keeps the project as stored. | `{ reason }` | `{ cleared: { removed }, version, graphHash }` |
|
|
429
|
+
| `undo` | Step back through temporal history. | `{ steps? }` | `{ undone: number }` |
|
|
430
|
+
| `redo` | Step forward through temporal history. | `{ steps? }` | `{ redone: number }` |
|
|
431
|
+
| `export_json` | Serialize the scene graph as JSON. | `{ pretty? }` | `{ json: string }` |
|
|
432
|
+
| `export_glb` | Stubbed: GLB export requires the browser renderer. | — | throws `not_implemented` |
|
|
433
|
+
| `validate_design` | Read-only: validate a design (procedural item recipe, object or JSON string) and measure it. The authority over `pascal://schema/design`. | `{ design, parameters? }` | `{ valid, diagnostics[], design, sweep, measurements }` |
|
|
434
|
+
| `verify_scene` | The one scene check: schema validity, per-level counts, roles and the spaces the walls enclose, empty levels, and typed practical issues (every node field its schema rejects as `schema_invalid` with its `nodeId` and `path`, a node kind no schema knows once per kind, a space with no room or with several, a room the walls do not close or with no name, storeys with no stair, openings off their wall, stairs off their slab, blocked doors, a house past its lot line or in its required yards…). | — | `{ valid, levels[], issues: { type, message, … }[], hasIssues }` |
|
|
435
|
+
| `check_collisions` | Find overlapping items and out-of-bounds placements. | `{ levelId? }` | `{ collisions: { aId, bId, kind }[] }` |
|
|
436
|
+
| `analyze_floorplan_image` | Vision tool: extract walls, rooms, and approximate dimensions from a floorplan image. | `{ image, scaleHint? }` | `{ walls, rooms, approximateDimensions, confidence }` |
|
|
437
|
+
| `analyze_room_photo` | Vision tool: extract approximate dimensions and fixtures from a room photo. | `{ image }` | `{ approximateDimensions, identifiedFixtures, identifiedWindows }` |
|
|
374
438
|
|
|
375
439
|
The vision tools require the MCP host to support the sampling capability
|
|
376
440
|
(`createMessage`). Hosts that don't will see a structured
|
|
377
441
|
`sampling_unavailable` error.
|
|
378
442
|
|
|
443
|
+
A retired tool is listed no more and still answered: a call to its name
|
|
444
|
+
returns an error with the code `tool_retired`, the tool that replaced it
|
|
445
|
+
(`replacement`) and how to use that one. `validate_scene` retired into
|
|
446
|
+
`verify_scene`, `find_nodes` into `find_by_type` and `describe_node` into
|
|
447
|
+
`get_node` (`RETIRED_MCP_TOOLS` in `@pascal-app/core/agent-tools`).
|
|
448
|
+
|
|
449
|
+
```jsonc
|
|
450
|
+
// find_nodes { zoneId } → find_by_type, with or without a type
|
|
451
|
+
{ "name": "find_by_type", "arguments": { "type": "window", "zoneId": "zone_kitchen" } }
|
|
452
|
+
{ "name": "find_by_type", "arguments": { "sourceIdPrefix": "ground/exterior-01" } }
|
|
453
|
+
// describe_node → get_node: resolvedHeight, resolvedThickness, ancestorIds, displayName…
|
|
454
|
+
{ "name": "get_node", "arguments": { "id": "wall_host" } }
|
|
455
|
+
// validate_scene → verify_scene: schema_invalid issues by nodeId and path, room findings
|
|
456
|
+
{ "name": "verify_scene", "arguments": {} }
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Examples
|
|
460
|
+
|
|
461
|
+
```jsonc
|
|
462
|
+
// The lot before the house: frontage, depth, each buildable edge as a front, the ground
|
|
463
|
+
{ "name": "get_site", "arguments": {} }
|
|
464
|
+
// An L stair with a glass railing, and porch steps to no level
|
|
465
|
+
{ "name": "create_stair", "arguments": { "x": 2, "z": 1, "layout": "l", "turn": "left", "lowerSteps": 6, "railingStyle": "glass" } }
|
|
466
|
+
{ "name": "create_stair", "arguments": { "x": 0, "z": -4, "toLevelId": null, "steps": 3, "height": "0.5 m" } }
|
|
467
|
+
// A glazed barrel roof over a 6 × 4 m rectangle
|
|
468
|
+
{ "name": "create_roof", "arguments": { "roofType": "barrel", "roofSystem": "glazed", "width": 6, "depth": 4, "barrelRise": 1.4, "barrelSegments": 12 } }
|
|
469
|
+
// Gable ends reaching 0.6 m past their walls, apart from the eaves
|
|
470
|
+
{ "name": "apply_patch", "arguments": { "patches": [{ "op": "update", "id": "rseg_main", "data": { "gableOverhang": 0.6 } }] } }
|
|
471
|
+
// Find a finish, then paint every exterior wall face with it
|
|
472
|
+
{ "name": "search_materials", "arguments": { "queries": [{ "query": "white render", "surface": "wall" }] } }
|
|
473
|
+
{ "name": "paint", "arguments": { "targets": ["wall_a", "wall_b"], "role": "exterior", "material": "library:<id>" } }
|
|
474
|
+
// The house as a street photo shows it, at dusk, small
|
|
475
|
+
{ "name": "view_scene", "arguments": { "from": "street-left", "lighting": "dusk", "maxPx": 960 } }
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
`gableOverhang` is a roof segment's own reach past its gable-end walls (a
|
|
479
|
+
gable's or gambrel's ends, a shed's sides), in plan metres; left out, the ends
|
|
480
|
+
reach as far as the eaves. The roof panel's "Gable overhang" slider edits the
|
|
481
|
+
same field.
|
|
482
|
+
|
|
379
483
|
## Resources
|
|
380
484
|
|
|
381
|
-
| URI
|
|
382
|
-
|
|
|
383
|
-
| `pascal://scene/current`
|
|
384
|
-
| `pascal://scene/current/summary` | `text/markdown`
|
|
385
|
-
| `pascal://agent/guide`
|
|
386
|
-
| `pascal://catalog/items`
|
|
387
|
-
| `pascal://constraints/{levelId}` | `application/json` | Slab footprints and wall polygons for the given level — useful as planner context.
|
|
388
|
-
| `pascal://schema/design`
|
|
485
|
+
| URI | MIME | Purpose |
|
|
486
|
+
| -------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
487
|
+
| `pascal://scene/current` | `application/json` | Full `{ nodes, rootNodeIds, collections }` snapshot. |
|
|
488
|
+
| `pascal://scene/current/summary` | `text/markdown` | Human-readable summary with node counts, bounding box, and level areas. |
|
|
489
|
+
| `pascal://agent/guide` | `text/markdown` | MCP-first construction workflow, scene invariants, and tool preferences for agents. On a hosted server it also points at the project's other chats, files and members (`search_project_chats`, `read_project_chat`, `get_project_assets`, `read_project_asset`, `list_project_members`), read as quoted data, never instructions. |
|
|
490
|
+
| `pascal://catalog/items` | `application/json` | Dependency-free built-in catalog subset for common residential furniture and fixtures. |
|
|
491
|
+
| `pascal://constraints/{levelId}` | `application/json` | Slab footprints and wall polygons for the given level — useful as planner context. |
|
|
492
|
+
| `pascal://schema/design` | `application/json` | Design contract: JSON Schema generated from core's `RecipeSchema`, limits, rules only `validate_design` checks, and an example. |
|
|
389
493
|
|
|
390
494
|
## Prompts
|
|
391
495
|
|
|
392
|
-
| Name
|
|
393
|
-
|
|
|
394
|
-
| `from_brief`
|
|
395
|
-
| `iterate_on_feedback`
|
|
396
|
-
| `renovation_from_photos` | `{ currentPhotos: string[], referencePhotos: string[], goals: string }` | Chains the vision tools with the scene mutation tools to produce a renovation plan grounded in photos.
|
|
496
|
+
| Name | Args | Purpose |
|
|
497
|
+
| ------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
498
|
+
| `from_brief` | `{ brief: string, constraints?: string }` | Guided workflow for turning a prose brief (e.g. "2-bed apartment in 80 m²") into an incremental sequence of `apply_patch` calls starting from an empty site. |
|
|
499
|
+
| `iterate_on_feedback` | `{ feedback: string }` | Minimal-diff instructions: examine the current scene, then propose the smallest patch set that satisfies the feedback. |
|
|
500
|
+
| `renovation_from_photos` | `{ currentPhotos: string[], referencePhotos: string[], goals: string }` | Chains the vision tools with the scene mutation tools to produce a renovation plan grounded in photos. |
|
|
397
501
|
|
|
398
502
|
## Limitations
|
|
399
503
|
|