Pascal 3D Editor

app.pascal由 pascal.app 發布官方發布者v1.0.6更新於 Nov 6, 2026

Create, inspect, validate, and save editable 3D building scenes with Pascal's MCP server.

已驗證Streamable HTTP可網頁執行檔案與儲存媒體與設計

概覽

AI 產生的概覽

Pascal 3D 建築編輯器的 MCP 伺服器,讓助理建立、檢視、驗證並儲存可編輯的 3D 建築場景。

可以做什麼
  • 讀取與檢視場景:完整場景圖、節點、樓層、牆體、區域、基地,以及依類型查找
  • 建立與修改幾何:牆體、圍籬、房間、樓層、樓梯、屋頂、轉角窗、樓板開口、基礎
  • 布置與裝飾:搜尋家具與材質庫、放置物件、為房間配置家具、塗刷飾面
  • 驗證與量測:驗證設計、檢查場景、量測與調整樓梯、設定檢查點比對變更
  • 儲存與同步:變更會與本機場景資料庫做版本檢查,並可推送到已開啟的編輯器頁面
適用情境
適合讓助理實際動手建立或修改 3D 建築模型:依需求繪製房間、牆體、屋頂與樓梯,布置家具,或檢查既有場景,而不只是用文字描述。
執行需求
可使用託管端點(以 OAuth 登入 Pascal 帳號),或本機 npm 套件,透過 npx/bunx 以 stdio 或本機回環 HTTP 執行。本機執行需要 Node.js 22.23.4+/24.22.1+/26.12.1+ 或 Bun、@pascal-app/core 同層相依套件,以及可寫入的資料目錄(PASCAL_DATA_DIR 或 PASCAL_DB_PATH)。綁定非回環位址需設定 PASCAL_MCP_HTTP_TOKEN。
安裝前請注意
工具會寫入與刪除場景內容:牆體、樓層、節點與開口都可能被建立、修改或移除,變更會存到本機 SQLite 資料庫。選用的腳本編譯(PASCAL_SERVER_SCRIPT_COMPILE=1)與 Blender 腳本(PASCAL_BLENDER)會執行程式碼,README 說明其並非供不受信任程式碼使用的沙箱。託管端點會編輯 Pascal 帳號中的專案。視覺工具需要主機支援 sampling。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Pascal 3D Editor,將其新增到工作區。
  2. 連接 所需的 憑證。它們會被私密儲存,絕不會在公開頁面上展示。
  3. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/app.pascal%2Feditor/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。 客戶端會在首次連接時完成 OAuth 登入。

{
  "mcpServers": {
    "pascal-3d-editor": {
      "type": "http",
      "url": "https://editor.pascal.app/api/mcp"
    }
  }
}

README

@pascal-app/mcp

Also listed on Smithery.

Model Context Protocol server for the Pascal 3D editor. Drives the @pascal-app/core scene graph from any MCP-compatible AI host.

For the hosted Pascal MCP endpoint and copy-ready setup for Claude Code, Codex, Cursor, and OpenClaw, read Connect an AI agent. The hosted endpoint edits projects in a Pascal account; this package is the open-source, local server for custom hosts and local scene storage.

Listed in the official MCP Registry as app.pascal/editor.

The server runs headlessly in Node.js (22.23.4+ on 22.x, 24.22.1+ on 24.x, or 26.12.1+) or Bun, with no browser, WebGPU, React, or external database service. It exposes the same scene mutations used by the editor UI (create walls, place items, cut openings, undo, etc.) as MCP tools, resources, and prompts.

Recommended local setup

For a local editor and MCP that share projects automatically, install the Pascal CLI:

bash
npx @pascal-app/cli editorpascal mcp setup codex

pascal editor starts the editor and an authenticated MCP service together. pascal mcp connect is a stable stdio connector that discovers the dynamic loopback port, so MCP client configuration contains neither a changing port nor a secret.

Use this package directly when embedding the MCP server, supplying a custom store, or running MCP without the Pascal editor.

Install the package directly

bash
bun add @pascal-app/mcp

@pascal-app/core is a peer dependency. The local store uses Bun SQLite under Bun and Node's built-in SQLite driver under Node.js.

Quick start

Launch the server over stdio in one line:

bash
bunx @pascal-app/mcp# ornpm exec --package=@pascal-app/mcp -- pascal-mcp

Load an initial scene from disk:

bash
bunx @pascal-app/mcp --stdio --scene ./my-scene.json

Expose it over loopback HTTP:

bash
bunx @pascal-app/mcp --http --port 8787

Binding a non-loopback host requires a bearer token:

bash
PASCAL_MCP_HTTP_TOKEN="$(openssl rand -hex 32)" \  bunx @pascal-app/mcp --http --host 0.0.0.0 --port 8787 --cors-origin https://editor.example

Local scene storage

Scenes saved through MCP are stored in a local SQLite database:

text
~/.pascal/data/pascal.db

Set PASCAL_DATA_DIR when you want the MCP server and the running editor to share a different directory, or PASCAL_DB_PATH when you need an exact database file path. The store uses WAL mode and transactional version checks so separate local processes can save and open the same scene database.

During workspace development, run both sides with the same data directory:

bash
# Terminal 1: run the editorPASCAL_DATA_DIR="$HOME/.pascal/data" bun run dev
# Terminal 2 or an MCP host: run the serverPASCAL_DATA_DIR="$HOME/.pascal/data" bun packages/mcp/dist/bin/pascal-mcp.js

Local geometry artifacts

Scripted objects keep their GLB and source in <database-path>.artifacts/<sha256> beside the local SQLite scene database, where saved scenes live (also with --scene, which only seeds the first scene); keep that directory with the database. Files are verified against their hash and written once, so saved scenes and undo history keep their original geometry. The standalone editor reads and writes the same database-side store.

Local MCP compilation is opt-in with PASCAL_SERVER_SCRIPT_COMPILE=1. It runs in a separate process without inherited environment variables, with a deadline; this is not a sandbox for untrusted code. Blender (bpy) modules run in the Blender binary PASCAL_BLENDER names (blender -b), with the same caveat; Blender geometry scripts walks through one. get_source can read saved scripts without enabling compilation.

Live editor updates

When the editor and MCP server share the same PASCAL_DATA_DIR, MCP mutations against a loaded saved scene are persisted to SQLite and recorded in a local scene_events stream. The editor page subscribes to that stream at /api/scenes/:id/events with server-sent events, so an open browser tab can apply scene graph snapshots as the agent edits the scene.

The flow is intentionally local and lightweight:

  1. Open or create a scene in the editor so it is saved in the local database.
  2. Load that scene through MCP with load_scene.
  3. Run MCP mutation tools such as create_room, add_door, furnish_room, add_wall, place_items, or set_zone.

Each mutation version-checks the saved scene before writing. If the browser or another MCP process saved a newer version first, the MCP tool returns live_sync_version_conflict; reload the scene with load_scene before continuing.

Managed CLI client configuration

The recommended JSON configuration for Claude Desktop, Cursor, and compatible clients is:

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

json
{  "mcpServers": {    "pascal": {      "command": "pascal",      "args": ["mcp", "connect"]    }  }}

Claude Code

Via the CLI:

bash
pascal mcp setup claude

Or add to .mcp.json at the repo root:

json
{  "mcpServers": {    "pascal": {      "command": "pascal",      "args": ["mcp", "connect"]    }  }}

For package-development testing without the managed CLI, build first and point Claude Code at the built binary:

json
{  "mcpServers": {    "pascal": {      "command": "node",      "args": ["/absolute/path/to/editor/packages/mcp/dist/bin/pascal-mcp.js"],      "env": {        "PASCAL_DATA_DIR": "/Users/you/.pascal/data"      }    }  }}

Codex CLI

Via the CLI:

bash
pascal mcp setup codex

For local workspace testing before publish:

bash
bun run --cwd packages/mcp buildcodex mcp add pascal-dev \  --env PASCAL_DATA_DIR="$HOME/.pascal/data" \  -- node "$PWD/packages/mcp/dist/bin/pascal-mcp.js"

This writes an entry like this to ~/.codex/config.toml:

toml
[mcp_servers.pascal-dev]command = "node"args = ["/absolute/path/to/editor/packages/mcp/dist/bin/pascal-mcp.js"]
[mcp_servers.pascal-dev.env]PASCAL_DATA_DIR = "/Users/you/.pascal/data"

Cursor config

In Cursor settings (settings.json):

json
{  "mcp.servers": {    "pascal": {      "command": "pascal",      "args": ["mcp", "connect"]    }  }}

Programmatic use

Embed the server in your own Node.js or Bun process using the in-memory transport. The example below runs a full client/server pair inside a single script — useful for agent frameworks and tests.

ts
import { createPascalMcpServer, SceneBridge } from '@pascal-app/mcp'import { Client, InMemoryTransport } from '@modelcontextprotocol/client'
const bridge = new SceneBridge()// Or over a detached scene a host opened (`@pascal-app/core/runtime`): every tool then reads and// writes that scene, with its ids, and the host turns it into a patch.// const bridge = new SceneBridge({ runtime: openDetachedScene(snapshot, options) })bridge.loadDefault()const server = createPascalMcpServer({ bridge })
const [srvT, cliT] = InMemoryTransport.createLinkedPair()const client = new Client({ name: 'my-agent', version: '0.1.0' })await Promise.all([server.connect(srvT), client.connect(cliT)])
const tools = await client.listTools()console.log('available tools:', tools.tools.map((t) => t.name))
const scene = await client.callTool({ name: 'get_scene', arguments: {} })console.log(scene)

