@pascal-app/mcp 1.0.1 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +47 -26
  2. package/dist/bin/pascal-mcp.js +8 -2
  3. package/dist/bridge/scene-bridge.d.ts +41 -6
  4. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  5. package/dist/bridge/scene-bridge.js +230 -45
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/operations/scene-operations.d.ts +23 -3
  10. package/dist/operations/scene-operations.d.ts.map +1 -1
  11. package/dist/operations/scene-operations.js +31 -1
  12. package/dist/prompts/from-brief.js +1 -1
  13. package/dist/prompts/iterate-on-feedback.js +1 -1
  14. package/dist/prompts/scene-guidance.js +3 -3
  15. package/dist/resources/agent-guide.d.ts.map +1 -1
  16. package/dist/resources/agent-guide.js +22 -1
  17. package/dist/resources/catalog-items.d.ts +4 -5
  18. package/dist/resources/catalog-items.d.ts.map +1 -1
  19. package/dist/resources/catalog-items.js +7 -9
  20. package/dist/resources/design-schema.d.ts +9 -0
  21. package/dist/resources/design-schema.d.ts.map +1 -0
  22. package/dist/resources/design-schema.js +21 -0
  23. package/dist/resources/index.d.ts +3 -1
  24. package/dist/resources/index.d.ts.map +1 -1
  25. package/dist/resources/index.js +5 -2
  26. package/dist/server.d.ts +19 -0
  27. package/dist/server.d.ts.map +1 -1
  28. package/dist/server.js +14 -6
  29. package/dist/storage/disk-artifact-store.d.ts +9 -0
  30. package/dist/storage/disk-artifact-store.d.ts.map +1 -0
  31. package/dist/storage/disk-artifact-store.js +46 -0
  32. package/dist/storage/index.d.ts +1 -0
  33. package/dist/storage/index.d.ts.map +1 -1
  34. package/dist/storage/index.js +1 -0
  35. package/dist/storage/sqlite-scene-store.d.ts +2 -2
  36. package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
  37. package/dist/storage/sqlite-scene-store.js +4 -3
  38. package/dist/storage/types.d.ts +32 -3
  39. package/dist/storage/types.d.ts.map +1 -1
  40. package/dist/storage/types.js +12 -0
  41. package/dist/tools/add-column.d.ts +5 -0
  42. package/dist/tools/add-column.d.ts.map +1 -0
  43. package/dist/tools/add-column.js +58 -0
  44. package/dist/tools/add-object.d.ts +60 -0
  45. package/dist/tools/add-object.d.ts.map +1 -0
  46. package/dist/tools/add-object.js +156 -0
  47. package/dist/tools/apply-patch.d.ts +3 -0
  48. package/dist/tools/apply-patch.d.ts.map +1 -1
  49. package/dist/tools/apply-patch.js +28 -2
  50. package/dist/tools/asset-catalog.d.ts +9 -5
  51. package/dist/tools/asset-catalog.d.ts.map +1 -1
  52. package/dist/tools/asset-catalog.js +10 -12
  53. package/dist/tools/check-collisions.d.ts +1 -1
  54. package/dist/tools/check-collisions.d.ts.map +1 -1
  55. package/dist/tools/check-collisions.js +1 -2
  56. package/dist/tools/clear-scene.d.ts +9 -0
  57. package/dist/tools/clear-scene.d.ts.map +1 -0
  58. package/dist/tools/clear-scene.js +59 -0
  59. package/dist/tools/collections.d.ts +4 -0
  60. package/dist/tools/collections.d.ts.map +1 -0
  61. package/dist/tools/collections.js +52 -0
  62. package/dist/tools/construction-tools.d.ts +8 -41
  63. package/dist/tools/construction-tools.d.ts.map +1 -1
  64. package/dist/tools/construction-tools.js +120 -230
  65. package/dist/tools/create-unit.d.ts +2 -0
  66. package/dist/tools/create-unit.d.ts.map +1 -1
  67. package/dist/tools/describe-node.d.ts.map +1 -1
  68. package/dist/tools/describe-node.js +8 -6
  69. package/dist/tools/errors.d.ts +11 -0
  70. package/dist/tools/errors.d.ts.map +1 -1
  71. package/dist/tools/errors.js +10 -0
  72. package/dist/tools/find-nodes.d.ts +3 -19
  73. package/dist/tools/find-nodes.d.ts.map +1 -1
  74. package/dist/tools/find-nodes.js +150 -53
  75. package/dist/tools/geometry-script-worker.d.ts +2 -0
  76. package/dist/tools/geometry-script-worker.d.ts.map +1 -0
  77. package/dist/tools/geometry-script-worker.js +21 -0
  78. package/dist/tools/get-scene.d.ts +1 -0
  79. package/dist/tools/get-scene.d.ts.map +1 -1
  80. package/dist/tools/get-scene.js +3 -1
  81. package/dist/tools/honest-patch-guard.d.ts +2 -0
  82. package/dist/tools/honest-patch-guard.d.ts.map +1 -0
  83. package/dist/tools/honest-patch-guard.js +32 -0
  84. package/dist/tools/hosted-services.d.ts +4 -0
  85. package/dist/tools/hosted-services.d.ts.map +1 -0
  86. package/dist/tools/hosted-services.js +30 -0
  87. package/dist/tools/index.d.ts +10 -1
  88. package/dist/tools/index.d.ts.map +1 -1
  89. package/dist/tools/index.js +17 -26
  90. package/dist/tools/live-sync.d.ts +24 -5
  91. package/dist/tools/live-sync.d.ts.map +1 -1
  92. package/dist/tools/live-sync.js +47 -26
  93. package/dist/tools/local-geometry-scripts.d.ts +8 -0
  94. package/dist/tools/local-geometry-scripts.d.ts.map +1 -0
  95. package/dist/tools/local-geometry-scripts.js +67 -0
  96. package/dist/tools/measure.d.ts +6 -0
  97. package/dist/tools/measure.d.ts.map +1 -1
  98. package/dist/tools/measure.js +33 -65
  99. package/dist/tools/measurement.d.ts +1 -36
  100. package/dist/tools/measurement.d.ts.map +1 -1
  101. package/dist/tools/measurement.js +2 -94
  102. package/dist/tools/node-world-point.d.ts +37 -0
  103. package/dist/tools/node-world-point.d.ts.map +1 -0
  104. package/dist/tools/node-world-point.js +457 -0
  105. package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
  106. package/dist/tools/normalize-schema-dialect.js +20 -0
  107. package/dist/tools/patch-guards.d.ts +58 -0
  108. package/dist/tools/patch-guards.d.ts.map +1 -0
  109. package/dist/tools/patch-guards.js +253 -0
  110. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  111. package/dist/tools/photo-to-scene/photo-to-scene.js +3 -2
  112. package/dist/tools/place-design.d.ts +34 -0
  113. package/dist/tools/place-design.d.ts.map +1 -0
  114. package/dist/tools/place-design.js +91 -0
  115. package/dist/tools/redo.d.ts +2 -0
  116. package/dist/tools/redo.d.ts.map +1 -1
  117. package/dist/tools/redo.js +2 -2
  118. package/dist/tools/room-tools.d.ts +50 -113
  119. package/dist/tools/room-tools.d.ts.map +1 -1
  120. package/dist/tools/room-tools.js +129 -565
  121. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
  122. package/dist/tools/scene-lifecycle/create-project.js +26 -1
  123. package/dist/tools/scene-lifecycle/delete-scene.d.ts +2 -0
  124. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
  125. package/dist/tools/scene-lifecycle/delete-scene.js +4 -3
  126. package/dist/tools/scene-lifecycle/save-scene.d.ts +1 -0
  127. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
  128. package/dist/tools/scene-lifecycle/save-scene.js +46 -6
  129. package/dist/tools/scene-lifecycle/test-utils.d.ts +2 -2
  130. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
  131. package/dist/tools/scene-lifecycle/test-utils.js +2 -1
  132. package/dist/tools/set-unit-members.d.ts +2 -0
  133. package/dist/tools/set-unit-members.d.ts.map +1 -1
  134. package/dist/tools/set-zone.d.ts +2 -0
  135. package/dist/tools/set-zone.d.ts.map +1 -1
  136. package/dist/tools/set-zone.js +2 -2
  137. package/dist/tools/shared-tools.d.ts +30 -0
  138. package/dist/tools/shared-tools.d.ts.map +1 -0
  139. package/dist/tools/shared-tools.js +361 -0
  140. package/dist/tools/structure-tools.d.ts +57 -0
  141. package/dist/tools/structure-tools.d.ts.map +1 -0
  142. package/dist/tools/structure-tools.js +57 -0
  143. package/dist/tools/undo.d.ts +2 -0
  144. package/dist/tools/undo.d.ts.map +1 -1
  145. package/dist/tools/undo.js +2 -2
  146. package/dist/tools/validate-design.d.ts +136 -0
  147. package/dist/tools/validate-design.d.ts.map +1 -0
  148. package/dist/tools/validate-design.js +28 -0
  149. package/dist/tools/validate-scene.d.ts +5 -0
  150. package/dist/tools/validate-scene.d.ts.map +1 -1
  151. package/dist/tools/validate-scene.js +8 -6
  152. package/dist/tools/variants/mutations.js +1 -1
  153. package/dist/tools/view-scene.d.ts +38 -0
  154. package/dist/tools/view-scene.d.ts.map +1 -0
  155. package/dist/tools/view-scene.js +63 -0
  156. package/dist/version.d.ts +5 -0
  157. package/dist/version.d.ts.map +1 -1
  158. package/dist/version.js +17 -3
  159. package/examples/embed-in-agent.ts +1 -1
  160. package/examples/generate-apartment.md +9 -11
  161. package/examples/photo-to-scene.md +1 -1
  162. package/examples/renovate-from-photos.md +3 -4
  163. package/package.json +12 -6
  164. package/dist/tools/create-level.d.ts +0 -21
  165. package/dist/tools/create-level.d.ts.map +0 -1
  166. package/dist/tools/create-level.js +0 -63
  167. package/dist/tools/create-wall.d.ts +0 -22
  168. package/dist/tools/create-wall.d.ts.map +0 -1
  169. package/dist/tools/create-wall.js +0 -57
  170. package/dist/tools/cut-opening.d.ts +0 -25
  171. package/dist/tools/cut-opening.d.ts.map +0 -1
  172. package/dist/tools/cut-opening.js +0 -61
  173. package/dist/tools/delete-node.d.ts +0 -19
  174. package/dist/tools/delete-node.d.ts.map +0 -1
  175. package/dist/tools/delete-node.js +0 -40
  176. package/dist/tools/door-clearance.d.ts +0 -135
  177. package/dist/tools/door-clearance.d.ts.map +0 -1
  178. package/dist/tools/door-clearance.js +0 -334
  179. package/dist/tools/duplicate-level.d.ts +0 -19
  180. package/dist/tools/duplicate-level.d.ts.map +0 -1
  181. package/dist/tools/duplicate-level.js +0 -60
  182. package/dist/tools/geometry.d.ts +0 -24
  183. package/dist/tools/geometry.d.ts.map +0 -1
  184. package/dist/tools/geometry.js +0 -94
  185. package/dist/tools/get-node.d.ts +0 -11
  186. package/dist/tools/get-node.d.ts.map +0 -1
  187. package/dist/tools/get-node.js +0 -29
  188. package/dist/tools/layout-clearance.d.ts +0 -105
  189. package/dist/tools/layout-clearance.d.ts.map +0 -1
  190. package/dist/tools/layout-clearance.js +0 -190
  191. package/dist/tools/place-item.d.ts +0 -22
  192. package/dist/tools/place-item.d.ts.map +0 -1
  193. package/dist/tools/place-item.js +0 -89
  194. package/dist/tools/scene-query.d.ts +0 -62
  195. package/dist/tools/scene-query.d.ts.map +0 -1
  196. package/dist/tools/scene-query.js +0 -670
