@pascal-app/mcp 1.0.3 → 1.0.5

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 (305) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +181 -56
  3. package/dist/bin/pascal-mcp.js +9 -3
  4. package/dist/bridge/agent-session.d.ts +18 -0
  5. package/dist/bridge/agent-session.d.ts.map +1 -0
  6. package/dist/bridge/agent-session.js +15 -0
  7. package/dist/bridge/index.d.ts +1 -1
  8. package/dist/bridge/index.d.ts.map +1 -1
  9. package/dist/bridge/scene-bridge.d.ts +42 -6
  10. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  11. package/dist/bridge/scene-bridge.js +232 -45
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/lib/safe-fetch.d.ts +22 -4
  16. package/dist/lib/safe-fetch.d.ts.map +1 -1
  17. package/dist/lib/safe-fetch.js +233 -134
  18. package/dist/operations/scene-operations.d.ts +41 -3
  19. package/dist/operations/scene-operations.d.ts.map +1 -1
  20. package/dist/operations/scene-operations.js +42 -1
  21. package/dist/prompts/from-brief.d.ts +1 -1
  22. package/dist/prompts/from-brief.d.ts.map +1 -1
  23. package/dist/prompts/from-brief.js +3 -3
  24. package/dist/prompts/index.d.ts +1 -1
  25. package/dist/prompts/index.d.ts.map +1 -1
  26. package/dist/prompts/iterate-on-feedback.d.ts +1 -1
  27. package/dist/prompts/iterate-on-feedback.d.ts.map +1 -1
  28. package/dist/prompts/iterate-on-feedback.js +3 -3
  29. package/dist/prompts/renovation-from-photos.d.ts +1 -1
  30. package/dist/prompts/renovation-from-photos.d.ts.map +1 -1
  31. package/dist/prompts/renovation-from-photos.js +2 -2
  32. package/dist/prompts/scene-guidance.d.ts.map +1 -1
  33. package/dist/prompts/scene-guidance.js +13 -6
  34. package/dist/resources/agent-guide.d.ts +1 -1
  35. package/dist/resources/agent-guide.d.ts.map +1 -1
  36. package/dist/resources/agent-guide.js +62 -33
  37. package/dist/resources/catalog-items.d.ts +5 -6
  38. package/dist/resources/catalog-items.d.ts.map +1 -1
  39. package/dist/resources/catalog-items.js +7 -9
  40. package/dist/resources/constraints.d.ts +1 -1
  41. package/dist/resources/constraints.d.ts.map +1 -1
  42. package/dist/resources/constraints.js +1 -1
  43. package/dist/resources/design-schema.d.ts +9 -0
  44. package/dist/resources/design-schema.d.ts.map +1 -0
  45. package/dist/resources/design-schema.js +21 -0
  46. package/dist/resources/index.d.ts +4 -2
  47. package/dist/resources/index.d.ts.map +1 -1
  48. package/dist/resources/index.js +5 -2
  49. package/dist/resources/scene-current.d.ts +1 -1
  50. package/dist/resources/scene-current.d.ts.map +1 -1
  51. package/dist/resources/scene-summary.d.ts +1 -1
  52. package/dist/resources/scene-summary.d.ts.map +1 -1
  53. package/dist/server.d.ts +25 -1
  54. package/dist/server.d.ts.map +1 -1
  55. package/dist/server.js +71 -25
  56. package/dist/storage/disk-artifact-store.d.ts +9 -0
  57. package/dist/storage/disk-artifact-store.d.ts.map +1 -0
  58. package/dist/storage/disk-artifact-store.js +46 -0
  59. package/dist/storage/index.d.ts +1 -0
  60. package/dist/storage/index.d.ts.map +1 -1
  61. package/dist/storage/index.js +1 -0
  62. package/dist/storage/sqlite-scene-store.d.ts +3 -3
  63. package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
  64. package/dist/storage/sqlite-scene-store.js +4 -3
  65. package/dist/storage/types.d.ts +36 -7
  66. package/dist/storage/types.d.ts.map +1 -1
  67. package/dist/storage/types.js +12 -0
  68. package/dist/templates/empty-studio.d.ts +3 -3
  69. package/dist/templates/empty-studio.d.ts.map +1 -1
  70. package/dist/templates/garden-house.d.ts +3 -3
  71. package/dist/templates/garden-house.d.ts.map +1 -1
  72. package/dist/templates/index.d.ts.map +1 -1
  73. package/dist/templates/two-bedroom.d.ts +3 -3
  74. package/dist/templates/two-bedroom.d.ts.map +1 -1
  75. package/dist/tools/add-column.d.ts +5 -0
  76. package/dist/tools/add-column.d.ts.map +1 -0
  77. package/dist/tools/add-column.js +59 -0
  78. package/dist/tools/add-object.d.ts +76 -0
  79. package/dist/tools/add-object.d.ts.map +1 -0
  80. package/dist/tools/add-object.js +187 -0
  81. package/dist/tools/annotations.d.ts.map +1 -1
  82. package/dist/tools/apply-patch.d.ts +9 -2
  83. package/dist/tools/apply-patch.d.ts.map +1 -1
  84. package/dist/tools/apply-patch.js +36 -5
  85. package/dist/tools/asset-catalog.d.ts +9 -5
  86. package/dist/tools/asset-catalog.d.ts.map +1 -1
  87. package/dist/tools/asset-catalog.js +33 -12
  88. package/dist/tools/author-scene.d.ts +10 -0
  89. package/dist/tools/author-scene.d.ts.map +1 -0
  90. package/dist/tools/author-scene.js +140 -0
  91. package/dist/tools/check-collisions.d.ts +7 -7
  92. package/dist/tools/check-collisions.d.ts.map +1 -1
  93. package/dist/tools/check-collisions.js +7 -8
  94. package/dist/tools/clear-scene.d.ts +9 -0
  95. package/dist/tools/clear-scene.d.ts.map +1 -0
  96. package/dist/tools/clear-scene.js +59 -0
  97. package/dist/tools/collections.d.ts +4 -0
  98. package/dist/tools/collections.d.ts.map +1 -0
  99. package/dist/tools/collections.js +53 -0
  100. package/dist/tools/construction-tools.d.ts +12 -88
  101. package/dist/tools/construction-tools.d.ts.map +1 -1
  102. package/dist/tools/construction-tools.js +122 -381
  103. package/dist/tools/create-unit.d.ts +5 -3
  104. package/dist/tools/create-unit.d.ts.map +1 -1
  105. package/dist/tools/create-unit.js +2 -2
  106. package/dist/tools/errors.d.ts +15 -4
  107. package/dist/tools/errors.d.ts.map +1 -1
  108. package/dist/tools/errors.js +14 -4
  109. package/dist/tools/export-glb.d.ts +1 -1
  110. package/dist/tools/export-glb.d.ts.map +1 -1
  111. package/dist/tools/export-glb.js +2 -2
  112. package/dist/tools/export-json.d.ts +1 -1
  113. package/dist/tools/export-json.d.ts.map +1 -1
  114. package/dist/tools/export-json.js +2 -2
  115. package/dist/tools/geometry-script-worker.d.ts +2 -0
  116. package/dist/tools/geometry-script-worker.d.ts.map +1 -0
  117. package/dist/tools/geometry-script-worker.js +21 -0
  118. package/dist/tools/get-scene.d.ts +2 -1
  119. package/dist/tools/get-scene.d.ts.map +1 -1
  120. package/dist/tools/get-scene.js +5 -3
  121. package/dist/tools/honest-patch-guard.d.ts +2 -0
  122. package/dist/tools/honest-patch-guard.d.ts.map +1 -0
  123. package/dist/tools/honest-patch-guard.js +32 -0
  124. package/dist/tools/hosted-services.d.ts +4 -0
  125. package/dist/tools/hosted-services.d.ts.map +1 -0
  126. package/dist/tools/hosted-services.js +40 -0
  127. package/dist/tools/index.d.ts +12 -2
  128. package/dist/tools/index.d.ts.map +1 -1
  129. package/dist/tools/index.js +18 -32
  130. package/dist/tools/level-height-guard.d.ts +2 -0
  131. package/dist/tools/level-height-guard.d.ts.map +1 -0
  132. package/dist/tools/level-height-guard.js +11 -0
  133. package/dist/tools/list-units.d.ts +2 -2
  134. package/dist/tools/list-units.d.ts.map +1 -1
  135. package/dist/tools/list-units.js +2 -2
  136. package/dist/tools/live-sync.d.ts +25 -6
  137. package/dist/tools/live-sync.d.ts.map +1 -1
  138. package/dist/tools/live-sync.js +48 -27
  139. package/dist/tools/local-geometry-scripts.d.ts +8 -0
  140. package/dist/tools/local-geometry-scripts.d.ts.map +1 -0
  141. package/dist/tools/local-geometry-scripts.js +67 -0
  142. package/dist/tools/measure.d.ts +7 -1
  143. package/dist/tools/measure.d.ts.map +1 -1
  144. package/dist/tools/measure.js +38 -70
  145. package/dist/tools/measurement.d.ts +1 -36
  146. package/dist/tools/measurement.d.ts.map +1 -1
  147. package/dist/tools/measurement.js +2 -94
  148. package/dist/tools/node-world-point.d.ts +37 -0
  149. package/dist/tools/node-world-point.d.ts.map +1 -0
  150. package/dist/tools/node-world-point.js +457 -0
  151. package/dist/tools/normalize-schema-dialect.d.ts +1 -1
  152. package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
  153. package/dist/tools/normalize-schema-dialect.js +22 -3
  154. package/dist/tools/patch-guards.d.ts +60 -0
  155. package/dist/tools/patch-guards.d.ts.map +1 -0
  156. package/dist/tools/patch-guards.js +256 -0
  157. package/dist/tools/photo-to-scene/index.d.ts +1 -1
  158. package/dist/tools/photo-to-scene/index.d.ts.map +1 -1
  159. package/dist/tools/photo-to-scene/photo-to-scene.d.ts +3 -3
  160. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  161. package/dist/tools/photo-to-scene/photo-to-scene.js +10 -9
  162. package/dist/tools/project-files.d.ts +4 -0
  163. package/dist/tools/project-files.d.ts.map +1 -0
  164. package/dist/tools/project-files.js +30 -0
  165. package/dist/tools/redo.d.ts +4 -2
  166. package/dist/tools/redo.d.ts.map +1 -1
  167. package/dist/tools/redo.js +4 -4
  168. package/dist/tools/retired-tools.d.ts +10 -0
  169. package/dist/tools/retired-tools.d.ts.map +1 -0
  170. package/dist/tools/retired-tools.js +22 -0
  171. package/dist/tools/roof-level-guard.d.ts +2 -0
  172. package/dist/tools/roof-level-guard.d.ts.map +1 -0
  173. package/dist/tools/roof-level-guard.js +22 -0
  174. package/dist/tools/room-tools.d.ts +36 -98
  175. package/dist/tools/room-tools.d.ts.map +1 -1
  176. package/dist/tools/room-tools.js +130 -565
  177. package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
  178. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
  179. package/dist/tools/scene-lifecycle/create-project.js +31 -6
  180. package/dist/tools/scene-lifecycle/delete-scene.d.ts +3 -1
  181. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
  182. package/dist/tools/scene-lifecycle/delete-scene.js +10 -9
  183. package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
  184. package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
  185. package/dist/tools/scene-lifecycle/get-project-status.js +6 -6
  186. package/dist/tools/scene-lifecycle/index.d.ts +1 -1
  187. package/dist/tools/scene-lifecycle/index.d.ts.map +1 -1
  188. package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
  189. package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
  190. package/dist/tools/scene-lifecycle/list-scenes.js +4 -4
  191. package/dist/tools/scene-lifecycle/load-scene.d.ts +2 -2
  192. package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
  193. package/dist/tools/scene-lifecycle/load-scene.js +5 -5
  194. package/dist/tools/scene-lifecycle/metadata.d.ts +1 -1
  195. package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
  196. package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
  197. package/dist/tools/scene-lifecycle/rename-scene.js +6 -6
  198. package/dist/tools/scene-lifecycle/save-scene.d.ts +6 -3
  199. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
  200. package/dist/tools/scene-lifecycle/save-scene.js +124 -16
  201. package/dist/tools/scene-lifecycle/test-utils.d.ts +3 -3
  202. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
  203. package/dist/tools/scene-lifecycle/test-utils.js +2 -1
  204. package/dist/tools/set-unit-members.d.ts +4 -2
  205. package/dist/tools/set-unit-members.d.ts.map +1 -1
  206. package/dist/tools/set-unit-members.js +6 -6
  207. package/dist/tools/set-zone.d.ts +4 -2
  208. package/dist/tools/set-zone.d.ts.map +1 -1
  209. package/dist/tools/set-zone.js +30 -18
  210. package/dist/tools/shared-tools.d.ts +40 -0
  211. package/dist/tools/shared-tools.d.ts.map +1 -0
  212. package/dist/tools/shared-tools.js +574 -0
  213. package/dist/tools/structure-tools.d.ts +57 -0
  214. package/dist/tools/structure-tools.d.ts.map +1 -0
  215. package/dist/tools/structure-tools.js +57 -0
  216. package/dist/tools/templates/create-from-template.d.ts +2 -2
  217. package/dist/tools/templates/create-from-template.d.ts.map +1 -1
  218. package/dist/tools/templates/create-from-template.js +6 -6
  219. package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
  220. package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
  221. package/dist/tools/templates/create-house-from-brief.js +6 -6
  222. package/dist/tools/templates/index.d.ts +1 -1
  223. package/dist/tools/templates/index.d.ts.map +1 -1
  224. package/dist/tools/templates/list-templates.d.ts +1 -1
  225. package/dist/tools/templates/list-templates.d.ts.map +1 -1
  226. package/dist/tools/templates/list-templates.js +2 -2
  227. package/dist/tools/undo.d.ts +4 -2
  228. package/dist/tools/undo.d.ts.map +1 -1
  229. package/dist/tools/undo.js +4 -4
  230. package/dist/tools/unit-members.d.ts.map +1 -1
  231. package/dist/tools/unit-members.js +6 -6
  232. package/dist/tools/variants/generate-variants.d.ts +6 -6
  233. package/dist/tools/variants/generate-variants.d.ts.map +1 -1
  234. package/dist/tools/variants/generate-variants.js +6 -6
  235. package/dist/tools/variants/index.d.ts +1 -1
  236. package/dist/tools/variants/index.d.ts.map +1 -1
  237. package/dist/tools/variants/mutations.js +1 -1
  238. package/dist/tools/view-scene.d.ts +44 -0
  239. package/dist/tools/view-scene.d.ts.map +1 -0
  240. package/dist/tools/view-scene.js +75 -0
  241. package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
  242. package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
  243. package/dist/tools/vision/analyze-floorplan-image.js +7 -7
  244. package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
  245. package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
  246. package/dist/tools/vision/analyze-room-photo.js +7 -7
  247. package/dist/tools/vision/index.d.ts +1 -1
  248. package/dist/tools/vision/index.d.ts.map +1 -1
  249. package/dist/transports/http.d.ts +8 -2
  250. package/dist/transports/http.d.ts.map +1 -1
  251. package/dist/transports/http.js +9 -3
  252. package/dist/transports/stdio.d.ts +15 -9
  253. package/dist/transports/stdio.d.ts.map +1 -1
  254. package/dist/transports/stdio.js +18 -11
  255. package/dist/version.d.ts +5 -0
  256. package/dist/version.d.ts.map +1 -1
  257. package/dist/version.js +17 -3
  258. package/examples/coordinate-conventions-demo.md +12 -13
  259. package/examples/embed-in-agent.ts +6 -7
  260. package/examples/generate-apartment.md +13 -15
  261. package/examples/photo-to-scene.md +3 -4
  262. package/examples/renovate-from-photos.md +6 -9
  263. package/package.json +19 -9
  264. package/dist/tools/create-level.d.ts +0 -21
  265. package/dist/tools/create-level.d.ts.map +0 -1
  266. package/dist/tools/create-level.js +0 -63
  267. package/dist/tools/create-wall.d.ts +0 -22
  268. package/dist/tools/create-wall.d.ts.map +0 -1
  269. package/dist/tools/create-wall.js +0 -57
  270. package/dist/tools/cut-opening.d.ts +0 -25
  271. package/dist/tools/cut-opening.d.ts.map +0 -1
  272. package/dist/tools/cut-opening.js +0 -61
  273. package/dist/tools/delete-node.d.ts +0 -19
  274. package/dist/tools/delete-node.d.ts.map +0 -1
  275. package/dist/tools/delete-node.js +0 -40
  276. package/dist/tools/describe-node.d.ts +0 -17
  277. package/dist/tools/describe-node.d.ts.map +0 -1
  278. package/dist/tools/describe-node.js +0 -97
  279. package/dist/tools/door-clearance.d.ts +0 -135
  280. package/dist/tools/door-clearance.d.ts.map +0 -1
  281. package/dist/tools/door-clearance.js +0 -334
  282. package/dist/tools/duplicate-level.d.ts +0 -19
  283. package/dist/tools/duplicate-level.d.ts.map +0 -1
  284. package/dist/tools/duplicate-level.js +0 -60
  285. package/dist/tools/find-nodes.d.ts +0 -32
  286. package/dist/tools/find-nodes.d.ts.map +0 -1
  287. package/dist/tools/find-nodes.js +0 -107
  288. package/dist/tools/geometry.d.ts +0 -24
  289. package/dist/tools/geometry.d.ts.map +0 -1
  290. package/dist/tools/geometry.js +0 -94
  291. package/dist/tools/get-node.d.ts +0 -11
  292. package/dist/tools/get-node.d.ts.map +0 -1
  293. package/dist/tools/get-node.js +0 -29
  294. package/dist/tools/layout-clearance.d.ts +0 -105
  295. package/dist/tools/layout-clearance.d.ts.map +0 -1
  296. package/dist/tools/layout-clearance.js +0 -190
  297. package/dist/tools/place-item.d.ts +0 -22
  298. package/dist/tools/place-item.d.ts.map +0 -1
  299. package/dist/tools/place-item.js +0 -89
  300. package/dist/tools/scene-query.d.ts +0 -62
  301. package/dist/tools/scene-query.d.ts.map +0 -1
  302. package/dist/tools/scene-query.js +0 -670
  303. package/dist/tools/validate-scene.d.ts +0 -19
  304. package/dist/tools/validate-scene.d.ts.map +0 -1
  305. package/dist/tools/validate-scene.js +0 -28