A host that passes its own operations (createPascalMcpServer({ operations })) may implement runInScene(run): every tool handler starts inside it, and tools re-enter it after their awaits, so the ids a tool mints and the operations it plans run in the scene's synchronous scope. It is optional; without it handlers run as they always have, with random ids. An implementation that computes on a detached scene must provide it, delegating to SceneBridge.runInScene, or its tools' ids will not be the scene's and a recomputed effect will not match.

See examples/embed-in-agent.ts for a compilable version.

Plugin tools and the person's floor

A plugin's 'operation' tools register with the same runner as the shared tools: it parses the input, applies the outcome as one history step and answers with achieved and the refusals. The annotations are the host's, never read from the plugin's contract.

ts
import { createSceneOperations } from '@pascal-app/mcp/operations'import { registerOperationTool } from '@pascal-app/mcp/tools/shared-tools'import { signageAgentTools } from 'acme-signage/agent-tools'import { signageAgentOperations } from 'acme-signage/agent-operations'
const operations = createSceneOperations({  bridge,  // The floor the person is viewing: a tool call that names no level reads it.  // Leave it out on a host with no view, and the lowest storey is read.  activeLevelId: () => myHost.viewedLevelId(),})
for (const contract of signageAgentTools.tools) {  if (contract.runsIn !== 'operation') continue  registerOperationTool(server, operations, {    contract,    operation: signageAgentOperations[contract.name],    annotations: { readOnlyHint: false, destructiveHint: false },  })}

The contract shapes, versions and trust rules are in plugin authoring.

Coordinate conventions

Pascal is a right-handed scene where X and Z form the ground plane and Y is up. Lengths are in metres; rotations are radians, stored as Euler [x, y, z] tuples.

Plan → world. Every 2-D point you pass is a level/building-local ground-plane coordinate [x, z] — this includes wall.start / wall.end and the polygon / holes arrays of slab, zone, and ceiling. With the default identity building transform, it appears in world space as:

[x, z]  →  (x, y, z)      // the 2nd component is world Z (depth), not "up"

There is no sign flip in the stored convention: tooling consumes the second component as world Z directly. The vertical y starts from the owning level's stacked height as computed by the level system from accumulated level heights, plus the element's own height; slabs additionally carry an absolute elevation.

Heads-up when you compute coordinates outside the editor. Pascal's viewports apply their own rotations on top of the world axes: the 2-D plan panel rotates its content by the user's view rotation (north-aligned = 0°, FLOORPLAN_VIEW_ROTATION_DEG baseline, north = world −Z), and the 3-D "top-down" snap preserves the camera's current azimuth, so when invoked from the iso default position, world and screen axes are offset by ~45° until you orbit to an axis-aligned view. So a layout authored as if "Y = north, viewed top-down" — common in land surveys, north-up site plans, and 2-D plotting libraries — will arrive rotated relative to its source when viewed in Pascal (and possibly further reflected, depending on which viewport and camera state you're in). The editor's own 2-D and 3-D tools are internally consistent with their stored coordinates, so this only affects geometry authored programmatically. To verify orientation before trusting externally-computed coordinates, place a scaled guide image at known anchor points and check alignment; apply whatever rotation (or reflection) your authoring side needs to match.

A worked demonstration of all of this — axis-aligned baseline, the rotated 30° example below, and a paired "page-intent vs world-result" L for the external-coordinate gotcha — lives in examples/coordinate-conventions-demo.md and examples/coordinate-conventions-demo.json. Load the JSON with bunx @pascal-app/mcp --stdio --scene examples/coordinate-conventions-demo.json.

Example — a 6 × 4 m slab rotated 30° about its first corner (coordinates rounded to 3 dp; sides ≈ 6 m / 4 m; not axis-aligned, so the mapping is actually exercised):

json
{  "op": "create",  "parentId": "<levelId>",  "node": {    "type": "slab",    "elevation": 0.0,    "polygon": [[0, 0], [5.196, 3.0], [3.196, 6.464], [-2.0, 3.464]]  }}

This lands flat on the ground (Y = 0), about 6 m along a heading 30° off the +X axis and 4 m along its perpendicular — i.e. occupying world (x, z) directly.

One separate gotcha: wall-attached coordinates are wall-local, not plan coordinates. Stored door/window position[0] is metres along the wall, and wall-attached rotations are wall-local too.

Tools

All tools validate their inputs and outputs with Zod. Mutation tools are captured by Zundo's temporal middleware as a single undoable step.

Arguments a tool's input schema rejects come back as a tool error whose JSON text is { error, code: "invalid_input" }, as every other refusal names its code: the arguments are the wrong shape or type, so read error, fix them and call again. It is the schema's refusal, before the tool ran; the codes a tool raises itself (apply_patch's invalid_update and invalid_parent, a room's cannot_enclose) come back as { error, code, ...details }, and apply_patch adds patchIndex and id. A level an agent writes without a height (a create in apply_patch, or an update that clears it) is given the height the editor's own load would derive from what the level holds: the top of its tallest wall or ceiling, and for an empty level the default storey height, 2.5 m. apply_patch says so in notes, one line per level filled. add_level takes an optional height and uses the same default.

Every length and angle a shared tool takes reads one notation: a bare number is in the unit the field states (metres, degrees, radians), and text carries its own unit, inches, feet and millimetres included. A roof pitch is degrees or rise over run:

json
{ "wallHeight": "9 ft", "overhang": "18 in", "pitch": "6:12" }

"26.6", "26.6°", "0.46 rad", "6/12", "6 in 12" and "6 on 12" are read too. Minutes and seconds are refused ('6"' is a builder's inches), and so is a "pitch" fraction like "1/4 pitch", which is rise over span.

NamePurposeKey inputOutput
get_sceneReturn the full scene graph.—{ nodes, rootNodeIds, collections }
get_nodeFetch 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
find_by_typeFind 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
list_levelsList 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[] }
get_level_summaryCompact 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 }
get_wallsWalls on a level with length, stored and resolved height, and child doors/windows. Each wall reports type { id, label } or null; wallTypes lists available preset and project types. wallType reads standard (when absent), railing or curtain.{ levelId?, level? }{ levelId, walls[] }
get_zonesRoom/zone polygons with holes, areas (holes taken out), bounds and floor choices. Reports roomType (room/porch/deck/patio), floor.drop in metres, porchFraming (auto, postSize, postSpacing in metres, beamSize, postCount, beamCount; null with no intent), and decking (boardSize, gap in metres, direction), matching set_zone_intent.{ levelId?, level? }{ levelId, zones[] }
get_siteThe 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
place_buildingStand 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
measureDistance 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' }
view_sceneLook 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)
search_assetsSearch 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. Each query lists its best matches first, at most limit (24 by default, up to 60); a cut query says how to narrow it, one that finds nothing gets a hint to build the item with add_object. The text result carries each item's id, name and placement facts; the structured result adds its category.{ queries: [{ query, category? }], limit? }{ groups: [{ query, total, truncated, results[] }], total, hint? }; refusal no_catalog
design_briefThe request read as a brief, once: axes (the request read axis by axis: action, size, form, facade, 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, except form and facade, which are optional, 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; an open form states its massing, an open facade its treatment, an open form owes rooms; over MCP an axis quote is required and not compared), the tenth 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, naming the brand, the catalog or what is already there; not compared here) and, with an open source, technical equipment and plants placed from the catalog by its tags (furniture is made). Furnishing 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 too. Source open opens the made pieces, places technical equipment and plants from the catalog 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 checks each call under its own onRefusal policy. 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.
view_assetsPictures 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
search_materialsSearch 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? }] }
paintGive 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
create_story_shellCreate 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 }
create_stairCreate 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
measure_stairRead 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 }
fit_stairFit 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 }
create_roofCap 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).{ 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)
move_roofs_to_roof_levelMove 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
create_roomCreate 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
add_doorAdd 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, nodeId rebuilds a scripted door, and the rare sillHeight lifts it off the floor or, negative, lowers it to a floor sunk beside the wall (down to the wall's bottom).{ wallId, t?, width?, height?, sillHeight?, openingKind?, hingesSide?, swingDirection?, doorType?, style?, openingShape?, code?, nodeId?, force? }{ doorId, wallId, localX, t, clamped, sillHeight?, achieved }; refusals wall_not_found, not_a_wall, curved_wall, wall_too_short, opening_overlap (unless force)
add_windowAdd 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
furnish_roomFurnish 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
add_corner_windowA 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
merge_windowsMake 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
apply_patchBatched 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[] }
add_levelAdd 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
set_ground_levelMake 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 on grade (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, gradeElevation, movedBuildingBy, renumbered, unchanged? }; refusals level_not_found, not_a_level, no_building, not_a_storey
add_wallAdd a wall to a level, straight or, with curveOffset, an arc. Omit levelId for the lowest storey. wallType railing builds a guard (default 0.9144 m high); railing sets style, posts, infill and cap; postSize sets thickness.{ start, end, levelId?, thickness?, height?, curveOffset?, type?, wallType?, railing? }{ wallId, levelId, length }; refusals roof_level, wall_too_short, level_not_found, unknown_wall_type
set_wall_typeApply a preset or project wall assembly to existing standard walls; copies layers and total thickness. null clears the assembly.{ wallIds, type }{ ok, wallIds, type }; refusals unknown_wall_type, target_not_found, curtain_wall, railing_wall
add_columnAdd or rebuild a column. role post uses the Post tile: square pressure-treated wood, postSize 4x4, 6x6 (default), 8x8; postBase none (default), post-base (steel), pier (concrete).{ x?, z?, level?, role?, postSize?, postBase?, height?, nodeId?, ... }{ columnId, nodeId, height, width, depth }; not_a_post for post choices on an ordinary column
add_fenceAdd 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
place_itemsPlace 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
place_designCreate 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 }
set_zoneCreate 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 }
duplicate_levelCopy 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[] }
create_unitCreate a unit (a dwelling, suite or tenancy of a building with several: apartment, hotel-room, commercial, common) under a building, holding zones from any of its levels; a zone already in another unit is refused unless move. A zone in no unit is common area.{ buildingId?, name, kind?, color?, memberZoneIds?, move? }{ unitId, buildingId, name, kind, color, members, memberCount, message }
set_unit_membersReplace the zones a unit holds; a zone left out becomes common area. Every zone must be on a level of the unit's building; one in another unit is refused unless move.{ unitId, memberZoneIds, move? }{ unitId, memberCount, members, message }
list_unitsUnits of a building, or of every building, each with its member zone ids and a report (member count, level span, gross area, each member's name, level and area).{ buildingId? }{ units[] }
delete_nodeDelete a node with everything under it, as the editor's Delete does.{ id }{ deletedIds: [] }
clear_sceneEmpty 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 }
undoStep back through temporal history.{ steps? }{ undone: number }
redoStep forward through temporal history.{ steps? }{ redone: number }
export_jsonSerialize the scene graph as JSON.{ pretty? }{ json: string }
export_glbStubbed: GLB export requires the browser renderer.—throws not_implemented
validate_designRead-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, schemaUri?, example? }
verify_sceneThe 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 }
check_collisionsFind overlapping items and out-of-bounds placements.{ levelId? }{ collisions: { aId, bId, kind }[] }
analyze_floorplan_imageVision tool: extract walls, rooms, and approximate dimensions from a floorplan image.{ image, scaleHint? }{ walls, rooms, approximateDimensions, confidence }
analyze_room_photoVision tool: extract approximate dimensions and fixtures from a room photo.{ image }{ approximateDimensions, identifiedFixtures, identifiedWindows }

