{"_id":"@aruct/mcp","_rev":"2-6d82d39745d5b0259404aae315f8a3dd","name":"@aruct/mcp","dist-tags":{"latest":"0.3.3"},"versions":{"0.3.2":{"name":"@aruct/mcp","version":"0.3.2","keywords":["mcp","model-context-protocol","aruct","3d","building","editor","ai"],"license":"MIT","_id":"@aruct/mcp@0.3.2","maintainers":[{"name":"tucnow","email":"tucnowsolutions@gmail.com"}],"homepage":"https://github.com/aruct/editor/tree/main/packages/mcp#readme","bugs":{"url":"https://github.com/aruct/editor/issues"},"bin":{"aruct-mcp":"dist/bin/aruct-mcp.js"},"dist":{"shasum":"349a1c71b170a643fe0f29462736b8447f5f8771","tarball":"https://registry.npmjs.org/@aruct/mcp/-/mcp-0.3.2.tgz","fileCount":258,"integrity":"sha512-YdvqeVpAXJWtVdXCo0xgjPY8dtgCXssB3a6KD2j4rpbJvSqcO6wL9lVEUvFnPjj9spR37DPtAGv5q/JcO002xg==","signatures":[{"sig":"MEUCIQCh6Nb3sFXXMPZbDkRJvuOEB03KHAJdJ4Lq3O22AWMyPAIgPVXqy5V29EdR1tAZJTsxUNFkiRROqJ0UjTULsG50LQQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":556387},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./bridge":{"types":"./dist/bridge/index.d.ts","import":"./dist/bridge/index.js","default":"./dist/bridge/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","default":"./dist/server.js"},"./storage":{"types":"./dist/storage/index.d.ts","import":"./dist/storage/index.js","default":"./dist/storage/index.js"},"./operations":{"types":"./dist/operations/index.d.ts","import":"./dist/operations/index.js","default":"./dist/operations/index.js"},"./storage/types":{"types":"./dist/storage/types.d.ts","import":"./dist/storage/types.js","default":"./dist/storage/types.js"}},"gitHead":"b887d0b52f8a848d414d59b37255095a34ac0773","scripts":{"dev":"tsgo --build --watch","test":"bun test","build":"tsc --build","smoke":"bun run scripts/smoke.ts","start":"bun dist/bin/aruct-mcp.js","prepublishOnly":"bun run build && bun test"},"_npmUser":{"name":"tucnow","email":"tucnowsolutions@gmail.com"},"repository":{"url":"git+https://github.com/aruct/editor.git","type":"git","directory":"packages/mcp"},"_npmVersion":"10.9.2","description":"Model Context Protocol server for Aruct 3D editor","directories":{},"_nodeVersion":"23.10.0","dependencies":{"zod":"^4.3.5","@aruct/lingo":"^0.2.0","@modelcontextprotocol/sdk":"^1.29.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"6.0.3","@aruct/core":"^0.9.2","@types/node":"^22.19.20","@aruct/typescript-config":"*"},"peerDependencies":{"@aruct/core":"^0.9.2"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.3.2_1785217070975_0.49025037668207516","host":"s3://npm-registry-packages-npm-production"}},"0.3.3":{"name":"@aruct/mcp","version":"0.3.3","description":"Model Context Protocol server for Aruct 3D editor","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","default":"./dist/server.js"},"./storage":{"types":"./dist/storage/index.d.ts","import":"./dist/storage/index.js","default":"./dist/storage/index.js"},"./storage/types":{"types":"./dist/storage/types.d.ts","import":"./dist/storage/types.js","default":"./dist/storage/types.js"},"./operations":{"types":"./dist/operations/index.d.ts","import":"./dist/operations/index.js","default":"./dist/operations/index.js"},"./bridge":{"types":"./dist/bridge/index.d.ts","import":"./dist/bridge/index.js","default":"./dist/bridge/index.js"}},"bin":{"aruct-mcp":"dist/bin/aruct-mcp.js"},"scripts":{"build":"tsc --build","dev":"tsgo --build --watch","start":"bun dist/bin/aruct-mcp.js","test":"bun test","smoke":"bun run scripts/smoke.ts","prepublishOnly":"bun run build && bun test"},"peerDependencies":{"@aruct/core":"^0.9.2"},"dependencies":{"@modelcontextprotocol/sdk":"^1.29.0","@aruct/lingo":"^0.2.0","zod":"^4.3.5"},"devDependencies":{"@aruct/core":"^0.9.2","@aruct/typescript-config":"*","@types/node":"^22.19.20","typescript":"6.0.3"},"keywords":["mcp","model-context-protocol","aruct","3d","building","editor","ai"],"repository":{"type":"git","url":"git+https://github.com/aruct/editor.git","directory":"packages/mcp"},"publishConfig":{"access":"public"},"license":"MIT","homepage":"https://github.com/aruct/editor/tree/main/packages/mcp#readme","bugs":{"url":"https://github.com/aruct/editor/issues"},"_id":"@aruct/mcp@0.3.3","gitHead":"169eb99dfb6bc63d0dc325249968dc7a8bae4cb7","_nodeVersion":"23.10.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-jmrVODWp16U83AmNE56wMcBlO672N+9iFJyLdG/KpJE2Qt3yvW64WUOGu9S/8z6t+kaDE6RlIAT8Kd2umB9Eow==","shasum":"dc90af3a1fa51ab9cbe40df7f18cff944251a8be","tarball":"https://registry.npmjs.org/@aruct/mcp/-/mcp-0.3.3.tgz","fileCount":258,"unpackedSize":556387,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCFwneGJhFmlqgY9ylsF4pqhcKklmxiIgW5RTMdqWddWAIhAMClJZIU4oeMU0vmK3Hsx0A6n3/6Qiep98sf5qOMvC1x"}]},"_npmUser":{"name":"tucnow","email":"tucnowsolutions@gmail.com"},"directories":{},"maintainers":[{"name":"tucnow","email":"tucnowsolutions@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_0.3.3_1785217956587_0.15081817611314574"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-28T05:37:50.811Z","modified":"2026-07-28T05:52:36.940Z","0.3.2":"2026-07-28T05:37:51.157Z","0.3.3":"2026-07-28T05:52:36.774Z"},"bugs":{"url":"https://github.com/aruct/editor/issues"},"license":"MIT","homepage":"https://github.com/aruct/editor/tree/main/packages/mcp#readme","keywords":["mcp","model-context-protocol","aruct","3d","building","editor","ai"],"repository":{"type":"git","url":"git+https://github.com/aruct/editor.git","directory":"packages/mcp"},"description":"Model Context Protocol server for Aruct 3D editor","maintainers":[{"name":"tucnow","email":"tucnowsolutions@gmail.com"}],"readme":"# @aruct/mcp\n\nModel Context Protocol server for the Aruct 3D editor. Drives the\n`@aruct/core` scene graph from any MCP-compatible AI host.\n\nThe server runs headlessly in Bun with no browser, WebGPU, React, or external\ndatabase service. It exposes the same scene mutations used by the editor UI\n(create walls, place items, cut openings, undo, etc.) as MCP tools, resources,\nand prompts.\n\n## Install\n\n```bash\nbun add @aruct/mcp\n```\n\n`@aruct/core` is a peer dependency; Bun workspaces resolve it automatically.\nThe MCP CLI is intended to run with Bun. When the storage package is consumed by\nthe Next.js editor server, it opens the same local database through Node's\nbuilt-in SQLite driver.\n\n## Quick start\n\nLaunch the server over stdio in one line:\n\n```bash\nbunx aruct-mcp\n```\n\nLoad an initial scene from disk:\n\n```bash\naruct-mcp --stdio --scene ./my-scene.json\n```\n\nExpose it over loopback HTTP:\n\n```bash\naruct-mcp --http --port 8787\n```\n\nBinding a non-loopback host requires a bearer token:\n\n```bash\nARUCT_MCP_HTTP_TOKEN=\"$(openssl rand -hex 32)\" \\\n  aruct-mcp --http --host 0.0.0.0 --port 8787 --cors-origin https://editor.example\n```\n\n## Local scene storage\n\nScenes saved through MCP are stored in a local SQLite database:\n\n```text\n~/.aruct/data/aruct.db\n```\n\nSet `ARUCT_DATA_DIR` when you want the MCP server and the running editor to\nshare a different directory, or `ARUCT_DB_PATH` when you need an exact database\nfile path. The store uses WAL mode and transactional version checks so separate\nlocal processes can save and open the same scene database.\n\nDuring workspace development, run both sides with the same data directory:\n\n```bash\n# Terminal 1: run the editor\nARUCT_DATA_DIR=\"$HOME/.aruct/data\" bun run dev\n\n# Terminal 2 or an MCP host: run the server\nARUCT_DATA_DIR=\"$HOME/.aruct/data\" bun packages/mcp/dist/bin/aruct-mcp.js\n```\n\n## Live editor updates\n\nWhen the editor and MCP server share the same `ARUCT_DATA_DIR`, MCP mutations\nagainst a loaded saved scene are persisted to SQLite and recorded in a local\n`scene_events` stream. The editor page subscribes to that stream at\n`/api/scenes/:id/events` with server-sent events, so an open browser tab can\napply scene graph snapshots as the agent edits the scene.\n\nThe flow is intentionally local and lightweight:\n\n1. Open or create a scene in the editor so it is saved in the local database.\n2. Load that scene through MCP with `load_scene`.\n3. Run MCP mutation tools such as `create_room`, `add_door`, `furnish_room`,\n   `create_wall`, `place_item`, or `set_zone`.\n\nEach mutation version-checks the saved scene before writing. If the browser or\nanother MCP process saved a newer version first, the MCP tool returns\n`live_sync_version_conflict`; reload the scene with `load_scene` before\ncontinuing.\n\n## Claude Desktop config\n\nEdit `~/Library/Application Support/Claude/claude_desktop_config.json`\n(macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"aruct\": {\n      \"command\": \"bunx\",\n      \"args\": [\"aruct-mcp\"],\n      \"env\": {\n        \"ARUCT_DATA_DIR\": \"/Users/you/.aruct/data\"\n      }\n    }\n  }\n}\n```\n\nIf `bunx` is not on your PATH, point `command` at the absolute path to `bun`\nand pass the built `dist/bin/aruct-mcp.js` file as the first arg.\n\n## Claude Code config\n\nVia the CLI:\n\n```bash\nclaude mcp add aruct bunx aruct-mcp\n```\n\nOr add to `.mcp.json` at the repo root:\n\n```json\n{\n  \"mcpServers\": {\n    \"aruct\": {\n      \"command\": \"bunx\",\n      \"args\": [\"aruct-mcp\"],\n      \"env\": {\n        \"ARUCT_DATA_DIR\": \"/Users/you/.aruct/data\"\n      }\n    }\n  }\n}\n```\n\nFor local workspace testing before publish, build first and point Claude Code at\nthe built binary:\n\n```json\n{\n  \"mcpServers\": {\n    \"aruct\": {\n      \"command\": \"bun\",\n      \"args\": [\"/absolute/path/to/editor/packages/mcp/dist/bin/aruct-mcp.js\"],\n      \"env\": {\n        \"ARUCT_DATA_DIR\": \"/Users/you/.aruct/data\"\n      }\n    }\n  }\n}\n```\n\n## Codex CLI config\n\nVia the CLI:\n\n```bash\ncodex mcp add aruct --env ARUCT_DATA_DIR=\"$HOME/.aruct/data\" -- bunx aruct-mcp\n```\n\nFor local workspace testing before publish:\n\n```bash\nbun run --cwd packages/mcp build\ncodex mcp add aruct-dev \\\n  --env ARUCT_DATA_DIR=\"$HOME/.aruct/data\" \\\n  -- bun \"$PWD/packages/mcp/dist/bin/aruct-mcp.js\"\n```\n\nThis writes an entry like this to `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.aruct-dev]\ncommand = \"bun\"\nargs = [\"/absolute/path/to/editor/packages/mcp/dist/bin/aruct-mcp.js\"]\n\n[mcp_servers.aruct-dev.env]\nARUCT_DATA_DIR = \"/Users/you/.aruct/data\"\n```\n\n## Cursor config\n\nIn Cursor settings (`settings.json`):\n\n```json\n{\n  \"mcp.servers\": {\n    \"aruct\": {\n      \"command\": \"bunx\",\n      \"args\": [\"aruct-mcp\"],\n      \"env\": {\n        \"ARUCT_DATA_DIR\": \"/Users/you/.aruct/data\"\n      }\n    }\n  }\n}\n```\n\n## Programmatic use\n\nEmbed the server in your own Bun process using the in-memory transport. The\nexample below runs a full client/server pair inside a single script — useful\nfor agent frameworks and tests.\n\n```ts\nimport { createAructMcpServer, SceneBridge } from '@aruct/mcp'\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'\n\nconst bridge = new SceneBridge()\nbridge.loadDefault()\nconst server = createAructMcpServer({ bridge })\n\nconst [srvT, cliT] = InMemoryTransport.createLinkedPair()\nconst client = new Client({ name: 'my-agent', version: '0.1.0' })\nawait Promise.all([server.connect(srvT), client.connect(cliT)])\n\nconst tools = await client.listTools()\nconsole.log('available tools:', tools.tools.map((t) => t.name))\n\nconst scene = await client.callTool({ name: 'get_scene', arguments: {} })\nconsole.log(scene)\n```\n\nSee [`examples/embed-in-agent.ts`](./examples/embed-in-agent.ts) for a\ncompilable version.\n\n## Coordinate conventions\n\nAruct is a **right-handed** scene where **X and Z form the ground plane and Y\nis up**. Lengths are in **metres**; rotations are **radians**, stored as Euler\n`[x, y, z]` tuples.\n\n**Plan → world.** Every 2-D point you pass is a level/building-local\nground-plane coordinate\n`[x, z]` — this includes `wall.start` / `wall.end` and the `polygon` / `holes`\narrays of `slab`, `zone`, and `ceiling`. With the default identity building\ntransform, it appears in world space as:\n\n```\n[x, z]  →  (x, y, z)      // the 2nd component is world Z (depth), not \"up\"\n```\n\nThere is no sign flip in the stored convention: tooling consumes the second\ncomponent as world Z directly. The vertical `y` starts from the owning level's\nstacked height as computed by the level system from accumulated level heights,\nplus the element's own height; slabs additionally carry an absolute\n`elevation`.\n\n**Heads-up when you compute coordinates outside the editor.** Aruct's\nviewports apply their own rotations on top of the world axes: the 2-D plan\npanel rotates its content by the user's view rotation (north-aligned = 0°,\n`FLOORPLAN_VIEW_ROTATION_DEG` baseline, north = world −Z), and\nthe 3-D \"top-down\" snap preserves the camera's current azimuth, so when invoked\nfrom the iso default position, world and screen axes are offset by ~45° until\nyou orbit to an axis-aligned view. So a layout authored as if\n*\"Y = north, viewed top-down\"* — common in land surveys, north-up site plans,\nand 2-D plotting libraries — will arrive **rotated** relative to its source\nwhen viewed in Aruct (and possibly further reflected, depending on which\nviewport and camera state you're in). The editor's own 2-D and 3-D tools are\ninternally consistent with their stored coordinates, so this only affects\ngeometry authored programmatically. To verify orientation before trusting\nexternally-computed coordinates, place a scaled guide image at known anchor\npoints and check alignment; apply whatever rotation (or reflection) your\nauthoring side needs to match.\n\nA worked demonstration of all of this — axis-aligned baseline, the rotated\n30° example below, and a paired \"page-intent vs world-result\" L for the\nexternal-coordinate gotcha — lives in\n[`examples/coordinate-conventions-demo.md`](./examples/coordinate-conventions-demo.md)\nand [`examples/coordinate-conventions-demo.json`](./examples/coordinate-conventions-demo.json).\nLoad the JSON with\n`aruct-mcp --stdio --scene examples/coordinate-conventions-demo.json`.\n\n**Example — a 6 × 4 m slab rotated 30° about its first corner** (coordinates\nrounded to 3 dp; sides ≈ 6 m / 4 m; not axis-aligned, so the mapping is\nactually exercised):\n\n```json\n{\n  \"op\": \"create\",\n  \"parentId\": \"<levelId>\",\n  \"node\": {\n    \"type\": \"slab\",\n    \"elevation\": 0.0,\n    \"polygon\": [[0, 0], [5.196, 3.0], [3.196, 6.464], [-2.0, 3.464]]\n  }\n}\n```\n\nThis lands flat on the ground (Y = 0), about 6 m along a heading 30° off the +X\naxis and 4 m along its perpendicular — i.e. occupying world (x, z) directly.\n\nOne separate gotcha: wall-attached coordinates are wall-local, not plan\ncoordinates. Stored door/window `position[0]`, and `place_item` `position[0]`\nwhen the target is a wall, are metres along the wall; wall-attached rotations\nare wall-local too.\n\n## Tools\n\nAll tools validate their inputs and outputs with Zod. Mutation tools are\ncaptured by Zundo's temporal middleware as a single undoable step.\n\n| Name | Purpose | Key input | Output |\n| --- | --- | --- | --- |\n| `get_scene` | Return the full scene graph. | — | `{ nodes, rootNodeIds, collections }` |\n| `get_node` | Fetch a node by id. | `{ id }` | the node, or `InvalidParams` if not found |\n| `describe_node` | Node summary with ancestry, children count and properties. | `{ id }` | `{ id, type, parentId, ancestry[], childrenCount, properties, description }` |\n| `find_nodes` | Filter nodes by type / parent / zone / level. | `{ type?, parentId?, zoneId?, levelId? }` | `{ nodes: AnyNode[] }` |\n| `list_levels` | List levels with ids, floor indices, parent ids and child counts. | — | `{ activeSceneId, levels[] }` |\n| `get_level_summary` | Compact summary of one level with counts, wall/opening lists, zones, slabs, ceilings and items. | `{ levelId? }` | `{ levelId, counts, walls, zones, items, slabs, ceilings }` |\n| `get_walls` | Walls on a level with length and child doors/windows. | `{ levelId? }` | `{ levelId, walls[] }` |\n| `get_zones` | Room/zone polygons with approximate areas and bounds. | `{ levelId? }` | `{ levelId, zones[] }` |\n| `measure` | Distance between two nodes; area when applicable. | `{ fromId, toId }` | `{ distanceMeters, areaSqMeters?, units: 'meters' }` |\n| `search_assets` | Search the built-in MCP item catalog. | `{ query, category? }` | `{ results, total }` |\n| `create_story_shell` | Create one level-owned story shell from a footprint: perimeter walls plus optional slab and ceiling. Use once per story. | `{ levelId, footprint, wallHeight?, wallThickness?, createSlab?, createCeiling? }` | `{ wallIds, slabId, ceilingId, createdIds }` |\n| `create_stair_between_levels` | Create a straight stair and one rectangular manual opening in the destination slab/source ceiling, with auto-opening disabled. | `{ fromLevelId, toLevelId, position, width?, runLength?, totalRise? }` | `{ stairId, stairSegmentId, openingPolygon }` |\n| `create_roof` | Create a roof container and one roof segment. By default creates a dedicated roof level above the reference occupied level for solo/exploded views. | `{ levelId, width, depth, roofType?, roofHeight?, roofLevelId?, useDedicatedRoofLevel? }` | `{ roofLevelId, createdRoofLevelId, roofId, roofSegmentId }` |\n| `create_room` | Create a zone, slab, ceiling, and walls from a polygon. | `{ levelId, name, polygon, color?, wallHeight?, wallThickness? }` | `{ zoneId, slabId, ceilingId, wallIds, areaSqMeters }` |\n| `add_door` | Add a door to a wall using parametric placement. | `{ wallId, t, width?, height?, hingesSide?, swingDirection? }` | `{ doorId, localX }` |\n| `add_window` | Add a window to a wall using parametric placement and sill height. | `{ wallId, t, width?, height?, sillHeight? }` | `{ windowId, localX, sillHeight }` |\n| `furnish_room` | Place realistic furniture for a room type inside a polygon. | `{ levelId, roomType, polygon, doorWallIndex? }` | `{ placed, itemIds, skipped }` |\n| `apply_patch` | Batched create/update/delete/move, validated and dry-run before commit. | `{ patches: Patch[] }` | `{ applied: number }` |\n| `create_level` | Add a new level to a building. | `{ buildingId, elevation, height, label? }` | `{ levelId }` |\n| `create_wall` | Add a wall to a level. | `{ levelId, start, end, thickness?, height? }` | `{ wallId }` |\n| `place_item` | Place a catalog item on a level/slab/zone, ceiling, wall, or site. Slab/zone targets resolve to the parent level so floor items render and validate. | `{ catalogItemId, targetNodeId, position, rotation? }` | `{ itemId, status }` |\n| `cut_opening` | Cut a door or window opening into a wall. `position` is 0..1 along the wall and is stored as wall-local meters. | `{ wallId, type: 'door' \\| 'window', position, width, height }` | `{ openingId }` |\n| `set_zone` | Create a zone/room polygon on a level. | `{ levelId, polygon, label, properties? }` | `{ zoneId }` |\n| `duplicate_level` | Clone a level and all of its descendants. | `{ levelId }` | `{ newLevelId, newNodeIds[] }` |\n| `delete_node` | Delete a node; cascades when `cascade: true`. | `{ id, cascade? }` | `{ deletedIds: [] }` |\n| `undo` | Step back through temporal history. | `{ steps? }` | `{ undone: number }` |\n| `redo` | Step forward through temporal history. | `{ steps? }` | `{ redone: number }` |\n| `export_json` | Serialize the scene graph as JSON. | `{ pretty? }` | `{ json: string }` |\n| `export_glb` | Stubbed: GLB export requires the browser renderer. | — | throws `not_implemented` |\n| `validate_scene` | Zod-validate every node and parent-child integrity. | — | `{ valid, errors: { nodeId, path, message }[] }` |\n| `verify_scene` | High-level layout check with validation status, per-level counts, empty levels and practical issues. | — | `{ valid, levels[], issues, hasIssues }` |\n| `check_collisions` | Find overlapping items and out-of-bounds placements. | `{ levelId? }` | `{ collisions: { aId, bId, kind }[] }` |\n| `analyze_floorplan_image` | Vision tool: extract walls, rooms, and approximate dimensions from a floorplan image. | `{ image, scaleHint? }` | `{ walls, rooms, approximateDimensions, confidence }` |\n| `analyze_room_photo` | Vision tool: extract approximate dimensions and fixtures from a room photo. | `{ image }` | `{ approximateDimensions, identifiedFixtures, identifiedWindows }` |\n\nThe vision tools require the MCP host to support the sampling capability\n(`createMessage`). Hosts that don't will see a structured\n`sampling_unavailable` error.\n\n## Resources\n\n| URI | MIME | Purpose |\n| --- | --- | --- |\n| `aruct://scene/current` | `application/json` | Full `{ nodes, rootNodeIds, collections }` snapshot. |\n| `aruct://scene/current/summary` | `text/markdown` | Human-readable summary with node counts, bounding box, and level areas. |\n| `aruct://agent/guide` | `text/markdown` | MCP-first construction workflow, scene invariants, and tool preferences for agents. |\n| `aruct://catalog/items` | `application/json` | Dependency-free built-in catalog subset for common residential furniture and fixtures. |\n| `aruct://constraints/{levelId}` | `application/json` | Slab footprints and wall polygons for the given level — useful as planner context. |\n\n## Prompts\n\n| Name | Args | Purpose |\n| --- | --- | --- |\n| `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. |\n| `iterate_on_feedback` | `{ feedback: string }` | Minimal-diff instructions: examine the current scene, then propose the smallest patch set that satisfies the feedback. |\n| `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. |\n\n## Limitations\n\n- `export_glb` returns `not_implemented`. GLB export depends on the Three.js\n  renderer and isn't reachable headlessly without a large additional effort.\n- Vision tools require MCP host sampling support. Claude Desktop supports\n  this; some MCP clients don't.\n- The built-in MCP catalog is intentionally small. Host applications can expose\n  their own richer catalog through additional tools/resources without requiring\n  the MCP package to depend on the editor UI bundle.\n- Systems (wall mitering, slab triangulation, CSG cutouts, roof / stair\n  generation) run inside React hooks in the editor. Headless mode doesn't\n  regenerate derived geometry — but all node data remains fully manipulable.\n  Consumers that need rendered geometry run `@aruct/viewer` in a browser\n  host.\n- Core's `loadAssetUrl` / `saveAsset` are browser-only; items that reference\n  `asset://<id>` URLs aren't resolvable in Node. Supply absolute URLs or\n  `data:` URLs for item assets if you need them usable outside the browser.\n- `dirtyNodes` accumulates in headless mode because no renderer consumes it.\n  Call `bridge.flushDirty()` if observability matters to your consumer.\n\n## Development\n\n```bash\nbun install\nbun run --cwd packages/mcp build\nbun test\n```\n\nSmoke-test the stdio binary end-to-end:\n\n```bash\nbun run --cwd packages/mcp smoke\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}