@@ -0,0 +1,8 @@
1
+ import type { GeometryScriptHost } from './add-object.js';
2
+ /**
3
+ * Local compile is opt-in: a separate process is not an untrusted-code sandbox.
4
+ * three.js modules need PASCAL_SERVER_SCRIPT_COMPILE=1; bpy modules run in the
5
+ * Blender binary PASCAL_BLENDER names.
6
+ */
7
+ export declare function localGeometryScripts(projectPath: string): GeometryScriptHost;
8
+ //# sourceMappingURL=local-geometry-scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-geometry-scripts.d.ts","sourceRoot":"","sources":["../../src/tools/local-geometry-scripts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEtD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,CA4D5E"}
@@ -0,0 +1,67 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { compileBlenderBuild } from '@pascal-app/geometry-script/blender';
4
+ import { runBlenderLocally } from '@pascal-app/geometry-script/blender-local';
5
+ import { DiskArtifactStore } from '../storage/disk-artifact-store.js';
6
+ /**
7
+ * Local compile is opt-in: a separate process is not an untrusted-code sandbox.
8
+ * three.js modules need PASCAL_SERVER_SCRIPT_COMPILE=1; bpy modules run in the
9
+ * Blender binary PASCAL_BLENDER names.
10
+ */
11
+ export function localGeometryScripts(projectPath) {
12
+ const artifacts = new DiskArtifactStore(projectPath);
13
+ return {
14
+ compile: async (input) => {
15
+ if (input.language === 'bpy') {
16
+ const blender = process.env.PASCAL_BLENDER;
17
+ if (!blender)
18
+ throw new Error('bpy scripts need PASCAL_BLENDER, the path to a Blender binary');
19
+ const run = await runBlenderLocally({ blender, code: input.code, params: input.params });
20
+ const { glb, ...compiled } = await compileBlenderBuild({ ...input, run });
21
+ return { ...compiled, glb: new Uint8Array(glb) };
22
+ }
23
+ if (process.env.PASCAL_SERVER_SCRIPT_COMPILE !== '1') {
24
+ throw new Error('Local script compilation requires PASCAL_SERVER_SCRIPT_COMPILE=1');
25
+ }
26
+ return new Promise((resolve, reject) => {
27
+ const child = spawn(process.execPath, [fileURLToPath(new URL('./geometry-script-worker.js', import.meta.url))], { env: {}, stdio: ['pipe', 'pipe', 'pipe'] });
28
+ const chunks = [];
29
+ let stderr = '';
30
+ const timer = setTimeout(() => {
31
+ child.kill('SIGKILL');
32
+ reject(new Error('Geometry script compilation timed out'));
33
+ }, 20_000);
34
+ child.stdout.on('data', (chunk) => chunks.push(chunk));
35
+ child.stderr.on('data', (chunk) => {
36
+ stderr = (stderr + chunk.toString()).slice(-2000);
37
+ });
38
+ child.stdin.on('error', reject);
39
+ child.on('error', (error) => {
40
+ clearTimeout(timer);
41
+ reject(error);
42
+ });
43
+ child.on('close', () => {
44
+ clearTimeout(timer);
45
+ try {
46
+ const message = JSON.parse(Buffer.concat(chunks).toString());
47
+ if (!message.ok)
48
+ return reject(new Error(message.error));
49
+ resolve({
50
+ ...message.compiled,
51
+ glb: new Uint8Array(Buffer.from(message.glb, 'base64')),
52
+ });
53
+ }
54
+ catch {
55
+ reject(new Error(`Geometry script compilation failed: ${stderr}`));
56
+ }
57
+ });
58
+ child.stdin.end(JSON.stringify(input));
59
+ });
60
+ },
61
+ storeArtifact: async ({ sha256, bytes }) => {
62
+ await artifacts.put(sha256, bytes);
63
+ return sha256;
64
+ },
65
+ readArtifact: ({ sha256 }) => artifacts.read(sha256),
66
+ };
67
+ }
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import { z } from 'zod';
3
3
  import type { SceneOperations } from '../operations/index.js';