The vision tools require the MCP host to support the sampling capability (createMessage). Hosts that don't will see a structured sampling_unavailable error.

A retired tool is listed no more and still answered: a call to its name returns an error with the code tool_retired, the tool that replaced it (replacement) and how to use that one. validate_scene retired into verify_scene, find_nodes into find_by_type and describe_node into get_node (RETIRED_MCP_TOOLS in @pascal-app/core/agent-tools).

jsonc
// find_nodes { zoneId } → find_by_type, with or without a type{ "name": "find_by_type", "arguments": { "type": "window", "zoneId": "zone_kitchen" } }{ "name": "find_by_type", "arguments": { "sourceIdPrefix": "ground/exterior-01" } }// describe_node → get_node: resolvedHeight, resolvedThickness, ancestorIds, displayName…{ "name": "get_node", "arguments": { "id": "wall_host" } }// validate_scene → verify_scene: schema_invalid issues by nodeId and path, room findings{ "name": "verify_scene", "arguments": {} }

Examples

jsonc
// The lot before the house: frontage, depth, each buildable edge as a front, the ground{ "name": "get_site", "arguments": {} }// An L stair with a glass railing, and porch steps to no level{ "name": "create_stair", "arguments": { "x": 2, "z": 1, "layout": "l", "turn": "left", "lowerSteps": 6, "railingStyle": "glass" } }{ "name": "create_stair", "arguments": { "x": 0, "z": -4, "toLevelId": null, "steps": 3, "height": "0.5 m" } }// A glazed barrel roof over a 6 × 4 m rectangle{ "name": "create_roof", "arguments": { "roofType": "barrel", "roofSystem": "glazed", "width": 6, "depth": 4, "barrelRise": 1.4, "barrelSegments": 12 } }// Gable ends reaching 0.6 m past their walls, apart from the eaves{ "name": "apply_patch", "arguments": { "patches": [{ "op": "update", "id": "rseg_main", "data": { "gableOverhang": 0.6 } }] } }// Find a finish, then paint every exterior wall face with it{ "name": "search_materials", "arguments": { "queries": [{ "query": "white render", "surface": "wall" }] } }{ "name": "paint", "arguments": { "targets": ["wall_a", "wall_b"], "role": "exterior", "material": "library:<id>" } }// The house as a street photo shows it, at dusk, small{ "name": "view_scene", "arguments": { "from": "street-left", "lighting": "dusk", "maxPx": 960 } }

gableOverhang is a roof segment's own reach past its gable-end walls (a gable's or gambrel's ends, a shed's sides), in plan metres; left out, the ends reach as far as the eaves. The roof panel's "Gable overhang" slider edits the same field.

Resources

URIMIMEPurpose
pascal://scene/currentapplication/jsonFull { nodes, rootNodeIds, collections } snapshot.
pascal://scene/current/summarytext/markdownHuman-readable summary with node counts, bounding box, and level areas.
pascal://agent/guidetext/markdownMCP-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.
pascal://catalog/itemsapplication/jsonDependency-free built-in catalog subset for common residential furniture and fixtures.
pascal://constraints/{levelId}application/jsonSlab footprints and wall polygons for the given level — useful as planner context.
pascal://schema/designapplication/jsonDesign contract: JSON Schema generated from core's RecipeSchema, limits, rules only validate_design checks, and an example.

Prompts

NameArgsPurpose
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.
iterate_on_feedback{ feedback: string }Minimal-diff instructions: examine the current scene, then propose the smallest patch set that satisfies the feedback.
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.

Limitations

  • export_glb returns not_implemented. GLB export depends on the Three.js renderer and isn't reachable headlessly without a large additional effort.
  • Vision tools require MCP host sampling support. Claude Desktop supports this; some MCP clients don't.
  • The built-in MCP catalog is intentionally small. Host applications can expose their own richer catalog through additional tools/resources without requiring the MCP package to depend on the editor UI bundle.
  • Systems (wall mitering, slab triangulation, CSG cutouts, roof / stair generation) run inside React hooks in the editor. Headless mode doesn't regenerate derived geometry — but all node data remains fully manipulable. Consumers that need rendered geometry run @pascal-app/viewer in a browser host.
  • Core's loadAssetUrl / saveAsset are browser-only; items that reference asset://<id> URLs aren't resolvable in Node. Supply absolute URLs or data: URLs for item assets if you need them usable outside the browser.
  • dirtyNodes accumulates in headless mode because no renderer consumes it. Call bridge.flushDirty() if observability matters to your consumer.

Development

bash
bun installbun run --cwd packages/mcp buildbun test

Smoke-test the stdio binary end-to-end:

bash
bun run --cwd packages/mcp smoke

License

MIT

來源:packages/mcp/README.md,提交 0602f9e

工具

86

get_scene

Get scene

Returns the full scene graph: flat node dictionary, root node IDs, collections, and explicit installed plugin IDs when present.

無需輸入參數。

validate_design

Validate design

Check a design (procedural item recipe) without changing the scene. Returns `valid`, coded `diagnostics` with paths (errors block placement, warnings flag floating or unbalanced parts), the parameter `sweep`, and `measurements` at the given parameters: bounds, per-part bounds and instances, true triangle counts, draw groups per slot, datum contact and connected components. It is the authority: some rules are not expressible in pascal://schema/design. Repair and re-validate until `valid` is true. Version 2 designs validate fully but carry a design_version_not_enabled warning: placement accepts version 1 only until the next release.

輸入參數design必填parameters

place_design

Place design

Create one instance of a design (procedural item recipe, object or JSON string) in the scene. The design must pass validate_design. Its mounting picks the host: floor designs go on a level (or a slab or zone of it) or on a named surface of a placed design; wall-side designs on a straight wall face; ceiling designs under a ceiling. Only creates: refusals carry a code (invalid_design with diagnostics, design_version_not_enabled for version 2 designs until the next release, design_too_large above 24 KiB, invalid_placement, node_exists, host_not_found, wrong_host, unknown_surface, does_not_fit) and change nothing.

輸入參數idnamesideslotsdesign必填hostId必填position必填rotationsurfaceIdparameters

measure_stair

Measure stair

Inspect actual flight risers, walking-line going, slope, arrival, uniformity, headroom against floors, ceilings and stair bodies, and design-target diagnostics without changing measured geometry.

輸入參數stairId必填available

fit_stair

Fit stair

Explicitly replace stair riser proportions with uniform risers from design targets. Optionally fit going by changing flight runs or arc sweep; preserves landing elevations. Optional straight, L or U layout replaces the flight chain with width-sized turning landings or quarter-turn winders. Winder going is measured along the selected walking line. One atomic edit.

輸入參數turnwidthfitRunlayoutstairId必填targetsdivisioninnerGaplandingDepthturningStrategywalkingLineOffset

find_by_type

Find by type