@@ -1,84 +1,179 @@
1
+ import { measurementCentroid, nodeRegistry } from '@pascal-app/core';
2
+ import { nodeLevelFrame, transformPoint } from '@pascal-app/core/procedural-items';
3
+ import { AnyNode, nodeKindOf } from '@pascal-app/core/schema';
1
4
  import { pointInPolygon } from '@pascal-app/core/spatial-grid';
2
5
  import { z } from 'zod';
3
6
  import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
7
+ import { ErrorCode, throwMcpError } from './errors.js';
4
8
  import { NodeIdSchema } from './schemas.js';
5
- const ALL_NODE_TYPES = [
6
- 'site',
7
- 'building',
8
- 'level',
9
- 'wall',
10
- 'fence',
11
- 'zone',
12
- 'slab',
13
- 'ceiling',
14
- 'roof',
15
- 'roof-segment',
16
- 'stair',
17
- 'stair-segment',
18
- 'item',
19
- 'door',
20
- 'window',
21
- 'scan',
22
- 'guide',
23
- ];
9
+ const CORE_NODE_KINDS = AnyNode.options.map(nodeKindOf);
24
10
  export const findNodesInput = {
25
- type: z.enum(ALL_NODE_TYPES).optional(),
11
+ type: z
12
+ .string()
13
+ .min(1)
14
+ .optional()
15
+ .describe(`Node kind: a core kind (${CORE_NODE_KINDS.join(', ')}) or a plugin kind such as "fixture:bench".`),
26
16
  parentId: NodeIdSchema.optional(),
27
17
  levelId: NodeIdSchema.optional(),
28
18
  zoneId: NodeIdSchema.optional(),
19
+ sourceId: z
20
+ .string()
21
+ .min(1)
22
+ .optional()
23
+ .describe('Exact match on any import source id of the node: provenance.refs[].id or legacy metadata.sourceIds. Characters outside printable ASCII also match their percent-encoded form.'),
24
+ sourceIdPrefix: z
25
+ .string()
26
+ .min(1)
27
+ .optional()
28
+ .describe('Prefix match on any import source id of the node (provenance.refs[].id or legacy metadata.sourceIds).'),
29
29
  };