4
4
  export declare const measureInput: {
@@ -7,6 +7,12 @@ export declare const measureInput: {
7
7
  };
8
8
  export declare const measureOutput: {
9
9
  distanceMeters: z.ZodNumber;
10
+ fromPoint: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
11
+ toPoint: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
12
+ approximate: z.ZodOptional<z.ZodArray<z.ZodObject<{
13
+ id: z.ZodString;
14
+ reason: z.ZodString;
15
+ }, z.core.$strip>>>;
10
16
  areaSqMeters: z.ZodOptional<z.ZodNumber>;
11
17
  units: z.ZodLiteral<"meters">;
12
18
  areaUnits: z.ZodOptional<z.ZodLiteral<"square_meters">>;
@@ -1 +1 @@
1
- {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../src/tools/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAKpD,eAAO,MAAM,YAAY;;;CAGxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;CAKzB,CAAA;AAsED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAyEhF"}
1
+ {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../src/tools/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAE7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,YAAY;IACvB,MAAM;IACN,IAAI;CACL,CAAA;AAED,eAAO,MAAM,aAAa;IACxB,cAAc;IACd,SAAS;IACT,OAAO;IACP,WAAW;;;;IACX,YAAY;IACZ,KAAK;IACL,SAAS;CACV,CAAA;AAeD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA6FhF"}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
3
- import { ErrorCode, throwMcpError } from './errors.js';
3
+ import { ProtocolErrorCode, throwMcpError } from './errors.js';
4
+ import { resolveNodeWorldPoint } from './node-world-point.js';
4
5
  import { NodeIdSchema } from './schemas.js';
5
6
  export const measureInput = {
6
7
  fromId: NodeIdSchema,
@@ -8,66 +9,13 @@ export const measureInput = {
8
9
  };
9
10
  export const measureOutput = {
10
11
  distanceMeters: z.number(),
12
+ fromPoint: z.array(z.number()).optional(),
13
+ toPoint: z.array(z.number()).optional(),
14
+ approximate: z.array(z.object({ id: z.string(), reason: z.string() })).optional(),
11
15
  areaSqMeters: z.number().optional(),
12
16
  units: z.literal('meters'),
13
17
  areaUnits: z.literal('square_meters').optional(),
14
18
  };
15
- /**
16
- * Compute a 3D centre point in level-coordinate space for distance measurement.
17
- *
18
- * For walls / fences: midpoint of the start/end segment at Y=0.
19
- * For positioned nodes (item, door, window, building, stair, roof): `position`.
20
- * For polygon nodes (slab, ceiling, zone): 2D centroid lifted to Y=0.
21
- * For site: centroid of property line at Y=0 if available.
22
- *
23
- * Returns null if no representative centre can be derived (e.g. level node
24
- * has no position of its own).
25
- */
26
- function getCentre(node) {
27
- switch (node.type) {
28
- case 'wall':
29
- case 'fence': {
30
- const [x1, z1] = node.start;
31
- const [x2, z2] = node.end;
32
- return [(x1 + x2) / 2, 0, (z1 + z2) / 2];
33
- }
34
- case 'item':
35
- case 'door':
36
- case 'window':
37
- case 'building':
38
- case 'stair':
39
- case 'roof':
40
- return node.position;
41
- case 'slab':
42
- case 'ceiling':
43
- case 'zone': {
44
- const poly = node.polygon;
45
- if (!poly || poly.length === 0)
46
- return null;
47
- let cx = 0;
48
- let cz = 0;
49
- for (const [x, z] of poly) {
50
- cx += x;
51
- cz += z;
52
- }
53
- return [cx / poly.length, 0, cz / poly.length];
54
- }
55
- case 'site': {
56
- const pts = node.polygon?.points ?? [];
57
- if (pts.length === 0)
58
- return [0, 0, 0];
59
- let cx = 0;
60
- let cz = 0;
61
- for (const [x, z] of pts) {
62
- cx += x;
63
- cz += z;
64
- }
65
- return [cx / pts.length, 0, cz / pts.length];
66
- }
67
- default:
68
- return null;
69
- }
70
- }
71
19
  /** Compute polygon area via the shoelace formula. */
72
20
  function shoelaceArea(polygon) {
73
21
  if (polygon.length < 3)
@@ -84,18 +32,18 @@ function shoelaceArea(polygon) {
84
32
  export function registerMeasure(server, bridge) {
85
33
  server.registerTool('measure', {
86
34
  title: 'Measure',
87
- description: 'Measure distance (in meters) between two nodes, or the net area of a polygon node when fromId === toId.',
88
- inputSchema: measureInput,
89
- outputSchema: measureOutput,
35
+ description: "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.",
36
+ inputSchema: z.object(measureInput),
37
+ outputSchema: z.object(measureOutput),
90
38
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
91
39
  }, async ({ fromId, toId }) => {
92
40
  const from = bridge.getNode(fromId);
93
41
  if (!from) {
94
- throwMcpError(ErrorCode.InvalidParams, `Node not found: ${fromId}`);
42
+ throwMcpError(ProtocolErrorCode.InvalidParams, `Node not found: ${fromId}`);
95
43
  }
96
44
  const to = bridge.getNode(toId);
97
45
  if (!to) {
98
- throwMcpError(ErrorCode.InvalidParams, `Node not found: ${toId}`);
46
+ throwMcpError(ProtocolErrorCode.InvalidParams, `Node not found: ${toId}`);
99
47
  }
100
48
  // Self-measurement: compute area for polygon-bearing nodes.
101
49
  if (fromId === toId) {
@@ -124,17 +72,37 @@ export function registerMeasure(server, bridge) {
124
72
  structuredContent: payload,
125
73
  };
126
74
  }
127
- const fromCentre = getCentre(from);
128
- const toCentre = getCentre(to);
129
- if (!(fromCentre && toCentre)) {
130
- throwMcpError(ErrorCode.InvalidRequest, `Cannot derive centre for measurement between ${from.type} and ${to.type}`);
131
- }
132
- const dx = fromCentre[0] - toCentre[0];
133
- const dy = fromCentre[1] - toCentre[1];
134
- const dz = fromCentre[2] - toCentre[2];
75
+ const nodes = bridge.getNodes();
76
+ const pointOf = (node) => {
77
+ try {
78
+ const point = resolveNodeWorldPoint(node.id, nodes);
79
+ if (point)
80
+ return point;
81
+ }
82
+ catch (err) {
83
+ const reason = err instanceof Error ? err.message : String(err);
84
+ throwMcpError(ProtocolErrorCode.InvalidRequest, `Cannot derive a world point for ${node.type} ${node.id}: ${reason}`);
85
+ }
86
+ throwMcpError(ProtocolErrorCode.InvalidRequest, `Cannot derive a world point for ${node.type} ${node.id}`);
87
+ };
88
+ const fromResolved = pointOf(from);
89
+ const toResolved = pointOf(to);
90
+ const fromPoint = fromResolved.point;
91
+ const toPoint = toResolved.point;
92
+ const approximate = [];
93
+ if (fromResolved.approximate)
94
+ approximate.push({ id: from.id, reason: fromResolved.approximate });
95
+ if (toResolved.approximate)
96
+ approximate.push({ id: to.id, reason: toResolved.approximate });
97
+ const dx = fromPoint[0] - toPoint[0];
98
+ const dy = fromPoint[1] - toPoint[1];
99
+ const dz = fromPoint[2] - toPoint[2];
135
100
  const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
136
101
  const payload = {
137
102
  distanceMeters: distance,
103
+ fromPoint,
104
+ toPoint,
105
+ ...(approximate.length > 0 ? { approximate } : {}),
138
106
  units: 'meters',
139
107
  };
140
108
  return {
@@ -1,37 +1,2 @@
1
- import { type Kind } from '@pascal-app/lingo';
2
- import { z } from 'zod';
3
- /**
4
- * A zod field for a measurement tool argument that a model may emit as a bare
5
- * number OR as natural language. `measurement('length', 'm')` accepts `0.15`,
6
- * `"6 in"`, `"180cm"`, `"2 ft 3 in"`; `measurement('angle', 'deg')` accepts
7
- * `45`, `"45°"`, `"1.57rad"`, `"0.25 turn"`. The value is canonicalized (via
8
- * `@pascal-app/lingo`) to a number in `unit` — the exact unit the tool handler
9
- * already expects — so no handler change is needed. `min`/`max` (in `unit`)
10
- * reject out-of-range values with a model-readable message.
11
- *
12
- * The emitted JSON Schema is `number | string`, so the model is free to answer
13
- * in whatever unit it is thinking in; AI SDK v6 applies the transform and
14
- * forwards the canonical number to the tool executor.
15
- *
16
- * Tool-boundary safety: genuinely ambiguous separators (`"1,234"`, which could
17
- * mean 1234 or 1.234) are rejected rather than silently absorbed, so a European
18
- * decimal never becomes a 1000× value.
19
- *
20
- * NOTE: intentionally duplicated as `editor/packages/mcp/src/tools/measurement.ts`
21
- * (MCP tools) and `packages/ai/src/tools/scene/measurement.ts` (AI-chat tools).
22
- * The two tool stacks live in different packages — one inside the `editor`
23
- * submodule — so they can't share a module. Keep the two copies identical; the
24
- * clean long-term dedup is a zod-field adapter exported from `@pascal-app/lingo`.
25
- */
26
- export interface MeasurementOptions {
27
- /** Semantic description (e.g. "Wall thickness"). The natural-language note is appended. */
28
- description?: string;
29
- /** Upper bound, in `unit`. */
30
- max?: number;
31
- /** Inclusive lower bound, in `unit`. */
32
- min?: number;
33
- /** Require the value to be strictly greater than 0 (for non-zero dimensions). */
34
- positive?: boolean;
35
- }
36
- export declare function measurement(kind: Kind, unit: string, opts?: MeasurementOptions): z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
1
+ export { measurement } from '@pascal-app/core/agent-tools';
37
2
  //# sourceMappingURL=measurement.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"measurement.d.ts","sourceRoot":"","sources":["../../src/tools/measurement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAiB,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,kBAAkB;IACjC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AA6CD,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,uGAoDlF"}
1
+ {"version":3,"file":"measurement.d.ts","sourceRoot":"","sources":["../../src/tools/measurement.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA"}
@@ -1,94 +1,2 @@
1
- import { parseQuantity } from '@pascal-app/lingo';
2
- import { z } from 'zod';
3
- function unitNoun(unit) {
4
- switch (unit) {
5
- case 'm':
6
- return 'meters';
7
- case 'deg':
8
- return 'degrees';
9
- case 'rad':
10
- return 'radians';
11
- default:
12
- return unit;
13
- }
14
- }
15
- function naturalLanguageNote(kind, unit) {
16
- let examples;
17
- if (kind === 'angle') {
18
- // Lead with an example in the field's own unit so a bare number is read
19
- // the way the model intends (a bare "45" in a radians field is 45 rad).
20
- examples =
21
- unit === 'rad' ? '1.5708, "90°", "1.57rad", "0.25 turn"' : '45, "45°", "1.57rad", "0.25 turn"';
22
- }
23
- else {
24
- examples = '0.9, "6 ft", "180cm", "2 ft 3 in"';
25
- }
26
- return `Accepts a number (${unitNoun(unit)}) or a natural-language string; other units are converted. e.g. ${examples}.`;
27
- }
28
- function boundsNote(unit, min, max, positive) {
29
- if (positive) {
30
- return max === undefined
31
- ? ` Must be greater than 0 ${unit}.`
32
- : ` Greater than 0, up to ${max} ${unit}.`;
33
- }
34
- if (min !== undefined && max !== undefined)
35
- return ` Range ${min}–${max} ${unit}.`;
36
- if (min !== undefined)
37
- return ` Minimum ${min} ${unit}.`;
38
- if (max !== undefined)
39
- return ` Maximum ${max} ${unit}.`;
40
- return '';
41
- }
42
- export function measurement(kind, unit, opts = {}) {
43
- const { min, max, positive = false } = opts;
44
- const description = [
45
- opts.description,
46
- naturalLanguageNote(kind, unit) + boundsNote(unit, min, max, positive),
47
- ]
48
- .filter(Boolean)
49
- .join(' ');
50
- return z
51
- .union([z.number(), z.string()])
52
- .transform((val, ctx) => {
53
- let value;
54
- if (typeof val === 'number') {
55
- value = val;
56
- }
57
- else {
58
- const result = parseQuantity(val, {
59
- kind,
60
- unit,
61
- strictness: 'forgiving',
62
- // A genuinely ambiguous separator ("1,234") must not silently become
63
- // a 1000× value at a tool boundary — fail so the model self-corrects.
64
- escalate: { AMBIGUOUS_NUMBER: 'error' },
65
- });
66
- if (!result.ok) {
67
- ctx.addIssue({
68
- code: 'custom',
69
- message: result.issues[0]?.message ?? `Could not read "${val}" as a ${kind}.`,
70
- });
71
- return z.NEVER;
72
- }
73
- value = result.quantity.to(unit).value;
74
- }
75
- if (!Number.isFinite(value)) {
76
- ctx.addIssue({ code: 'custom', message: `Value must be a finite ${kind} in ${unit}.` });
77
- return z.NEVER;
78
- }
79
- if (positive && value <= 0) {
80
- ctx.addIssue({ code: 'custom', message: `Must be greater than 0 ${unit} (got ${value}).` });
81
- return z.NEVER;
82
- }
83
- if (min !== undefined && value < min) {
84
- ctx.addIssue({ code: 'custom', message: `Must be at least ${min} ${unit} (got ${value}).` });
85
- return z.NEVER;
86
- }
87
- if (max !== undefined && value > max) {
88
- ctx.addIssue({ code: 'custom', message: `Must be at most ${max} ${unit} (got ${value}).` });
89
- return z.NEVER;
90
- }
91
- return value;
92
- })
93
- .describe(description);
94
- }
1
+ // The one measurement() helper, shared with the hosted chat through the agent tool contracts.
2
+ export { measurement } from '@pascal-app/core/agent-tools';
@@ -0,0 +1,37 @@
1
+ import type { Vec3 } from '@pascal-app/core/agent-operations';
2
+ import { AnyNode } from '@pascal-app/core/schema';
3
+ type Nodes = Readonly<Record<string, AnyNode>>;
4
+ /**
5
+ * Built-in kinds the viewer lifts onto the slab under them through the
6
+ * `floorPlaced` capability of their registered definition, and whose footprint
7
+ * core cannot derive without it (core derives item, shelf and procedural-item
8
+ * footprints from the node itself). Headless MCP does not load
9
+ * `@pascal-app/nodes`, so their lift is unresolved and flagged, not guessed. A
10
+ * test keeps this list in step with the definitions.
11
+ */
12
+ export declare const HEADLESS_UNRESOLVED_FLOOR_LIFT_KINDS: readonly ['block', 'cabinet', 'cabinet-module', 'column', 'duct-terminal', 'hvac-equipment', 'spawn', 'stair'];
13
+ /**
14
+ * World-space reference point of any node, following the frames the viewer
15
+ * renders it in (core's `nodeLevelFrame`, the level's stacked base Y and its
16
+ * building transform):
17
+ * - a positioned node: its origin (an item's or column's base, a door's or
18
+ * window's centre), inside its host's frame;
19
+ * - a wall or fence: the midpoint of its centreline (arc or spline) at its base;
20
+ * - a grid line: its midpoint;
21
+ * - a slab, ceiling, zone or site: its polygon centroid on its own plane;
22
+ * - a block or imported mesh: the centre of its vertex bounds;
23
+ * - a path or measurement: the centre of its points;
24
+ * - a level: the plan centre of its content on its base plane;
25
+ * - another container (a unit, …): the centre of its descendants' points,
26
+ * or its origin when it has none.
27
+ * Kinds whose renderer derives the pose elsewhere (a downspout at its gutter
28
+ * outlet, a gutter at the eave, …) are returned with an `approximate` reason.
29
+ */
30
+ export type NodeWorldPoint = {
31
+ point: Vec3;
32
+ /** Set when the renderer derives this kind's pose from data not modelled here. */
33
+ approximate?: string;
34
+ };
35
+ export declare function resolveNodeWorldPoint(id: string, nodes: Nodes): NodeWorldPoint | null;
36
+ export {};
37
+ //# sourceMappingURL=node-world-point.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-world-point.d.ts","sourceRoot":"","sources":["../../src/tools/node-world-point.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAA;AAS7D,OAAO,EACL,OAAO,EAIR,MAAM,yBAAyB,CAAA;AAEhC,KAAK,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAgN9C;;;;;;;GAOG;AACH,eAAO,MAAM,oCAAoC,YAC/C,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,OAAO,CACC,CAAA;AA6KV;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,IAAI,CAAA;IACX,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,cAAc,GAAG,IAAI,CAMrF"}