Find everything of one type in the scene, wherever it lives: nodes of that kind (column, door, window, wall, stair, imported-mesh, a plugin's kind…), items of that category, lights (any item that emits light), and typed parts inside scripted objects, windows and doors (a porch's columns, a window's grille). Narrow it to a level, a parent, a zone or an importer's source ids; without a type, every node the other filters match. A word that names nothing (no node kind, nor an item category or part type in this scene) is refused with the kinds the scene holds. Each result gives the node's id, name, kind, level and parent, with its metadata and provenance when it has them (one over 1 KB is named in omitted instead); get_node reads a node whole. Parts are read-only: they come back with their object's id, the part id and, for objects directly on a level, its bounds in level coordinates; edit them through their object. count says how many match; past limit (200 unless asked) the rest are left out of results, with truncated: narrow the call or raise limit.

輸入參數typelimitzoneIdlevelIdparentIdsourceIdsourceIdPrefix

list_levels

List levels

List every level of every building in floor order: id, name, floorIndex, building (parentId), role (occupied storey, roof-only level or support level), child count, gradeStanding (at-grade, partly-below-grade or below-grade against the ground line and the terrain), isGroundLevel, and which one the person is viewing (isActive); groundLevels names, per building, the level at grade (levelId, whether the building names it, floorElevation, gradeElevation, floorOverGrade, seated). A roof-only level is the roof, not a storey.

無需輸入參數。

get_node

Get node

Get the full data of any node by its id: its fields as stored, with what the scene decides for it beside them (a wall's or a ceiling's resolvedHeight, and heightIsExplicit, false when it follows its storey; a wall's resolvedThickness; a door's or window's resolvedOpening, its floor and what it hangs from; the displayName a level or a floor plate is shown by); its ancestorIds, its parent first (a door's wall, then the level, the building, the site); and the scene materials (scene:<id>) its finishes use. A container's node lists its children as stored; any that name no node are listed again in missingChildIds. A node its schema rejects comes back as stored, with nothing derived (verify_scene says what is wrong).

輸入參數id必填

get_level_summary

Get level summary

Summarise one level so nothing on it goes unseen: its role and counts, then walls (length, heights, doors and windows), zones (name, area, size, floor_choices), slabs, ceilings, items (floor, wall and ceiling), floor openings, stairs, roofs, and anything else by type. Zone polygons: get_zones; a node's full data: get_node.

輸入參數levellevelId

get_walls

Get walls

Get the walls of a level: start and end in metres, length, stored and resolved height, thickness, and their doors and windows. wallFaceChains gives base-chain provenance for geometric faces a (left of start → end) and b (right): ref (authored face slot), legacy (inline spec), or default (declared fallback ref). These are not final rendered finishes: room intent, face paint regions, exterior assembly cladding and reference availability can change appearance. A chosen white or default-matching ref is legitimate; fallback alone is not a defect or a requirement to paint.

輸入參數levellevelId

get_zones

Get zones

Get the zones (rooms) of a level: name, colour, polygon and holes in metres, area in m² (holes taken out), bounding size, and floor_choices: the floor plates the room can take (key, plateId, name, current; drawn and mezzanine flags). A room shares its floor plate with the rooms next to it unless detached. Persisted authored wall intent is wallMaterial (string, or null when absent) and wallOverrides (records of wallId, geometric face a or b, and finish; [] when absent). This is not a final rendered finish or visual verification: local overrides and face paint can take precedence, and absent room intent is not a defect or a requirement to paint.

輸入參數levellevelId

duplicate_level

Duplicate level

Copy a level with everything on it, as the editor does: fresh ids, internal links kept, and units whose rooms are all on that level copied too; plan references, scans and spawn points stay behind. The copy goes above the original (or below it) and the floors past it move up one.

輸入參數namepresetlevelIdposition

set_ground_level

Set ground level

Make a level the building's ground floor: the one standing at grade, its floor 8 in over the ground (about 6 in on cast concrete foundations, 12 in on stone: the building's foundation construction seats it; the result names it). A building's levels stack on a ground line; the levels with a lower floor index are below grade (basements), the ones at or over it are not, and list_levels reads it as groundLevels (levelId, floorElevation, floorOverGrade, seated) and each level's gradeStanding (at-grade, partly-below-grade, below-grade) and isGroundLevel. This names the level on the building, seats the building so that level's floor datum, the top of the foundation where the wood framing starts, stands 0.203 m (8 in) over grade on a block foundation, 0.15 m on cast concrete (Europe), 0.3 m on stone (a historic style) (flat site: world Y 0.203; sculpted terrain: 0.203 m over the mean ground height under its floor), moving the building in height only — the plan stays where it is and every level keeps its place relative to the others — and renumbers the floor indices so it is 0 and the ones under it are -1, -2…. A level added below (add_level position below) stacks under the lowest level without moving the ground floor. A roof level or a support level cannot be the ground floor (not_a_storey). One undo step. Refusals: level_not_found, not_a_level, no_building, not_a_storey. Answering unchanged: true when the level is already the named ground floor, at that seat, at index 0.

輸入參數levelId必填

verify_scene

Verify scene

Optional whole-scene evidence: per-level content, roles (storey, roof-only, support) and the spaces its walls enclose, then every problem found, each with a type: empty levels, walls with no room or door, a space the walls enclose with no room over it (space_no_room) or with several and no wall between them (space_many_rooms), a room the walls do not close (room_not_enclosed) or with no name (room_unnamed), rooms with no floor or ceiling, storeys with no stair, roof levels misused, walls intersecting a covering slab or ending coplanar with its walking surface (wall_covering_slab_contact, with wallId and slabId), openings off their wall, stairs off their slab or blocked, furniture blocking a door or overlapping, a house past its lot line or in its required yards, every node field its schema rejects (schema_invalid, with its nodeId and path; valid is then false, and the level holding the node lists it as unread and is measured once it is fixed), a node kind no schema here knows, once per kind (schema_unknown_kind, info: a plugin kind), a hidden site (site_hidden). Static authored ceiling objects report transformed bounds extending above their ceiling attachment and bounds overlapping solid covering slab bands, with node IDs and measured metres; stored and derived slab holes are subtracted. Wall-mounted objects report a front oriented toward their host; static authored wall bounds report wall-body overlap and extension through the opposite face. With settled editor meshes, catalog wall bounds and large competing coplanar faces against the host or another item on the same wall are also measured within 1 mm. Hidden back-to-wall contact is not competing face evidence. These are observations, not instructions to change the scene or proof of exact mesh intersection. Bounds are measured in the owning building frame, including stacked level elevations. Unknown catalog raw bounds without meshes, animated models, unsupported attachment frames, unresolved scripted wall cuts, roo

無需輸入參數。

set_checkpoint

Set a checkpoint

A named moment in your work (a roof, a floor, a batch of walls) that verify_scene({ since: name }) can compare against: it lists what was created, changed and deleted since, grouped by level and kind with a view each, and what you made that is gone. "start", the beginning of your work, is always one; a name used again replaces the first; a session keeps at most 8. A checkpoint is kept with your session, not in the project: it changes nothing in the scene, enters no undo, and does not restore anything. Refused: too_many_checkpoints (naming them), scene_too_large, name_required.

輸入參數name必填

design_brief

Write the design brief