30
30
  export const findNodesOutput = {
31
31
  nodes: z.array(z.record(z.string(), z.unknown())),
32
32
  };
33
- /** Compute a representative 2D point (x, z) for zone-filtering. */
34
- function getPointForZoneFilter(node) {
35
- if (node.type === 'wall' || node.type === 'fence') {
36
- const [x1, z1] = node.start;
37
- const [x2, z2] = node.end;
33
+ /**
34
+ * A source id as importers must write it for typed provenance: printable
35
+ * ASCII kept, every other character percent-encoded as UTF-8 bytes.
36
+ */
37
+ function encodeSourceId(id) {
38
+ let out = '';
39
+ for (const char of id) {
40
+ if (/^[\x20-\x7E]$/.test(char)) {
41
+ out += char;
42
+ continue;
43
+ }
44
+ for (const byte of new TextEncoder().encode(char)) {
45
+ out += `%${byte.toString(16).toUpperCase().padStart(2, '0')}`;
46
+ }
47
+ }
48
+ return out;
49
+ }
50
+ /** A node's import source ids: typed `provenance.refs[].id`, then legacy `metadata.sourceIds`. */
51
+ function nodeSourceIds(node) {
52
+ const typed = (node.provenance?.refs ?? []).map((ref) => ref.id);
53
+ const legacy = node.metadata?.sourceIds;
54
+ return Array.isArray(legacy)
55
+ ? [...typed, ...legacy.filter((id) => typeof id === 'string')]
56
+ : typed;
57
+ }
58
+ function centre(points) {
59
+ if (points.length === 0)
60
+ return null;
61
+ const area = measurementCentroid(points.map(([x, z]) => [x, 0, z]));
62
+ if (area)
63
+ return [area[0], area[2]];
64
+ let cx = 0;
65
+ let cz = 0;
66
+ for (const [x, z] of points) {
67
+ cx += x;
68
+ cz += z;
69
+ }
70
+ return [cx / points.length, cz / points.length];
71
+ }
72
+ function boundsCentre3(points) {
73
+ if (points.length === 0)
74
+ return null;
75
+ const centre = [0, 0, 0];
76
+ for (let axis = 0; axis < 3; axis++) {
77
+ const values = points.map((p) => p[axis]);
78
+ centre[axis] = (Math.min(...values) + Math.max(...values)) / 2;
79
+ }
80
+ return centre;
81
+ }
82
+ /**
83
+ * The node's plan point in its level's frame, for zone filtering: a polygon's
84
+ * area centroid, a segment's midpoint, a path's centre, or the origin of a
85
+ * positioned node resolved through its hosts by core's `nodeLevelFrame` (a
86
+ * window in its wall, a module in its cabinet); blocks and imported meshes
87
+ * use their vertex-bounds centre, transformed in 3D before projecting.
88
+ * Frames are plan-only (no slab support or floor lift, which only move
89
+ * heights) and `frames` is shared across one query, so each host resolves once.
90
+ */
91
+ function levelPlanPoint(node, nodes, frames) {
92
+ const n = node;
93
+ if (Array.isArray(n.polygon))
94
+ return centre(n.polygon);
95
+ if (Array.isArray(n.start) && Array.isArray(n.end)) {
96
+ const [x1, z1] = n.start;
97
+ const [x2, z2] = n.end;
38
98
  return [(x1 + x2) / 2, (z1 + z2) / 2];
39
99
  }
40
- if (node.type === 'item' ||
41
- node.type === 'door' ||
42
- node.type === 'window' ||
43
- node.type === 'building' ||
44
- node.type === 'stair' ||
45
- node.type === 'roof') {
46
- const [x, , z] = node.position;
47
- return [x, z];
100
+ if (Array.isArray(n.path)) {
101
+ const path = boundsCentre3(n.path);
102
+ return path ? [path[0], path[2]] : null;
48
103
  }
49
- if (node.type === 'slab' || node.type === 'ceiling' || node.type === 'zone') {
50
- const poly = node.polygon;
51
- if (!poly || poly.length === 0)
52
- return null;
53
- let cx = 0;
54
- let cz = 0;
55
- for (const [x, z] of poly) {
56
- cx += x;
57
- cz += z;
58
- }
59
- return [cx / poly.length, cz / poly.length];
104
+ if (!Array.isArray(n.position))
105
+ return null;
106
+ let frame;
107
+ try {
108
+ frame = nodeLevelFrame(node.id, nodes, undefined, { cache: frames, planOnly: true });
60
109
  }
61
- return null;
110
+ catch {
111
+ return null;
112
+ }
113
+ const local = node.type === 'block'
114
+ ? boundsCentre3(node.topology.vertices.map((v) => v.position))
115
+ : node.type === 'imported-mesh'
116
+ ? boundsCentre3(node.primitives.flatMap((primitive) => {
117
+ const out = [];
118
+ for (let i = 0; i + 2 < primitive.positions.length; i += 3) {
119
+ out.push([
120
+ primitive.positions[i],
121
+ primitive.positions[i + 1],
122
+ primitive.positions[i + 2],
123
+ ]);
124
+ }
125
+ return out;
126
+ }))
127
+ : null;
128
+ const [x, , z] = transformPoint(frame, local ?? [0, 0, 0]);
129
+ return [x, z];
130
+ }
131
+ /** Node kinds `type` accepts: core kinds, registered plugin kinds, and kinds in the scene. */
132
+ function knownNodeKinds(nodes) {
133
+ const kinds = new Set(CORE_NODE_KINDS);
134
+ for (const node of Object.values(nodes))
135
+ kinds.add(node.type);
136
+ return kinds;
62
137
  }
63
138
  export function registerFindNodes(server, bridge) {
64
139
  server.registerTool('find_nodes', {
65
140
  title: 'Find nodes',
66
- description: 'Find nodes matching any combination of type, parentId, levelId, or zoneId filters.',
141
+ description: 'Find nodes matching any combination of type, parentId, levelId, zoneId, sourceId or sourceIdPrefix filters. sourceId / sourceIdPrefix match the ids an importer recorded for the source elements: provenance.refs[].id, or legacy metadata.sourceIds.',
67
142
  inputSchema: findNodesInput,
68
143
  outputSchema: findNodesOutput,
69
144
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
70
145
  }, async (args) => {
71
- const { type, parentId, levelId, zoneId } = args;
146
+ const { type, parentId, levelId, zoneId, sourceId, sourceIdPrefix } = args;
72
147
  // Delegate type/parent/level filtering to the bridge.
73
148
  const baseFilter = {};
74
- if (type !== undefined)
149
+ if (type !== undefined) {
150
+ if (!(knownNodeKinds(bridge.getNodes()).has(type) || nodeRegistry.get(type))) {
151
+ throwMcpError(ErrorCode.InvalidParams, `unknown node type "${type}": not a core kind, a registered plugin kind or a kind in this scene`);
152
+ }
75
153
  baseFilter.type = type;
154
+ }
76
155
  if (parentId !== undefined)
77
156
  baseFilter.parentId = parentId;
78
157
  if (levelId !== undefined)
79
158
  baseFilter.levelId = levelId;
80
159
  let results = bridge.findNodes(baseFilter);
81
- // Zone-polygon filter: point-in-polygon on a representative 2D point.
160
+ if (sourceId !== undefined || sourceIdPrefix !== undefined) {
161
+ // Legacy ids may be stored raw, typed-provenance ones percent-encoded.
162
+ const exact = sourceId === undefined ? [] : [sourceId, encodeSourceId(sourceId)];
163
+ const prefixes = sourceIdPrefix === undefined ? [] : [sourceIdPrefix, encodeSourceId(sourceIdPrefix)];
164
+ results = results.filter((n) => {
165
+ const ids = nodeSourceIds(n);
166
+ if (sourceId !== undefined && !ids.some((id) => exact.includes(id)))
167
+ return false;
168
+ if (sourceIdPrefix !== undefined &&
169
+ !ids.some((id) => prefixes.some((prefix) => id.startsWith(prefix)))) {
170
+ return false;
171
+ }
172
+ return true;
173
+ });
174
+ }
175
+ // Zone filter: nodes on the zone's level whose level-frame plan point
176
+ // falls inside the zone polygon.
82
177
  if (zoneId) {
83
178
  const zone = bridge.getNode(zoneId);
84
179
  if (zone?.type !== 'zone') {
@@ -87,12 +182,14 @@ export function registerFindNodes(server, bridge) {
87
182
  results = [];
88
183
  }
89
184
  else {
90
- const poly = zone.polygon;
185
+ const nodes = bridge.getNodes();
186
+ const frames = new Map();
187
+ const zoneLevelId = bridge.resolveLevelId(zone.id);
91
188
  results = results.filter((n) => {
92
- const pt = getPointForZoneFilter(n);
93
- if (!pt)
189
+ if (bridge.resolveLevelId(n.id) !== zoneLevelId)
94
190
  return false;
95
- return pointInPolygon(pt[0], pt[1], poly);
191
+ const pt = levelPlanPoint(n, nodes, frames);
192
+ return pt !== null && pointInPolygon(pt[0], pt[1], zone.polygon);
96
193
  });
97
194
  }
98
195
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=geometry-script-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry-script-worker.d.ts","sourceRoot":"","sources":["../../src/tools/geometry-script-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import { Console } from 'node:console';
2
+ import { compileGeometryScript } from '@pascal-app/geometry-script/compile';
3
+ const stdout = process.stdout;
4
+ // stdout carries only the result; a script's own logging goes to stderr.
5
+ globalThis.console = new Console({ stdout: process.stderr, stderr: process.stderr });
6
+ const chunks = [];
7
+ for await (const chunk of process.stdin)
8
+ chunks.push(chunk);
9
+ const input = JSON.parse(Buffer.concat(chunks).toString('utf8'));
10
+ for (const name of ['fetch', 'WebSocket', 'EventSource', 'XMLHttpRequest', 'Request', 'Response']) {
11
+ Reflect.deleteProperty(globalThis, name);
12
+ }
13
+ for (const name of ['getBuiltinModule', 'binding', 'dlopen'])
14
+ Reflect.deleteProperty(process, name);
15
+ try {
16
+ const { glb, ...compiled } = await compileGeometryScript(input);
17
+ stdout.write(JSON.stringify({ ok: true, compiled, glb: Buffer.from(glb).toString('base64') }));
18
+ }
19
+ catch (error) {
20
+ stdout.write(JSON.stringify({ ok: false, error: error instanceof Error ? error.message : String(error) }));
21
+ }
@@ -6,6 +6,7 @@ export declare const getSceneOutput: {
6
6
  nodes: z.ZodRecord<z.ZodString, z.ZodUnknown>;
7
7
  rootNodeIds: z.ZodArray<z.ZodString>;
8
8
  collections: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
9
+ installedPlugins: z.ZodOptional<z.ZodArray<z.ZodString>>;
9
10
  };
10
11
  export declare function registerGetScene(server: McpServer, bridge: SceneOperations): void;
11
12
  //# sourceMappingURL=get-scene.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-scene.d.ts","sourceRoot":"","sources":["../../src/tools/get-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,eAAO,MAAM,aAAa,IAAK,CAAA;AAE/B,eAAO,MAAM,cAAc;;;;CAI1B,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwBjF"}
1
+ {"version":3,"file":"get-scene.d.ts","sourceRoot":"","sources":["../../src/tools/get-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,eAAO,MAAM,aAAa,IAAK,CAAA;AAE/B,eAAO,MAAM,cAAc;;;;;CAK1B,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAyBjF"}
@@ -5,11 +5,12 @@ export const getSceneOutput = {
5
5
  nodes: z.record(z.string(), z.unknown()),
6
6
  rootNodeIds: z.array(z.string()),
7
7
  collections: z.record(z.string(), z.unknown()).optional(),
8
+ installedPlugins: z.array(z.string()).optional(),
8
9
  };
9
10
  export function registerGetScene(server, bridge) {
10
11
  server.registerTool('get_scene', {
11
12
  title: 'Get scene',
12
- description: 'Returns the full scene graph: flat node dictionary, root node IDs, and collections.',
13
+ description: 'Returns the full scene graph: flat node dictionary, root node IDs, collections, and explicit installed plugin IDs when present.',
13
14
  inputSchema: getSceneInput,
14
15
  outputSchema: getSceneOutput,
15
16
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
@@ -19,6 +20,7 @@ export function registerGetScene(server, bridge) {
19
20
  nodes: scene.nodes,
20
21
  rootNodeIds: scene.rootNodeIds,
21
22
  collections: (scene.collections ?? {}),
23
+ ...(scene.installedPlugins ? { installedPlugins: scene.installedPlugins } : {}),
22
24
  };
23
25
  return {
24
26
  content: [{ type: 'text', text: JSON.stringify(payload) }],
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=honest-patch-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"honest-patch-guard.d.ts","sourceRoot":"","sources":["../../src/tools/honest-patch-guard.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { honestNodePatch } from '@pascal-app/core/agent-operations';
2
+ import { isAgentRefusal } from '@pascal-app/core/agent-tools';
3
+ import { PatchRefusedError, registerPatchGuard } from './patch-guards.js';
4
+ /**
5
+ * Each update checked to do what it says, in the batch's order (a node an earlier op created is
6
+ * the one updated): a field the node would drop, a material the library lacks or a field a set
7
+ * one hides is refused, naming it; `null` clears. The cleaned data is what is written.
8
+ */
9
+ function honestUpdates(patches, scene) {
10
+ const nodes = { ...scene };
11
+ for (const [index, patch] of patches.entries()) {
12
+ if (patch.op === 'create')
13
+ nodes[patch.node.id] = patch.node;
14
+ else if (patch.op === 'delete')
15
+ delete nodes[patch.id];
16
+ else if (patch.op === 'update') {
17
+ const current = nodes[patch.id];
18
+ if (!current)
19
+ continue;
20
+ try {
21
+ patch.data = honestNodePatch(current, patch.data);
22
+ }
23
+ catch (error) {
24
+ if (!isAgentRefusal(error))
25
+ throw error;
26
+ throw new PatchRefusedError(error.code, index, patch.id, error.message);
27
+ }
28
+ nodes[patch.id] = { ...current, ...patch.data };
29
+ }
30
+ }
31
+ }
32
+ registerPatchGuard({ name: 'honest-updates', order: 20, run: honestUpdates });
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type HostedServiceExecutor } from '@pascal-app/core/agent-operations';
3
+ export declare function registerHostedServiceTools(server: McpServer, execute: HostedServiceExecutor): void;
4
+ //# sourceMappingURL=hosted-services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-services.d.ts","sourceRoot":"","sources":["../../src/tools/hosted-services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,mCAAmC,CAAA;AAQ1C,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,qBAAqB,GAC7B,IAAI,CA8BN"}
@@ -0,0 +1,30 @@
1
+ import { executeHostedServiceTool, } from '@pascal-app/core/agent-operations';
2
+ import { HOSTED_SERVICE_TOOL_CONTRACTS, isAgentRefusal, refusalPayload, } from '@pascal-app/core/agent-tools';
3
+ import { z } from 'zod';
4
+ export function registerHostedServiceTools(server, execute) {
5
+ for (const contract of HOSTED_SERVICE_TOOL_CONTRACTS) {
6
+ server.registerTool(contract.name, {
7
+ title: contract.title,
8
+ description: contract.description,
9
+ inputSchema: z.object(contract.input).strict(),
10
+ annotations: contract.annotations,
11
+ }, async (input, extra) => {
12
+ try {
13
+ const result = await executeHostedServiceTool(contract.name, input, execute, extra.signal);
14
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
15
+ }
16
+ catch (error) {
17
+ const result = isAgentRefusal(error)
18
+ ? refusalPayload(error)
19
+ : {
20
+ error: 'Pascal could not complete this service request. Check its retained result before repeating paid work.',
21
+ code: 'service_error',
22
+ };
23
+ return {
24
+ isError: true,
25
+ content: [{ type: 'text', text: JSON.stringify(result) }],
26
+ };
27
+ }
28
+ });
29
+ }
30
+ }
@@ -1,5 +1,8 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { SceneOperations } from '../operations/index.js';
3
+ import { type GeometryScriptHost } from './add-object.js';
4
+ import type { AssetCatalog } from './asset-catalog.js';
5
+ import { type SceneViewHost } from './view-scene.js';
3
6
  /**
4
7
  * Register every non-vision MCP tool against the given server.
5
8
  * Vision tools (analyze_floorplan_image, analyze_room_photo) are registered
@@ -8,5 +11,11 @@ import type { SceneOperations } from '../operations/index.js';
8
11
  * Scene-lifecycle tools (save/load/list/delete/rename scene) are registered
9
12
  * when persistence operations are available.
10
13
  */
11
- export declare function registerTools(server: McpServer, operations: SceneOperations): void;
14
+ /** What the host lends the tools: its item library, script compiles, a view. */
15
+ export type ToolHosts = {
16
+ catalog?: AssetCatalog;
17
+ geometryScripts?: GeometryScriptHost;
18
+ sceneViews?: SceneViewHost;
19
+ };
20
+ export declare function registerTools(server: McpServer, operations: SceneOperations, { catalog, geometryScripts, sceneViews }?: ToolHosts): void;
12
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA+BpD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAgClF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,OAAO,EAAE,KAAK,kBAAkB,EAAwC,MAAM,cAAc,CAAA;AAE5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAyBnD,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAEpE;;;;;;;GAOG;AACH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,eAAe,CAAC,EAAE,kBAAkB,CAAA;IACpC,UAAU,CAAC,EAAE,aAAa,CAAA;CAC3B,CAAA;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,eAAe,EAC3B,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,GAAE,SAAc,GACvD,IAAI,CAgCN"}
@@ -1,65 +1,56 @@
1
+ import { registerAddColumn } from './add-column.js';
2
+ import { registerAddObject, registerGetSource } from './add-object.js';
1
3
  import { registerApplyPatch } from './apply-patch.js';
2
4
  import { registerCheckCollisions } from './check-collisions.js';
5
+ import { registerClearScene } from './clear-scene.js';
3
6
  import { registerConstructionTools } from './construction-tools.js';
4
- import { registerCreateLevel } from './create-level.js';
5
7
  import { registerCreateUnit } from './create-unit.js';
6
- import { registerCreateWall } from './create-wall.js';
7
- import { registerCutOpening } from './cut-opening.js';
8
- import { registerDeleteNode } from './delete-node.js';
9
8
  import { registerDescribeNode } from './describe-node.js';
10
- import { registerDuplicateLevel } from './duplicate-level.js';
11
9
  import { registerExportGlb } from './export-glb.js';
12
10
  import { registerExportJson } from './export-json.js';
13
11
  import { registerFindNodes } from './find-nodes.js';
14
- import { registerGetNode } from './get-node.js';
15
12
  import { registerGetScene } from './get-scene.js';
16
13
  import { registerListUnits } from './list-units.js';
17
14
  import { registerMeasure } from './measure.js';
18
15
  import { registerPhotoToSceneTool } from './photo-to-scene/index.js';
19
- import { registerPlaceItem } from './place-item.js';
16
+ import { registerPlaceDesign } from './place-design.js';
20
17
  import { registerRedo } from './redo.js';
21
18
  import { registerRoomTools } from './room-tools.js';
22
19
  import { registerSceneLifecycleTools } from './scene-lifecycle/index.js';
23
- import { registerSceneQueryTools } from './scene-query.js';
24
20
  import { registerSetUnitMembers } from './set-unit-members.js';
25
21
  import { registerSetZone } from './set-zone.js';
22
+ import { registerSharedTools } from './shared-tools.js';
26
23
  import { registerTemplateTools } from './templates/index.js';
27
24
  import { registerUndo } from './undo.js';
25
+ import { registerValidateDesign } from './validate-design.js';
28
26
  import { registerValidateScene } from './validate-scene.js';
29
27
  import { registerVariantTools } from './variants/index.js';
30
- /**
31
- * Register every non-vision MCP tool against the given server.
32
- * Vision tools (analyze_floorplan_image, analyze_room_photo) are registered
33
- * separately via `registerVisionTools` (Agent E).
34
- *
35
- * Scene-lifecycle tools (save/load/list/delete/rename scene) are registered
36
- * when persistence operations are available.
37
- */
38
- export function registerTools(server, operations) {
28
+ import { registerViewScene } from './view-scene.js';
29
+ export function registerTools(server, operations, { catalog, geometryScripts, sceneViews } = {}) {
39
30
  registerGetScene(server, operations);
40
- registerGetNode(server, operations);
41
31
  registerDescribeNode(server, operations);
42
32
  registerFindNodes(server, operations);
43
- registerSceneQueryTools(server, operations);
33
+ registerSharedTools(server, operations, catalog);
34
+ registerAddColumn(server, operations, geometryScripts);
35
+ registerAddObject(server, operations, geometryScripts);
36
+ registerGetSource(server, operations, geometryScripts);
44
37
  registerMeasure(server, operations);
38
+ registerViewScene(server, operations, sceneViews);
45
39
  registerConstructionTools(server, operations);
46
- registerRoomTools(server, operations);
40
+ registerRoomTools(server, operations, geometryScripts);
47
41
  registerApplyPatch(server, operations);
48
- registerCreateLevel(server, operations);
42
+ registerClearScene(server, operations);
49
43
  registerCreateUnit(server, operations);
50
44
  registerSetUnitMembers(server, operations);
51
45
  registerListUnits(server, operations);
52
- registerCreateWall(server, operations);
53
- registerPlaceItem(server, operations);
54
- registerCutOpening(server, operations);
46
+ registerPlaceDesign(server, operations);
55
47
  registerSetZone(server, operations);
56
- registerDuplicateLevel(server, operations);
57
- registerDeleteNode(server, operations);
58
48
  registerUndo(server, operations);
59
49
  registerRedo(server, operations);
60
50
  registerExportJson(server, operations);
61
51
  registerExportGlb(server, operations);
62
52
  registerValidateScene(server, operations);
53
+ registerValidateDesign(server, operations);
63
54
  registerCheckCollisions(server, operations);
64
55
  registerTemplateTools(server, operations);
65
56
  if (operations.hasStore) {
@@ -1,8 +1,12 @@
1
1
  import type { SceneGraph } from '@pascal-app/core/clone-scene-graph';
2
2
  import { z } from 'zod';
3
3
  import type { SceneOperations } from '../operations/index.js';
4
- export declare function syncDerivedStairOpenings(operations: SceneOperations): number;
5
4
  export type LiveSyncStatus = 'published' | 'unbound' | 'events_unsupported';
5
+ /** Where a write went: the project it reached, or none (a scratch scene). */
6
+ export type LiveSync = {
7
+ status: LiveSyncStatus;
8
+ project: string | null;
9
+ };
6
10
  type LiveSyncSkip = Exclude<LiveSyncStatus, 'published'>;
7
11
  /**
8
12
  * Output-schema fragment for every tool that mutates the scene. Spread into
@@ -10,6 +14,8 @@ type LiveSyncSkip = Exclude<LiveSyncStatus, 'published'>;
10
14
  * structured-content validation.
11
15
  */
12
16
  export declare const liveSyncOutput: {
17
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
18
+ unsaved: z.ZodOptional<z.ZodString>;
13
19
  persistence: z.ZodOptional<z.ZodObject<{
14
20
  status: z.ZodEnum<{
15
21
  unbound: "unbound";
@@ -19,15 +25,24 @@ export declare const liveSyncOutput: {
19
25
  }, z.core.$strip>>;
20
26
  };
21
27
  /**
22
- * Payload fragment matching `liveSyncOutput`: empty after a successful
23
- * publish, a `persistence` warning when the mutation stayed in-memory.
28
+ * Payload fragment matching `liveSyncOutput`: the project the write reached, or `null` and a note
29
+ * when none is bound; a `persistence` warning when the mutation stayed in-memory.
24
30
  */
25
- export declare function persistencePayload(status: LiveSyncStatus): {
31
+ export declare function persistencePayload({ status, project }: LiveSync): {
32
+ project: string | null;
33
+ unsaved?: string;
26
34
  persistence?: {
27
35
  status: LiveSyncSkip;
28
36
  warning: string;
29
37
  };
30
38
  };
39
+ /** Where the session's writes go, for a call that publishes nothing (an undo with nothing to undo). */
40
+ export declare function currentLiveSync(operations: SceneOperations): LiveSync;
41
+ /**
42
+ * Whether a tool call failed because the stored scene changed after the session
43
+ * loaded it. The store refuses before writing, so nothing from the call persisted.
44
+ */
45
+ export declare function isLiveSyncVersionConflict(error: unknown): boolean;
31
46
  /**
32
47
  * Persist the bridge's current graph to the active scene and append a live
33
48
  * event for browser subscribers. Skips persistence — reporting why — when the
@@ -35,7 +50,11 @@ export declare function persistencePayload(status: LiveSyncStatus): {
35
50
  * append scene events; callers surface that through `persistencePayload` so
36
51
  * the skip is never silent (#725).
37
52
  */
38
- export declare function publishLiveSceneSnapshot(operations: SceneOperations, kind: string): Promise<LiveSyncStatus>;
53
+ export declare function publishLiveSceneSnapshot(operations: SceneOperations, kind: string,
54
+ /** `allowSceneWipe`: the write empties the project on purpose (clear_scene). */
55
+ options?: {
56
+ allowSceneWipe?: boolean;
57
+ }): Promise<LiveSync>;
39
58
  export declare function appendLiveSceneEvent(operations: SceneOperations, sceneId: string, version: number, kind: string, graph: SceneGraph): Promise<void>;
40
59
  export {};
41
60
  //# sourceMappingURL=live-sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"live-sync.d.ts","sourceRoot":"","sources":["../../src/tools/live-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAEpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,eAAe,GAAG,MAAM,CAW5E;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,oBAAoB,CAAA;AAE3E,KAAK,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;AAExD;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;CAO1B,CAAA;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG;IAC1D,WAAW,CAAC,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CACxD,CAGA;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,cAAc,CAAC,CAwCzB;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf"}
1
+ {"version":3,"file":"live-sync.d.ts","sourceRoot":"","sources":["../../src/tools/live-sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,oBAAoB,CAAA;AAE3E,6EAA6E;AAC7E,MAAM,MAAM,QAAQ,GAAG;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAEzE,KAAK,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;AAExD;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;CAS1B,CAAA;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,GAAG;IACjE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CACxD,CAOA;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,UAAU,EAAE,eAAe,GAAG,QAAQ,CAGrE;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEjE;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM;AACZ,gFAAgF;AAChF,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GACzC,OAAO,CAAC,QAAQ,CAAC,CAsDnB;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf"}