The design brief of the request, written once for the whole request (piece by piece when the request invites it): the request read axis by axis (action, size, pieces, openings, style, materials, source, dressing), each axis bounded by the person's own words or open with a design statement, a vision, and where the objects come from. It is recorded as the design record of the turn and answers with what it unlocks. It changes nothing in the scene. Furnishings are placed or made after a brief: place_items, furnish_room, place_in_room (the catalog's) and add_object, place_design (the agent's own), also inside run_batch. Before one, search_assets and view_assets read the catalog and every other tool works as usual; rooms, walls, openings and finishes are not furnishings. Bounded and open, in four readings. "a 4 by 5 m bedroom with a double bed, two nightstands, a wardrobe, one window, one door": action, size, pieces (as a minimum: those pieces must be there, more may come) and openings bounded; style, materials, source and dressing open. "add a lamp next to the sofa": action and dressing bounded by "add a lamp" (one object in a room that exists); style open, its design statement saying how this lamp is designed. "a bedroom with only a bed and a lamp": pieces exact and dressing bounded by "only a bed and a lamp"; style and materials open, the bed and the lamp designed, not defaulted. "Build a house": the action bounded only by "a house"; every other axis open, the dressing included. source open: the pieces are made (add_object, place_design), view_assets shows the catalog's pieces for reference, and furnish_room, which places a whole stock set, stays closed. asIs keeps technical equipment only (sanitaryware, kitchen and laundry appliances, heating and similar services): each piece is named with a reason and was shown by view_assets earlier in the same turn (chat) or session (MCP); furniture is made, not kept as it is. source catalog, brand or existing: place_items, furnish

輸入參數asIsaxesbrandbeyond必填source必填vision必填sourceQuote

add_wall

Add wall

Add a single wall between two points on a level, straight or, with curveOffset, an arc. Refused with a code: a level that is not a storey (a declared roof level), and a wall shorter than 1 cm. On a level below grade, a wall against the ground (on the outline of the walls of the level) is a foundation wall, built of what the building's region says: 8 in CMU with 2x4 pressure-treated furring laid flat inside (0.241 m) in the United States and where the scene does not say where the building stands, 25 cm cast concrete insulated and waterproofed outside (0.354 m) in Europe; the result names the region it read; a partition inside the outline is an ordinary wall; the result says which.

輸入參數end必填levelstart必填heightlevelIdthicknesscurveOffset

add_fence

Add fence

Add a fence along points on a level: one fence per side, a whole run or a lot's boundary (closed) in one call. Privacy (solid panels) by default; picket for a picket fence, slat for vertical slats, rail for open rails, horizontal for cladding boards, guard for a deck's barrier. Fences stand on the level, as the editor's fence tool puts them. Refused with a code: a side shorter than 10 cm (fence_too_short), a roof level (roof_level).

輸入參數levelstyleclosedheightpoints必填levelIdbaseStylematerialPreset

add_corner_window

Add corner window

A window that wraps the corner where two walls meet: one window on each wall, both running to the corner and joined, the glass fused at the corner (post none, a fixed window's default) or meeting at a thin post (post post). Any angle the walls make, a bay's 135° as well as a square or an acute corner. The corner is the point the two walls end at ([x, z], as get_walls gives their ends); width is how far the glass runs along each wall from the corner, the same on both unless widths names a wall. Each side is placed by add_window's rules (height, sillHeight, windowType, style the same on both sides; with no sillHeight, centred on the lower of the two walls). The walls are the level's (levelId, default the floor the person is viewing): stacked storeys share their corners' points. Refused with a code: no_corner where no two walls of the level end at the point, corner_ambiguous where more than two do (name two with wallIds), corner_angle for a corner nearly folded back or nearly straight, corner_not_fused for post none on a window type other than fixed, width_exceeds_wall. Returns windowIds and wallIds; the editor's window panel shows the pair under Corner.

輸入參數postlevelstylewidthcorner必填heightwidthslevelIdwallIdssillHeightwindowType

merge_windows

Merge windows

Make two windows that exist one window, as the editor's Merge windows does: nothing is recreated. On two walls that meet at a corner, each window within 0.30 m of that corner, they become one corner window (the add_corner_window result): each is slid to the corner keeping its width, the glass fused (post none), 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° (walls_not_meeting, corner_angle, window_not_at_corner, height_exceeds_wall). On one wall, two windows with at most 0.30 m between them and nothing between become one window spanning both outer edges, from the lower sill to the higher head, with the first window's type and style (windows_not_adjacent, opening_between). Refused with a code and a sentence that says why: window_not_found, not_a_window, same_window, window_not_on_wall, window_already_joined (unwrap it first), windows_already_joined, opening_overlap (sliding to the corner would land on another opening). Returns kind (corner or same_wall), the windows that stay (windowIds) and the one that went (removedIds). Use it for two windows already built that open a corner or one wide opening; add_corner_window builds a new pair, and verify_scene's corner_unjoined names two windows this joins.

輸入參數windowIds必填

add_level

Add level

Add an empty level to a building, as the editor does: above its top storey, under the building's roof level, which moves up one so the roof stays on top; or below its lowest level for a basement, which goes one storey under the lowest level and every existing level keeps its place, the ground floor included. On a scene with no building (a new or cleared scene), it starts the scene as the editor does: a site, a building and its ground level. A level that ends up below grade says in its result how a new wall on it is built (wallConstruction): a wall against the ground is a foundation wall, built of what the building's region says: 8 in CMU with 2x4 pressure-treated furring laid flat inside (0.241 m) in the United States and where the scene does not say where the building stands, 25 cm cast concrete insulated and waterproofed outside (0.354 m) in Europe; the result names the region it read, a partition is an ordinary wall. To copy a floor with its content, use duplicate_level.

輸入參數nameheightpositionbuildingId

create_stair

Create stair

Create a staircase rising from a level, as the editor's stair tool builds one: straight, L (a flight, a landing as deep as the stair is wide, a flight turned 90°) or U (a flight, a landing across, a flight back beside the first). It rises to the level above and owns the floor opening it cuts in every floor it passes; when no level stands above, a blank one is made for it. toLevelId null makes a flight to no level (porch steps, a deck, a raised platform): it rises height, cuts nothing and makes no level. Refused with a code: a flight to or from a declared roof level, and a toLevelId that is not above the level it rises from. GEOMETRY (get this wrong and the stair pokes through a wall): - (x, z) is the middle of the bottom riser, where the flight starts. The first flight runs from there along the climb: at rotation 0 it covers x − width/2 .. x + width/2 and z .. z + its run. - rotation in degrees about Y: 0 climbs toward +Z, 90 → +X, 180 → −Z, 270 → −X. Point the climb into the room, away from the wall the bottom step sits against. - An L or U turns to the climber's left or right (turn). The result gives the whole footprint and the point the top step arrives at. - Keep 0.9 m clear at the foot and at the head of the flight, and the whole footprint inside one room on both levels (a hall is typical): read the room's outline first (get_zones). OPENINGS: by default the stair owns the opening over its flights and keeps it as the stair moves (openingOffset widens it). An opening of another size, centre or turn, through a slab or ceiling you name, or through one of the two only, is cut as an opening the stair owns, as given.

輸入參數x必填z必填nameturnlevelstepswidthheightlayoutlengthlevelIdrotation另有 15 個

create_roof

Create roof

Cap a storey with a roof over its footprint (its walls' outline, else its floor plates, else its rooms), swept in the building's own axes into rectangles, one roof segment each. Without a roofType: a gable for one rectangle, hips for a few wings, flat for a large block. The roof goes on the building's roof level above the top storey (the empty level directly above when it is the only one; made, named Roof, when there is none), and stands on the storey's wall tops, so exploded and solo level views lift it off the plan. Glazed construction supports gable, straight shed and barrel. Barrel depth is the span; barrelRise is at most half the span. glazing controls frame, glass, grids and panels. Paint glazing-frame, glazing-glass or glazing-solid. Call it once the storeys and their exterior walls stand. Refused with a code: storey_above (another storey stands above: a roof caps the top one; a lower wing's roof is drawn with the Roof tool), building_required (no level named on a site of several buildings: pass a level of the one to roof), roof_exists (the storey already has a roof: edit it, or delete_node it first when the person asked for a new one), no_rooms (no rooms or closed walls on any level of the building: pass width and depth to roof a rectangle of your own).

輸入參數namedepthlevelpitchwidthcenterglazinglevelIdoverhangroofTypebarrelRiseroofSystem另有 6 個

move_roofs_to_roof_level

Move roofs to the roof level

Move the roofs a storey holds on its own walls to the level the roof rule gives them, as a roof's panel does by hand (Move to the roof level): over the top storey, the building's roof level (made, named Roof, when there is none); under an upper storey, that storey. Each roof stands exactly where it stood, its segments, finishes and id kept: only the level holding it changes, so exploded and solo level views show the storey's plan with no roof over it. For a project built before roofs had their own level, when verify_scene reports roof_mixed_in_storey. Refused with a code: level_not_found, no_roof_on_walls (the storey holds no roof standing on its walls; a porch roof on its beam and a lower wing's roof on the storey above already sit where they belong). Returns the roofs moved (roofIds), the level they went to (roofLevelId) and the roof level made for them (createdRoofLevelId).

輸入參數levelId必填

place_items

Place items

Place one or more catalog items (furniture, fixtures, plants, art, lights) in one call. Each assetId is a known catalog id, available from search_assets results or existing scene item data, never guessed. Positions are level (x, z) metres. An item stands on the floor of the level, or on the host it names (targetNodeId): a wall (art, a sconce: y is the height of its bottom above the floor, required; it goes on the side of the wall the point is on), a ceiling (a pendant, a downlight: it hangs flush under it), or an item standing on the floor (a lamp on a nightstand: it rests on its top; on an object built with add_object it rests on the real surface under the point, such as a porch landing, or a ceiling item hangs from the underside above it, such as a sloped vault, and the result names it in restingOn). A room, a slab or a level as the target means its floor. Each item is placed or refused on its own (asset_not_found; outside_rooms: an indoor item on the floor outside every room of a level that has rooms — garden and outdoor items may stand outside; height_required, item_too_tall, unsupported_host, host_not_found, host_not_on_level; blocks_door: a floor item standing where a door needs space, with a spot in its room that clears every door; too_large_for_room: a floor item its room cannot hold in any turn).

輸入參數items必填levellevelId

delete_node

Delete node

Delete a node with everything under it, as the editor's Delete does: a wall goes with its doors and windows, a level with everything on it. The floors above a deleted level keep their index.

輸入參數id必填

create_room

Create room

Create a room on a level from its polygon: a wall per edge, an edge a wall already runs along reusing that wall (rooms share their boundary with their neighbour), and the room zone that names it. The floor plate and the ceiling are derived from the room — never author a slab or a ceiling for it. Declare the room's doors and windows in the same call by polygon edge (wallIndex) and t along it, no wall ids needed; each is placed with add_door's and add_window's rules, and one that cannot be is skipped and listed in skippedOpenings with its code. Returns zoneId, slabId, ceilingId, wallIds in edge order (null where no wall), doorIds, windowIds. outdoor: true draws a terrace instead: separators where no wall runs, no walls of its own and no ceiling. Where the walls already enclose a room the reconciler numbered, the call names that room instead of adding a second one; where they enclose a room with a name of its own (not a number the editor gave it), it is refused (room_named_by_person) unless rename is true, which you pass only when the person asked for the rename, so a name is never changed silently.

輸入參數name必填colordoorslevelrenamelevelIdoutdoorpolygon必填windowswallHeightwallThickness

furnish_room

Furnish room

Place the catalog's fixed furniture set for a room type: one set per type, not matched to a style. The bed, sofa, counters or toilet go against the wall facing the door, the rest along a side wall, each set off its wall by its own depth. Name the room by zoneId (its level and outline come with it), or give its polygon and level. The door wall is the edge a door of the room stands on, else doorWallIndex, else edge 0. 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. Items stand on the room's level. The set per room type: bedroom: double-bed (a side of 3.2 m or more) or single-bed, two bedside-table, dresser (10 m² or more), closet (13 m² or more); kitchen: kitchen (back wall of 2.6 m or more) or kitchen-counter, stove (back wall of 3.5 m or more), fridge; bathroom: toilet, bathroom-sink, bathtub (6.5 m² or more) or shower-square; living: sofa, coffee-table, livingroom-chair, tv-stand; dining: dining-table, two dining-chair (four when both sides are 3 m or more); laundry: washing-machine, drying-rack; entry and hallway: coat-rack (both sides 1.4 m or more); storage: closet. The result lists each piece placed with its size and id, and each piece skipped with its reason. view_scene with interior { zoneId } shows the room from inside, at a person's height.

輸入參數levelzoneIdlevelIdpolygonroomType必填doorWallIndex

paint

Paint

Give walls, slabs, ceilings, roofs, columns, fences, doors, windows, stairs, blocks, cabinets, shelves, items and the other built parts a finish in one call, as the editor's Paint does. Give one finish: color (#rrggbb, painted with the nearest of the library's flat colours, said with how near); material, a library:<id> (find it with search_materials) or scene:<id> ref, or a native material {properties: {color, roughness, metalness}} for an exact finish, kept as a scene material (materialName names a new one) and reused when the scene holds an equal one; or erase: true, back to the surface's default. role picks the surface: a wall's face a or b, or exterior and interior (the faces turned out of its rooms or into them, else by its stored sides; a partition is interior on both; a wall whose sides are unknown, or with rooms along only part of a face, is refused: name its face, a or b), its trims aSkirting, bSkirting, aCrown, bCrown, aChairRail, bChairRail (skirting, crown or chairRail: that trim on both faces), a curtain wall's curtain-frame, curtain-glass, curtain-solid; a slab's surface, side, edge, riser, underside or foundation; a ceiling's surface; a roof's top (the covering), edge (the gable ends and the trim band), wall (the soffit and the deck edge, and the fascia and rake boards unless they have their own) or fascia (the fascia and rake boards, a fascia band); a glazed roof's glazing-frame, glazing-glass or glazing-solid; a column's shaft, base, capital or frame; a fence's posts, infill, base or rail; a door's panel, frame, glass or hardware; a window's frame or glass; a stair's treads, body or railing; a block's body; a cabinet's front, carcass, countertop, plinth, hardware or glass; another part's own slots, which a refusal names; an item's surfaces by the names its model gives them (its painted ones are in get_node's slots), which paint cannot check. Without a role: both faces of a wall, a slab's or a ceiling's surface, a roof's top, a glazed roof's frame an

輸入參數rolecolorerasetargets必填materialmaterialName

place_in_room

Place an item in a room

Put one floor-standing catalog item in a room where it fits, without working out a position. Wall/ceiling-attached assets need a host: place_items accepts targetNodeId and, for a wall, y for the bottom height. A wall-bound floor piece (fridge, counter, stove, sink, TV stand, dresser, wardrobe, sofa, bed, bedside table, toilet, bath, shower, bookcase, cabinet) stands against a wall, centred in the widest free stretch of that wall (clear of every door and every other item), its front toward the room; it is never put in the middle, and when no wall has room the answer says how wide the widest free stretch is. A free-standing piece (dining table, rug, coffee table, plant, armchair) takes the middle of the room, or goes with what it belongs with: a coffee table and an armchair face the sofa. A chair goes around its table, one call per chair, facing the table, and is refused when the room has no table. Explicit edge, align, offset or inset instead requests a wall free run for any floor piece. Name the room by its zoneId (create_room, get_zones), or by its polygon and level. The item is a catalog id from search_assets. To furnish a whole room by its type in one call, use furnish_room.

輸入參數edgealigninsetlevelfacingoffsetzoneIdassetId必填levelIdpolygonrotationDegallowDuplicate另有 1 個

get_site

Get site

Read the site a building stands on, in its level coordinates: the lot and its area, with the footprint a house usually covers on it (30 to 45%); the buildable area inside the setbacks with its centre, its area, each edge's frontage and depth, and its front edge (behind the street; while no street is recorded, behind the edge facing north, which the setbacks take as the front); the street side with the lot's frontage and depth on it; north; the ground (sloped with its fall, read flat, or not read); the address, parcel and zoning when known; whether the lot is only the new-project placeholder; and where the footprint sits now with its yards.

輸入參數sitebuilding

place_building

Place building

Stand a building on its site before or after its walls go up. It moves only the building node, so walls, levels and roofs keep their coordinates and move with it. The footprint (outline, the plan you are about to draw, in the coordinates you will draw it; else the walls standing) lands centred in the buildable area, or at the yards a site plan or the person gives. A building with walls keeps its rotation unless rotationDeg is given (get_site's squareRotationDeg squares it to the street); one with none yet is turned square to the street. Fences, paths and plants on its levels move with it.

輸入參數yardsoutlinebuildingrotationDeg

search_assets

Search assets

Search the item library (furniture, fixtures, lights, plants, outdoor) by keyword, with one or several queries in a call. The catalog is a limited set that varies by server: what a search returns is what exists. Results provide valid asset ids and placement facts when these are not already known. A query matches the items whose name, id, category or tags hold every one of its words. Returns one group per query, each item with its id, name, category, dimensions [width, height, depth] in metres and attachTo (null on the floor, else 'wall', 'wall-side' or 'ceiling'). Related lookups (sofa, coffee table, tv stand) can share a call.

輸入參數queries必填

view_assets

View catalog items

Pictures of catalog items by id, each with its size and the names of its paintable surfaces (paint takes one as role).

輸入參數ids必填

search_materials

Search materials

Search or browse the material library (the finishes paint gives), several queries in one call: find a library material's ref here rather than guessing it. The library is Pascal's materials and, in the editor, the ones it holds besides (the community's, yours, your workspace's). A query's words match the words of a material's id, name and kind, or a piece of its id (plank48, cco), building words as the library writes them (timber is wood, weatherboard is siding, render is stucco), colour words by its shade (dark, light, white, grey) and surface words (roof, wall, floor, ceiling) by what it suits; a word of one or two letters, or one as common as 'the', searches nothing. Without a query, a browse lists by name every material of its kind, or that suits its surface (flat colours suit every one), or the whole library. Returns one group per query: each material's ref (library:<id>), name, kind, the surfaces it suits and its colour, best first (a browse by name), 12 at a time for a search and 150 for a browse; the words no material answers (missing), so a finish the library lacks is said, not guessed; and on a long list how many more there are (more) and the offset that lists them (next).

輸入參數queries必填

cut_floor_opening

Cut floor or ceiling opening

Create a persistent opening on a room or level from a polygon or rectangle. A floor cut also opens the automatic ceiling directly below; a ceiling cut can also open the floor above. Use cutsAdjacent:false for a one-surface cut. A mezzanine room cuts only its own plate. levelIds repeats the opening as a shaft on several levels.

輸入參數rectzoneIddrawnOnlevelIdpolygonlevelIdscutsPrimarycutsAdjacent

remove_floor_opening

Remove floor opening

Remove an authored floor or ceiling opening and restore the derived surfaces it cut.

輸入參數id必填

set_floor_foundation

Set floor and foundation

Supply slabId or slabIds; grouped plates receive the same floor-top displacement, while edge slots apply only to the first plate. A ground-contact floor has two inputs: thickness (slab, min 0.01 m, grows upward from the foundation) and foundationHeight (0 = on the ground, no foundation; > 0 = raised on a solid foundation; foundation.type "solid" alone raises it 0.3 m, "none" puts it on the ground). The floor top is derived and never floats: grade + foundationHeight + thickness; everything the floor carries (walls, openings, items, rooms, storeys above) moves with the top. Legacy floorHeight (a target top) maps to foundationHeight = top - grade - thickness, never below 0; null = on the ground. On upper plates the underside stays on the walls below: thickness (or floorHeight as a target top) changes the top; only ground-contact plates accept a foundation. Explicit room floors translate with the floor to preserve steps.

輸入參數patch必填slabIdslabIds

set_room_floor_construction

Set room floor construction

Edit the single floor volume associated with a room, with the same patch as set_floor_foundation. On a ground-contact floor: thickness (slab on the foundation, grows upward, min 0.01 m) and foundationHeight (0 = on the ground; > 0 = raised on a solid foundation); the top is derived as grade + foundationHeight + thickness and carries everything on the floor. A drawn slab takes thickness, finishes and a numeric floorHeight (its top) only. Supply slabId when the room spans multiple construction domains; an absorbed legacy slab is edited through its derived base plate.

輸入參數patch必填slabIdzoneId必填

rebase_floor_reference

Rebase floor reference

Change or clear one or more footprint reference datums in one atomic edit, compensating supported storey offsets so existing world geometry stays in place. Supply slabIds together for a shared storey.

輸入參數slabIdslabIdsreferenceFloorElevation必填

create_mezzanine

Create mezzanine

Add an open-below room inside a host reference polygon, with a separate thin plate and railings on open edges. Minimum area 1 m²; elevation defaults to half the storey snapped to 0.05 m, thickness to 0.2 m. Elevation must exceed thickness and stay at least 0.3 m below the resolved footprint plane; overlapping mezzanines are refused. The host floor and walls stay intact.

輸入參數polygon必填elevationthicknesshostZoneId必填

move_zone

Move or rotate room

Move room intent and contents. Shared walls and openings stay with neighbours; the room takes plain copies. Rotation is radians about Y, before translation, about the room centroid unless a pivot is supplied. Crossings split walls and overlaps re-derive rooms. Force can reposition obstructing openings when they fit; otherwise the edit is refused.

輸入參數forcerotatezoneId必填translate

duplicate_zone

Duplicate room

Copy room intent and contents with fresh IDs; shared walls are copied without openings. Rotation is radians about Y before translation. Collinear destination walls are merged, preserving their IDs and hosted children. Crossings split walls and rooms re-derive; overlap is allowed. Force can reposition obstructing openings only when they fit.

輸入參數forcerotatezoneId必填translate必填

rotate_zone

Rotate room a quarter turn

Rotate a room by quarterTurns 1 or -1 about its centroid, then align its first reference vertex to gridStep (default 0.5 m). Crossings split walls and collinear overlaps merge. Force can reposition obstructing openings only when they fit.

輸入參數forcezoneId必填gridStepquarterTurns必填

lock_outside_faces

Keep outside dimensions

Keep the building's outer size: put exterior wall faces on their fixed reference lines, moving wall bodies inward so walls thicken inward and later thickness edits preserve outer dimensions. Supply either levelId or zoneIds.

輸入參數levelIdzoneIds

set_zone_intent

Set room intent

Update room name, floor finish/elevation/regions, floor.footprint ("new" mints a unique floor key tied to this creator room, an existing key joins that floor on this level, null returns to the shared floor). Touching rooms with the same key share a base plate; disconnected pieces share construction settings. Conversion preserves world geometry in one edit and refuses when infeasible. get_zones exposes floor_choices. A keyed floor uses "<creator room name> floor" while the creator remains on that key; otherwise it uses the largest named room. Existing keys such as "own" without a creator use that fallback. A user-given plate name wins; the shared floor stays "Shared floor". Keyed rooms can have raised or sunken floor.elevation, stored as a level-local walking top. Slab thickness, foundation height and edge finish use set_room_floor_construction, updating every piece of the key. Update ceiling paint regions (ceiling.regions: [x, z] polygons with a finish, later wins), step finishes (floorStepFinish for every step of the room; floorStepOverrides: [{ key, step?, finish }] per doorway, key = the door id the step sits under or the lower room id), wall material and construction opt-outs. daylight { artificialLight, mechanicalVentilation } declares a habitable room lit and aired by artificial means (the IRC R303.1 exceptions to daylight; both true), stored in the room's metadata; a bedroom, living room or kitchen needs a window to outside or this declaration, above and below grade alike. Absent fields are unchanged; null clears.

輸入參數patch必填zoneId必填

divide_zone

Divide room

Divide a room with an open path (endpoints snap to its boundary; the seed side keeps its id) or a closed island (outer room keeps its id). The two rooms are parted by a separator (the editor's Separator: a room boundary with no wall), not a wall. Supply path or the legacy two-point cut. Islands need 0.25 m² and 5 cm wall clearance.

輸入參數cutpathclosedzoneId必填endBoundaryIdstartBoundaryId

merge_zones

Merge rooms

Merge two rooms by removing the separators they share (the editor's Separator: a room boundary with no wall). Walls are preserved; wall-only boundaries return a conflict.

輸入參數zoneIds必填

delete_zone

Delete room

Delete a room. A room Divide made, which shares a separator (the editor's Separator: a room boundary with no wall) with a room on its outline, merges back into that room: the separators go, walls and items stay (mode "merge"). A room whose every boundary is a wall shared with other rooms is refused with a "shared-walls" conflict and nothing changes (mode "blocked"); delete one of its walls instead. Otherwise the room goes with its unshared walls, separators and their openings (mode "delete"), and contents chooses whether its items are kept or deleted. Returns the disposition payload.

輸入參數zoneId必填contents必填

run_batch

Run a batch of scene tools

Optionally run several scene tools in one call: in order, each on the scene the calls before it left, applied as one undo step, with a report per call. Supports repeated or known-in-advance work; individual calls remain available. A call cannot use an id another call of the same batch creates; created ids are available in the report for subsequent calls. onRefusal stop (default): a refused call stops the batch and nothing is applied; skip: the others are applied and each refusal is reported; add_object calls are independent of each other under both: one that fails is reported with its own error and the others are applied, each with the receipt a single add_object gives. Tools: paint, search_materials, list_levels, get_node, get_level_summary, get_walls, get_zones, duplicate_level, verify_scene, delete_node, measure_stair, fit_stair, find_by_type, create_roof, add_wall, add_fence, add_level, create_stair, add_corner_window, merge_windows, add_column, add_object, place_design, place_in_room.

輸入參數calls必填onRefusal

list_collections

List collections

List the scene's collections with their elements (id, name, type, level), and the templates a collection can start from.

輸入參數collectionId

edit_collection

Edit collection

Create, rename, recolour or delete a collection, or add and remove its elements. A collection is a named set of any scene elements (lights, windows, doors, columns, objects built with add_object…), listed in the editor's Collections; being in one changes nothing about the elements. Without collectionId the call works on the scene's collection with that template, or else that name, and creates it when there is none.

輸入參數addnamecolordeleteremovetemplatecollectionId

add_column

Add column

Add a structural or decorative column at (x, z), or rebuild one by nodeId. Supports native fields or optional code using the same module and conventions as add_object, with floor mount and its bottom centre at the support point. Params named height, width and depth drive the native size controls. Only add columns when requested.

輸入參數xyzcodenametagsdepthlevelstylewidthheightnodeId另有 7 個

add_object

Build object

Author parametric Three.js furniture, decor and custom objects of any shape, dimensions or style, whether or not the catalog holds something similar (the catalog is a limited set that varies by server). Examples include a low oak table, a slatted bench, a paper lantern, built-ins, custom columns and capitals, mouldings and trim, panels, exposed beams, ceiling bodies, canopies, a porch and railings. Pascal runs the module in a sandbox, stores the result and places it as one object the user can move, paint, and ask you to edit again. Module shape (import THREE from the three package as usual; the addons below too): // also available: three/addons/utils/BufferGeometryUtils.js, three/addons/geometries/{RoundedBoxGeometry,ConvexGeometry,LoftGeometry,ParametricGeometry}.js, three-bvh-csg (Brush, Evaluator, SUBTRACTION, ADDITION, INTERSECTION) export const params = { width: { default: 4.8, min: 3, max: 8, step: 0.1, unit: 'm', label: 'Width' } } export const mount = 'floor' // 'floor' | 'wall-side' (on a wall face) | 'wall' (through a wall, like a window) | 'ceiling' export default function build({ params, THREE }) { const group = new THREE.Group(); /* … */ return group } One object is one feature that changes together: a table, a bench, a porch, a railing run, a fireplace surround, a ceiling with its beams. Never a whole house, and never walls, rooms, floors, roofs, stairs, doors or windows: those have their own tools. A new object's reason records its design intent: what the authored shape, dimensions or style contributes to the request. The scene check lists every authored object with its reason; no catalog-exhaustion proof is required. On a floor, a plain box with a wall's size or a floor plate is refused with the tool to use (use_walls, use_slab); a detailed object with a wall's size (a bookcase, a screen), or one named after something Pascal builds, is built with a hint naming the tool. Conventions (they make the object work in Pascal; follow them): - Me

輸入參數codenamesidetagsnodeIdparamsreasoncategorylanguageparentIdpositionrotation另有 1 個

author_scene

Author scene

Turn a whole design you wrote as free three.js into an editable Pascal scene, in one call. You read your own module however you like (its source, running it and reading the objects, both) and hand over a conversion payload: levels, walls with their openings, rooms, slabs, columns, railings, every piece of furniture as an authored object with its own module, and lights. Pascal does the rest server-side: compiles and stores each item, places it, derives the rooms and their floors, paints, runs its checks, saves, and answers with a gate report you iterate against. Nothing you hand over is silently dropped: what cannot be placed comes back by name with a reason, and what Pascal corrected (a wall end snapped onto the centerline it meets) is listed. Coordinates: metres, plan x/z, Y up, each level's floor at y = 0 in its own frame. A wall is its centerline; walls meet where centerlines meet, so end a partition on the centerline of the wall it runs into. An opening's 'along' is measured from the wall's start to the opening's centre; give each opening its type (how it opens) and style (its look), or its own module for a shape they cannot express. An item's position is where its bottom centre stands; its module follows the add_object contract (metres, Y up, built in its installed orientation; at most 300k triangles and 32 materials per item; name materials slot_<finish>; drawers, doors and lids as animation clips; a THREE light named light:<id> where a bulb is). A hex colour is written as an exact material, one per distinct colour. Pascal builds a wall's cap, the floor plate and the ceiling itself: fold a module's caps, underlays and floor planks into the wall, the room's floor finish and the room (say so in notes), never into slabs or regions: baseboards are the wall's skirting, a plank or tile floor is the room's floorCode, tiles, panelling or boards on a wall are its faceLeft / faceRight (or the room's wallCode); a slab is a terrace, a platform, a balcony. Build the build

輸入參數moduleadapterpayload必填replacesceneIdbuildingId

get_source

Read object script

The module (three.js or Blender bpy, named by language) an object (or a window, door or column built from code) runs, with its params and their current values; a room's id reads its floor script, the module that builds its floor finish; a wall's id with face reads that face's script (face may be left out when only one face has one). Optional source evidence when the current module is needed. An edited module can be passed back with the same nodeId: to add_object for an object, to add_window / add_door / add_column for their respective kinds.

輸入參數facenodeId必填

measure

Measure

Measure the distance (in meters) between two nodes, or the net area of a polygon node when fromId === toId. Distance is between world-space reference points, returned as fromPoint/toPoint: a positioned node's origin in its host's frame (an item's base, a door's or window's centre in its wall), a wall's or fence's midpoint at its base, a slab's, ceiling's or zone's polygon centroid on its plane, a block's or imported mesh's vertex-bounds centre, a level's plan centre on its base plane, and another container's descendants' centre. `approximate` lists nodes whose renderer derives the pose from data not modelled here (a downspout at its gutter outlet, a gutter at the eave), with the reason.

輸入參數toId必填fromId必填

view_scene

Look at the scene

Look at the building in 3D from a viewpoint you pick and get the picture back: the finished house, every storey and the roof by default, or an explicit Solo/Exploded level presentation, whatever view mode the person has on. Use it to compare what you built with a reference and say what differs before you fix it: the facade from the photo's own camera (camera: its position, aim, field of view and aspect), rendered at the photo's aspect to lay beside it; or from the photo's side at street height (from 'street', 'street-left', …, eyeHeight 1.7), in its light (lighting); one face square on (projection orthographic); the massing from above, or an aerial view (elevation 40). The view frames the target (a building, a level, a wall or a zone; the whole building, its roof included, by default) from outside, unless you place the eye yourself with position or camera. To stand inside a room and see its furniture and finishes at a person's height, give interior {zoneId} (a room: an id from get_zones or create_room): the eye stands in the room a step in from a corner, looking across it, with the cut below; from picks the side of the room it stands on, target something in the room to look at it. To see inside a floor, give interior {levelId}: the other floors and that floor's roofs and ceilings are left out of the picture, and everything on it, walls, openings and furniture alike, is cut at 0.65 of the storey's height (about two thirds), so a tall wardrobe or a door head is cut, not short; a roof level has no inside; with no eye given it looks down into the floor, or into the room or zone you target. A level below grade, targeted alone, is looked into the same way (from outside the ground would cover it), the ground over it drawn as a faint veil. The live view is not changed. A door, a window or an item on a floor frames at detail scale, an opening seen from its outside face: a close-up to lay beside the photo's crop of the same element, to see what differs. Over the MCP the user'

輸入參數fovfrommaxPxphotocameratargetinteriorlightingpositionelevationeyeHeightprojection另有 1 個

create_story_shell

Create story shell

Create one level-owned building shell from a footprint: the perimeter walls. The floor plate and the ceiling are DERIVED from the rooms the walls enclose — never author a slab or a ceiling here, and never pass boundary/autoFromWalls; createSlab / createCeiling / slabElevation are recorded as room intent instead. Use once per story; do not make first-floor walls span multiple stories.

輸入參數levelId必填footprint必填createSlabnamePrefixwallHeightceilingHeightcreateCeilingslabElevationwallThicknessslabMaterialPresetwallMaterialPresetceilingMaterialPreset

add_door

Add door

Add a door to an existing straight wall at t (0..1 along it), or a passage with no leaf (openingKind opening: a cased opening, an arch). The door slides to stay on the wall and reports clamped. Refused with a code, as in the editor: curved walls, walls shorter than the door, and overlapping another door, window or wall item unless force is set. Match the reference with the outline (rectangle, rounded, arch), doorType and style; what they cannot express (glass strips, a pattern of lites, carved panels) is written as a script in code, never left as not possible.

輸入參數tcodenametagsforcestylewidthheightnodeIdparamswallIdcategory另有 11 個

add_window

Add window

Add a window to an existing straight wall at t (0..1 along it), centred vertically on the wall, or on sillHeight above the floor when given. It slides to stay on the wall and under the wall's ceiling, and reports clamped. Refused with a code, as in the editor: curved walls, walls shorter than the window, overlapping another door, window or wall item unless force is set, and a style on a window that is not Fixed (style_needs_fixed_window). Match the reference with the outline (rectangle, rounded, arch), windowType and panes (columns × rows, or a style); what they cannot express (glass strips, leaded lites, a feature frame) is written as a script in code, never left as not possible.

輸入參數tcodenamerowstagsforcestylewidthheightnodeIdparamswallId另有 10 個

apply_patch

Apply patch

Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.

輸入參數patches必填

clear_scene

Clear the project

Empty the project on purpose, back to the default scaffold: a site, a building and one level of the standard storey height. It is the only way to empty a project: a write that would empty it is refused (scene_wipe_blocked), since that is far more often an accident. Call it only when the person asked to start over, and say what they asked in reason.

輸入參數reason必填

create_unit

Create unit

Create a named unit in a building, optionally referencing zones across its levels.

輸入參數kindname必填colorbuildingId必填memberZoneIds

set_unit_members

Set unit members

Replace the zones referenced by a unit. Every zone must belong to a level in the same building.

輸入參數unitId必填memberZoneIds必填

list_units

List units

List units with their member zones and current area reports, optionally filtered by building.

輸入參數buildingId

set_zone

Set zone

Create a polygonal zone on the given level. label is stored as the zone name and properties are merged into metadata. A polygon along walls that already enclose a room is that room, never a second zone: a room still named "Room N", or already named label, takes the label and properties; a room a person named otherwise is refused (rename it with update_node only when the person asked).

輸入參數label必填levelId必填polygon必填properties

undo

Undo

Undo the most recent N steps in the scene history (default 1). Returns the number of steps actually undone.

輸入參數steps

redo

Redo

Redo the next N previously-undone steps (default 1). Returns the number of steps actually redone.

輸入參數steps

export_json

Export JSON

Return the scene as a serialized JSON string. Pass `pretty: true` to indent with 2 spaces.

輸入參數pretty

export_glb

Export GLB

GLB export is not available in headless mode — it requires the Three.js renderer, which is browser-only. Returns a structured `not_implemented` tool error.

無需輸入參數。

check_collisions

Check collisions

Assess declared item footprints with a rotation-aware plan AABB test. Supports an explicit minimum clearance and reports units, source dimensions, skipped evidence, and unsupported checks. Optionally scope to one level or floor-standing items.

輸入參數levelIdcandidatefloorOnlyminimumClearance

list_templates

List scene templates

List the seed Pascal scene templates available to `create_from_template`. Returns the id, display name, one-line description and node count for each.

無需輸入參數。

create_from_template

Create scene from template

Instantiate a seed Pascal scene template into the bridge. Regenerates all ids before applying. When `save: true` and a SceneStore is wired, also persists the new scene and returns the SceneMeta.

輸入參數id必填namesaveprojectId

create_house_from_brief

Create house from brief

High-level hosted workflow for external agents: choose a starter house from a brief, create/save/publish it, and return the editor URL. Use semantic tools afterward for exact customization.

輸入參數brief必填roomsstyleprojectIdconstraintslandscapingprojectNamebedroomCount

create_project

Create project

Create a browser-visible Pascal project for the authenticated user. Use this before save_scene when the user asks for a new project.

輸入參數idname必填isPrivate

get_project_status

Get project status

Authoritative status/debug call for a Pascal project: editor URL, browser-visible version, latest saved version, published version, node count, and graph hash.

輸入參數id必填

save_scene

Save scene

Save the current scene (or a provided graph) to the SceneStore. Without id or projectId it saves to the project this session is bound to (create_project or load_scene). Defaults to a browser-visible draft save so agents can iterate without creating many project versions. Use saveMode: "checkpoint" for meaningful version history.

輸入參數idname必填graphpublishreplacesaveModeprojectIdthumbnailexpectedVersionincludeCurrentScene

load_scene

Load scene

Load a scene from the SceneStore into the bridge. Returns the scene metadata. Throws `scene_not_found` if the id does not exist.

輸入參數id必填

list_scenes

List scenes

List scenes in the SceneStore. Optionally filter by `projectId` and cap results with `limit` (default 100).

輸入參數limitprojectId

delete_scene

Delete scene

Remove a stored scene by id. In Pascal this never deletes the project: it hides it from Pascal's public listings until it is next saved with content, and answers `deleted: false, hidden: true` with a message; its owner can delete it from the projects page. Only someone who can manage the project may hide it. A local scene store deletes the scene. Optionally pass `expectedVersion` for optimistic concurrency.

輸入參數id必填expectedVersion

rename_scene

Rename scene

Rename a scene in the SceneStore. Returns the updated SceneMeta. Optionally pass `expectedVersion` for optimistic concurrency.

輸入參數id必填newName必填expectedVersion

generate_variants

Generate variants

Generate N variations of a base scene by forking and applying seeded mutations. Example: "give me 5 variations of this kitchen". If `save=true`, each variant is persisted via scene operations and returned with an id + URL; otherwise the graph is returned inline.

輸入參數saveseedvarycountbaseSceneId

photo_to_scene

Photo to Pascal scene

Orchestrator: analyse a floor-plan photo via MCP sampling, translate the structured vision result into a Pascal SceneGraph (site → building → level with walls and zones), optionally save it, and swap the bridge to the new scene. Requires host support for sampling.

輸入參數namesaveimage必填scaleHintdefaultWallHeightdefaultWallThickness

analyze_floorplan_image

Analyze floor-plan image

Defer to the MCP host (via sampling) to extract walls, rooms, and approximate dimensions from a floor-plan image. Requires host support for sampling.

輸入參數image必填scaleHint

analyze_room_photo

Analyze room photo

Defer to the MCP host (via sampling) to extract approximate dimensions, fixtures, and windows from a single-room photograph. Requires host support for sampling.

輸入參數image必填

版本歷史

2
  1. v1.0.6最新Oct 23, 2026
  2. v1.0.5Oct 23, 2026

評分

已滿足適用檢查項的 100%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分

其他 Pascal 整合