@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,187 @@
1
+ import { isScriptedNode, scriptedObjectMeta } from '@pascal-app/core';
2
+ import { achievedChanges, addObject, authoredObject, editedScriptLanguage, editedScriptParams, furnishingRefusal, readableScript, readSourceResult, requireAddObjectReason, } from '@pascal-app/core/agent-operations';
3
+ import { addObjectTool, CATALOG_SOURCE, getSourceTool, isAgentRefusal, refuse, } from '@pascal-app/core/agent-tools';
4
+ import { GEOMETRY_SCRIPT_MIME_TYPES, GeometryArtifactMetadata, GeometryReuseFields, generateId, } from '@pascal-app/core/schema';
5
+ import { z } from 'zod';
6
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS, READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
7
+ import { refusalResult, toolError } from './errors.js';
8
+ import { persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
9
+ import { briefInForce, toPatches } from './shared-tools.js';
10
+ /** The kind an artifact records; a room's floor finish and a wall face's are none of the node kinds. */
11
+ const ARTIFACT_KIND = {
12
+ object: 'item',
13
+ window: 'window',
14
+ door: 'door',
15
+ column: 'column',
16
+ floor: undefined,
17
+ 'wall-face': undefined,
18
+ };
19
+ /** Compiles a module on the host and stores its GLB and text for the scene: the step every scripted tool shares. */
20
+ export async function compileAndStore(host, sceneId, { code, language, params, inputs, }, kind, context = {}) {
21
+ const nodeId = context.nodeId ?? generateId(ARTIFACT_KIND[kind] ?? 'item');
22
+ const metadata = GeometryArtifactMetadata.parse({
23
+ ...context.metadata,
24
+ kind: ARTIFACT_KIND[kind],
25
+ });
26
+ if (host.build)
27
+ return {
28
+ ...(await host.build({ sceneId, code, language, params, inputs, kind, nodeId, metadata })),
29
+ nodeId,
30
+ };
31
+ const { glb, ...compiled } = await host.compile({ code, language, params, inputs });
32
+ metadata.mount = compiled.mount;
33
+ const [sha256] = await Promise.all([
34
+ host.storeArtifact({
35
+ sceneId,
36
+ nodeId,
37
+ metadata,
38
+ sha256: compiled.sha256,
39
+ bytes: glb,
40
+ mimeType: 'model/gltf-binary',
41
+ }),
42
+ host.storeArtifact({
43
+ sceneId,
44
+ nodeId,
45
+ metadata,
46
+ sha256: compiled.script,
47
+ bytes: new TextEncoder().encode(code),
48
+ mimeType: GEOMETRY_SCRIPT_MIME_TYPES[language],
49
+ }),
50
+ ]);
51
+ return { ...compiled, sha256, nodeId };
52
+ }
53
+ async function scriptText(host, sceneId, node) {
54
+ const bytes = await host.readArtifact({ sceneId, sha256: node.source.script });
55
+ if (!bytes)
56
+ throw new Error(`The script of ${node.id} could not be read`);
57
+ return new TextDecoder().decode(bytes);
58
+ }
59
+ export function readScript(host, sceneId, bridge, nodeId) {
60
+ return scriptText(host, sceneId, authoredObject(bridge.getNodes(), nodeId));
61
+ }
62
+ /**
63
+ * One object's module compiled and stored by the host: a new one from its code, an edit from its
64
+ * code or its stored script, with the params it keeps. The step a lone add_object and each
65
+ * add_object of a run_batch share.
66
+ */
67
+ export async function compileObject(host, sceneId, bridge, args) {
68
+ const code = args.code ??
69
+ (args.nodeId ? await readScript(host, sceneId, bridge, args.nodeId) : refuseMissingCode());
70
+ const nodes = bridge.getNodes();
71
+ const previous = args.nodeId ? nodes[args.nodeId] : undefined;
72
+ const language = editedScriptLanguage(previous, args);
73
+ const params = editedScriptParams(previous, args.params);
74
+ return compileAndStore(host, sceneId, { code, language, params }, 'object', {
75
+ nodeId: args.nodeId,
76
+ metadata: {
77
+ ...(isScriptedNode(previous) ? scriptedObjectMeta(previous) : {}),
78
+ ...GeometryReuseFields.parse(args),
79
+ },
80
+ });
81
+ }
82
+ /** What a batch's add_object builds with: the host's compile, on the scene open now. */
83
+ export async function compileObjectOnHost(host, bridge, args) {
84
+ if (!host)
85
+ refuse('scripts_unavailable', 'This Pascal server cannot run geometry scripts.');
86
+ const scene = bridge.getActiveScene();
87
+ if (!scene)
88
+ refuse('no_active_scene', 'Open or save a scene first: authored objects are stored with a scene.');
89
+ return compileObject(host, scene.id, bridge, args);
90
+ }
91
+ /** `add_object` on the MCP: the shared contract and operation, with the host's compile in front. */
92
+ export function registerAddObject(server, bridge, host) {
93
+ server.registerTool(addObjectTool.name, {
94
+ title: addObjectTool.title,
95
+ description: addObjectTool.description,
96
+ inputSchema: z.object(addObjectTool.input),
97
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
98
+ }, async (input) => {
99
+ const brief = briefInForce(bridge);
100
+ if (brief !== undefined) {
101
+ const refusal = furnishingRefusal(addObjectTool.name, input, brief);
102
+ if (refusal)
103
+ return refusalResult(refusal);
104
+ }
105
+ if (!host) {
106
+ return toolError('This Pascal server cannot run geometry scripts.', {
107
+ code: 'scripts_unavailable',
108
+ });
109
+ }
110
+ const scene = bridge.getActiveScene();
111
+ if (!scene) {
112
+ return toolError('Open or save a scene first: authored objects are stored with a scene.', {
113
+ code: 'no_active_scene',
114
+ });
115
+ }
116
+ const args = input;
117
+ let compiled;
118
+ try {
119
+ requireAddObjectReason(args);
120
+ compiled = await compileObject(host, scene.id, bridge, args);
121
+ }
122
+ catch (error) {
123
+ if (isAgentRefusal(error))
124
+ return refusalResult(error);
125
+ return toolError(`${error instanceof Error ? error.message : String(error)} ${CATALOG_SOURCE}`, { code: 'script_failed' });
126
+ }
127
+ // A copy: the hosted bridge writes its map in place, and a "before" that grows with the call
128
+ // reads every creation as unchanged.
129
+ const before = { ...bridge.getNodes() };
130
+ let outcome;
131
+ try {
132
+ outcome = addObject(before, { ...args, compiled }, { activeLevelId: bridge.activeLevelId?.() ?? null });
133
+ }
134
+ catch (error) {
135
+ return refusalResult(error);
136
+ }
137
+ const patches = outcome.changes ? toPatches(outcome.changes) : [];
138
+ if (patches.length)
139
+ bridge.applyPatch(patches);
140
+ const payload = {
141
+ ...outcome.result,
142
+ // What the scene holds now, as every write answers.
143
+ achieved: achievedChanges(before, outcome.changes ?? {}),
144
+ ...persistencePayload(await publishLiveSceneSnapshot(bridge, addObjectTool.name)),
145
+ };
146
+ return {
147
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
148
+ structuredContent: payload,
149
+ };
150
+ });
151
+ }
152
+ function refuseMissingCode() {
153
+ refuse('code_required', 'Pass code to build a new object; params alone rebuild an existing one (nodeId).');
154
+ }
155
+ /** `get_source` on the MCP: the object's module text, read back through the host's store. */
156
+ export function registerGetSource(server, bridge, host) {
157
+ server.registerTool(getSourceTool.name, {
158
+ title: getSourceTool.title,
159
+ description: getSourceTool.description,
160
+ inputSchema: z.object(getSourceTool.input),
161
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
162
+ }, async ({ nodeId, face }) => {
163
+ if (!host) {
164
+ return toolError('This Pascal server cannot read geometry scripts.', {
165
+ code: 'scripts_unavailable',
166
+ });
167
+ }
168
+ const scene = bridge.getActiveScene();
169
+ if (!scene)
170
+ return toolError('Open a scene first.', { code: 'no_active_scene' });
171
+ try {
172
+ const node = readableScript(bridge.getNodes(), nodeId, face);
173
+ const payload = readSourceResult(node, await scriptText(host, scene.id, node));
174
+ return {
175
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
176
+ structuredContent: payload,
177
+ };
178
+ }
179
+ catch (error) {
180
+ if (isAgentRefusal(error))
181
+ return refusalResult(error);
182
+ return toolError(error instanceof Error ? error.message : String(error), {
183
+ code: 'script_unreadable',
184
+ });
185
+ }
186
+ });
187
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/tools/annotations.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B;;;;;CAK7B,CAAA;AAEV,eAAO,MAAM,qCAAqC;;;;;CAKxC,CAAA;AAEV,eAAO,MAAM,yBAAyB;;;;CAI5B,CAAA;AAEV,eAAO,MAAM,4BAA4B;;;;CAI/B,CAAA;AAEV,eAAO,MAAM,uCAAuC;;;;CAI1C,CAAA"}
1
+ {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/tools/annotations.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B;aACrC,YAAY;aACZ,eAAe;aACf,cAAc;aACd,aAAa;CACL,CAAA;AAEV,eAAO,MAAM,qCAAqC;aAChD,YAAY;aACZ,eAAe;aACf,cAAc;aACd,aAAa;CACL,CAAA;AAEV,eAAO,MAAM,yBAAyB;aACpC,YAAY;aACZ,eAAe;aACf,aAAa;CACL,CAAA;AAEV,eAAO,MAAM,4BAA4B;aACvC,YAAY;aACZ,eAAe;aACf,aAAa;CACL,CAAA;AAEV,eAAO,MAAM,uCAAuC;aAClD,YAAY;aACZ,eAAe;aACf,aAAa;CACL,CAAA"}
@@ -1,6 +1,9 @@
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
+ import './honest-patch-guard.js';
5
+ import './level-height-guard.js';
6
+ import './roof-level-guard.js';
4
7
  export declare const applyPatchInput: {
5
8
  patches: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
6
9
  op: z.ZodLiteral<"create">;
@@ -17,16 +20,20 @@ export declare const applyPatchInput: {
17
20
  }, z.core.$strip>], "op">>;
18
21
  };
19
22
  export declare const applyPatchOutput: {
23
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
24
+ unsaved: z.ZodOptional<z.ZodString>;
20
25
  persistence: z.ZodOptional<z.ZodObject<{
21
26
  status: z.ZodEnum<{
22
- unbound: "unbound";
23
27
  events_unsupported: "events_unsupported";
28
+ unbound: "unbound";
24
29
  }>;
25
30
  warning: z.ZodString;
26
31
  }, z.core.$strip>>;
27
32
  appliedOps: z.ZodNumber;
28
33
  deletedIds: z.ZodArray<z.ZodString>;
29
34
  createdIds: z.ZodArray<z.ZodString>;
35
+ /** What the server changed in the patch before writing it, one line each. */
36
+ notes: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
37
  };
31
38
  export declare function registerApplyPatch(server: McpServer, bridge: SceneOperations): void;
32
39
  //# sourceMappingURL=apply-patch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apply-patch.d.ts","sourceRoot":"","sources":["../../src/tools/apply-patch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;CAE3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;CAK5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqDnF"}
1
+ {"version":3,"file":"apply-patch.d.ts","sourceRoot":"","sources":["../../src/tools/apply-patch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAG7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,OAAO,sBAAsB,CAAA;AAC7B,OAAO,sBAAsB,CAAA;AAC7B,OAAO,oBAAoB,CAAA;AAK3B,eAAO,MAAM,eAAe;IAC1B,OAAO;;;;;;;;;;;;;CACR,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;IAI3B,6EAA6E;;CAG9E,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgFnF"}
@@ -1,7 +1,12 @@
1
+ import { isAgentRefusal } from '@pascal-app/core/agent-tools';
1
2
  import { z } from 'zod';
2
3
  import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
3
- import { ErrorCode, throwMcpError } from './errors.js';
4
+ import { ProtocolError, ProtocolErrorCode, refusalResult, throwMcpError } from './errors.js';
5
+ import './honest-patch-guard.js';
6
+ import './level-height-guard.js';
7
+ import './roof-level-guard.js';
4
8
  import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
9
+ import { assertPatchKeepsIdentity, PatchRefusedError, runPatchGuards } from './patch-guards.js';
5
10
  import { PatchSchema } from './schemas.js';
6
11
  export const applyPatchInput = {
7
12
  patches: z.array(PatchSchema),
@@ -10,14 +15,16 @@ export const applyPatchOutput = {
10
15
  appliedOps: z.number(),
11
16
  deletedIds: z.array(z.string()),
12
17
  createdIds: z.array(z.string()),
18
+ /** What the server changed in the patch before writing it, one line each. */
19
+ notes: z.array(z.string()).optional(),
13
20
  ...liveSyncOutput,
14
21
  };
15
22
  export function registerApplyPatch(server, bridge) {
16
23
  server.registerTool('apply_patch', {
17
24
  title: 'Apply patch',
18
- description: 'Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls.',
19
- inputSchema: applyPatchInput,
20
- outputSchema: applyPatchOutput,
25
+ description: "Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.",
26
+ inputSchema: z.object(applyPatchInput),
27
+ outputSchema: z.object(applyPatchOutput),
21
28
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
22
29
  }, async ({ patches }) => {
23
30
  const bridgePatches = patches.map((p) => {
@@ -42,12 +49,17 @@ export function registerApplyPatch(server, bridge) {
42
49
  };
43
50
  });
44
51
  try {
52
+ const notes = runPatchGuards(bridgePatches, bridge.getNodes());
53
+ const planDeletion = bridge
54
+ .planDeletion;
55
+ assertPatchKeepsIdentity(bridgePatches, bridge.getNodes(), bridge.getRootNodeIds(), typeof planDeletion === 'function' ? planDeletion.bind(bridge) : undefined);
45
56
  const result = bridge.applyPatch(bridgePatches);
46
57
  const persistence = await publishLiveSceneSnapshot(bridge, 'apply_patch');
47
58
  const payload = {
48
59
  appliedOps: result.appliedOps,
49
60
  deletedIds: result.deletedIds,
50
61
  createdIds: result.createdIds,
62
+ ...(notes.length ? { notes } : {}),
51
63
  ...persistencePayload(persistence),
52
64
  };
53
65
  return {
@@ -56,8 +68,27 @@ export function registerApplyPatch(server, bridge) {
56
68
  };
57
69
  }
58
70
  catch (err) {
71
+ if (err instanceof PatchRefusedError) {
72
+ // A tool error, not an ProtocolError: the SDK keeps only an ProtocolError's
73
+ // message, and clients need the code, index and id to act on it.
74
+ const refusal = {
75
+ code: err.code,
76
+ patchIndex: err.patchIndex,
77
+ id: err.nodeId,
78
+ message: err.message,
79
+ };
80
+ return {
81
+ content: [{ type: 'text', text: JSON.stringify(refusal) }],
82
+ isError: true,
83
+ };
84
+ }
85
+ // A refusal the store made (an accidental wipe) answers with its code, as the shared tools'.
86
+ if (isAgentRefusal(err))
87
+ return refusalResult(err);
88
+ if (err instanceof ProtocolError)
89
+ throw err;
59
90
  const msg = err instanceof Error ? err.message : String(err);
60
- throwMcpError(ErrorCode.InvalidParams, msg);
91
+ throwMcpError(ProtocolErrorCode.InvalidParams, msg);
61
92
  }
62
93
  });
63
94
  }
@@ -8,9 +8,13 @@ import type { AssetInput } from '@pascal-app/core/schema';
8
8
  * common AI-generated residential layouts.
9
9
  */
10
10
  export declare const MCP_CATALOG_ITEMS: AssetInput[];
11
- export declare function findCatalogItem(id: string): AssetInput | undefined;
12
- export declare function searchCatalogItems(args: {
13
- query: string;
14
- category?: string | undefined;
15
- }): AssetInput[];
11
+ /**
12
+ * The items search_assets, place_items and furnish_room draw from. A host passes its own (the hosted
13
+ * app: its published library, as the chat searches); a standalone server keeps the built-in list.
14
+ */
15
+ export type AssetCatalog = () => Promise<readonly AssetInput[]>;
16
+ export declare const builtInCatalog: AssetCatalog;
17
+ /** A host's catalog, read once by the first call that needs it; a failed read is tried again. */
18
+ export declare function cachedCatalog(load: AssetCatalog): AssetCatalog;
19
+ export declare function findCatalogItem(items: readonly AssetInput[], id: string): AssetInput | undefined;
16
20
  //# sourceMappingURL=asset-catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"asset-catalog.d.ts","sourceRoot":"","sources":["../../src/tools/asset-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAAU,EA6RzC,CAAA;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAElE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B,GAAG,UAAU,EAAE,CAUf"}
1
+ {"version":3,"file":"asset-catalog.d.ts","sourceRoot":"","sources":["../../src/tools/asset-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAAU,EAoTzC,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAA;AAE/D,eAAO,MAAM,cAAc,EAAE,YAA4C,CAAA;AAEzE,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAO9D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEhG"}
@@ -10,6 +10,7 @@ export const MCP_CATALOG_ITEMS = [
10
10
  {
11
11
  id: 'double-bed',
12
12
  category: 'furniture',
13
+ functionTags: ['double-beds'],
13
14
  tags: ['floor', 'bedroom'],
14
15
  name: 'Double Bed',
15
16
  thumbnail: '/items/double-bed/thumbnail.webp',
@@ -22,6 +23,7 @@ export const MCP_CATALOG_ITEMS = [
22
23
  {
23
24
  id: 'single-bed',
24
25
  category: 'furniture',
26
+ functionTags: ['single-beds'],
25
27
  tags: ['floor', 'bedroom'],
26
28
  name: 'Single Bed',
27
29
  thumbnail: '/items/single-bed/thumbnail.webp',
@@ -34,6 +36,7 @@ export const MCP_CATALOG_ITEMS = [
34
36
  {
35
37
  id: 'bedside-table',
36
38
  category: 'furniture',
39
+ functionTags: ['coffee-tables'],
37
40
  tags: ['floor', 'bedroom'],
38
41
  name: 'Bedside Table',
39
42
  thumbnail: '/items/bedside-table/thumbnail.webp',
@@ -47,6 +50,7 @@ export const MCP_CATALOG_ITEMS = [
47
50
  {
48
51
  id: 'dresser',
49
52
  category: 'furniture',
53
+ functionTags: ['dressers'],
50
54
  tags: ['floor', 'storage', 'bedroom'],
51
55
  name: 'Dresser',
52
56
  thumbnail: '/items/dresser/thumbnail.webp',
@@ -60,6 +64,7 @@ export const MCP_CATALOG_ITEMS = [
60
64
  {
61
65
  id: 'closet',
62
66
  category: 'furniture',
67
+ functionTags: ['wardrobes'],
63
68
  tags: ['floor', 'storage', 'bedroom'],
64
69
  name: 'Closet',
65
70
  thumbnail: '/items/closet/thumbnail.webp',
@@ -72,6 +77,7 @@ export const MCP_CATALOG_ITEMS = [
72
77
  {
73
78
  id: 'sofa',
74
79
  category: 'furniture',
80
+ functionTags: ['sofas'],
75
81
  tags: ['floor', 'seating', 'living'],
76
82
  name: 'Sofa',
77
83
  thumbnail: '/items/sofa/thumbnail.webp',
@@ -84,6 +90,7 @@ export const MCP_CATALOG_ITEMS = [
84
90
  {
85
91
  id: 'livingroom-chair',
86
92
  category: 'furniture',
93
+ functionTags: ['armchairs'],
87
94
  tags: ['floor', 'seating', 'living'],
88
95
  name: 'Livingroom Chair',
89
96
  thumbnail: '/items/livingroom-chair/thumbnail.webp',
@@ -96,6 +103,7 @@ export const MCP_CATALOG_ITEMS = [
96
103
  {
97
104
  id: 'coffee-table',
98
105
  category: 'furniture',
106
+ functionTags: ['coffee-tables'],
99
107
  tags: ['floor', 'table', 'living'],
100
108
  name: 'Coffee Table',
101
109
  thumbnail: '/items/coffee-table/thumbnail.webp',
@@ -109,6 +117,7 @@ export const MCP_CATALOG_ITEMS = [
109
117
  {
110
118
  id: 'tv-stand',
111
119
  category: 'furniture',
120
+ functionTags: ['cabinets'],
112
121
  tags: ['floor', 'storage', 'living'],
113
122
  name: 'TV Stand',
114
123
  thumbnail: '/items/tv-stand/thumbnail.webp',
@@ -122,6 +131,7 @@ export const MCP_CATALOG_ITEMS = [
122
131
  {
123
132
  id: 'shelf',
124
133
  category: 'furniture',
134
+ functionTags: ['shelves'],
125
135
  tags: ['wall', 'storage'],
126
136
  name: 'Shelf',
127
137
  thumbnail: '/items/shelf/thumbnail.webp',
@@ -136,6 +146,7 @@ export const MCP_CATALOG_ITEMS = [
136
146
  {
137
147
  id: 'dining-table',
138
148
  category: 'furniture',
149
+ functionTags: ['dining-tables'],
139
150
  tags: ['floor', 'table', 'dining'],
140
151
  name: 'Dining Table',
141
152
  thumbnail: '/items/dining-table/thumbnail.webp',
@@ -149,6 +160,7 @@ export const MCP_CATALOG_ITEMS = [
149
160
  {
150
161
  id: 'dining-chair',
151
162
  category: 'furniture',
163
+ functionTags: ['dining-chairs'],
152
164
  tags: ['floor', 'seating', 'dining'],
153
165
  name: 'Dining Chair',
154
166
  thumbnail: '/items/dining-chair/thumbnail.webp',
@@ -161,6 +173,7 @@ export const MCP_CATALOG_ITEMS = [
161
173
  {
162
174
  id: 'kitchen',
163
175
  category: 'kitchen',
176
+ functionTags: ['cabinets'],
164
177
  tags: ['floor', 'large', 'kitchen'],
165
178
  name: 'Kitchen',
166
179
  thumbnail: '/items/kitchen/thumbnail.webp',
@@ -173,6 +186,7 @@ export const MCP_CATALOG_ITEMS = [
173
186
  {
174
187
  id: 'kitchen-counter',
175
188
  category: 'kitchen',
189
+ functionTags: ['counters'],
176
190
  tags: ['floor', 'large', 'storage', 'kitchen'],
177
191
  name: 'Kitchen Counter',
178
192
  thumbnail: '/items/kitchen-counter/thumbnail.webp',
@@ -186,6 +200,7 @@ export const MCP_CATALOG_ITEMS = [
186
200
  {
187
201
  id: 'stove',
188
202
  category: 'kitchen',
203
+ functionTags: ['kitchen-appliances'],
189
204
  tags: ['floor', 'large', 'kitchen'],
190
205
  name: 'Stove',
191
206
  thumbnail: '/items/stove/thumbnail.webp',
@@ -198,6 +213,7 @@ export const MCP_CATALOG_ITEMS = [
198
213
  {
199
214
  id: 'fridge',
200
215
  category: 'kitchen',
216
+ functionTags: ['kitchen-appliances'],
201
217
  tags: ['floor', 'large', 'kitchen'],
202
218
  name: 'Fridge',
203
219
  thumbnail: '/items/fridge/thumbnail.webp',
@@ -210,6 +226,7 @@ export const MCP_CATALOG_ITEMS = [
210
226
  {
211
227
  id: 'toilet',
212
228
  category: 'bathroom',
229
+ functionTags: ['toilets'],
213
230
  tags: ['floor', 'large', 'bathroom'],
214
231
  name: 'Toilet',
215
232
  thumbnail: '/items/toilet/thumbnail.webp',
@@ -222,6 +239,7 @@ export const MCP_CATALOG_ITEMS = [
222
239
  {
223
240
  id: 'bathroom-sink',
224
241
  category: 'bathroom',
242
+ functionTags: ['sinks'],
225
243
  tags: ['floor', 'large', 'bathroom'],
226
244
  name: 'Bathroom Sink',
227
245
  thumbnail: '/items/bathroom-sink/thumbnail.webp',
@@ -234,6 +252,7 @@ export const MCP_CATALOG_ITEMS = [
234
252
  {
235
253
  id: 'shower-square',
236
254
  category: 'bathroom',
255
+ functionTags: ['showers'],
237
256
  tags: ['floor', 'large', 'bathroom'],
238
257
  name: 'Squared Shower',
239
258
  thumbnail: '/items/shower-square/thumbnail.webp',
@@ -246,6 +265,7 @@ export const MCP_CATALOG_ITEMS = [
246
265
  {
247
266
  id: 'bathtub',
248
267
  category: 'bathroom',
268
+ functionTags: ['bathtubs'],
249
269
  tags: ['floor', 'large', 'bathroom'],
250
270
  name: 'Bathtub',
251
271
  thumbnail: '/items/bathtub/thumbnail.webp',
@@ -258,6 +278,7 @@ export const MCP_CATALOG_ITEMS = [
258
278
  {
259
279
  id: 'washing-machine',
260
280
  category: 'bathroom',
281
+ functionTags: ['laundry-appliances'],
261
282
  tags: ['floor', 'large', 'electronics', 'laundry'],
262
283
  name: 'Washing Machine',
263
284
  thumbnail: '/items/washing-machine/thumbnail.webp',
@@ -270,6 +291,7 @@ export const MCP_CATALOG_ITEMS = [
270
291
  {
271
292
  id: 'drying-rack',
272
293
  category: 'bathroom',
294
+ functionTags: ['laundry-appliances'],
273
295
  tags: ['floor', 'laundry'],
274
296
  name: 'Drying Rack',
275
297
  thumbnail: '/items/drying-rack/thumbnail.webp',
@@ -282,6 +304,7 @@ export const MCP_CATALOG_ITEMS = [
282
304
  {
283
305
  id: 'coat-rack',
284
306
  category: 'furniture',
307
+ functionTags: ['racks'],
285
308
  tags: ['floor', 'storage', 'entry'],
286
309
  name: 'Coat Rack',
287
310
  thumbnail: '/items/coat-rack/thumbnail.webp',
@@ -292,17 +315,15 @@ export const MCP_CATALOG_ITEMS = [
292
315
  scale: [1, 1, 1],
293
316
  },
294
317
  ];
295
- export function findCatalogItem(id) {
296
- return MCP_CATALOG_ITEMS.find((item) => item.id === id);
318
+ export const builtInCatalog = async () => MCP_CATALOG_ITEMS;
319
+ /** A host's catalog, read once by the first call that needs it; a failed read is tried again. */
320
+ export function cachedCatalog(load) {
321
+ let items = null;
322
+ return () => (items ??= load().catch((error) => {
323
+ items = null;
324
+ throw error;
325
+ }));
297
326
  }
298
- export function searchCatalogItems(args) {
299
- const terms = args.query.trim().toLowerCase().split(/\s+/).filter(Boolean);
300
- return MCP_CATALOG_ITEMS.filter((item) => {
301
- if (args.category && item.category !== args.category)
302
- return false;
303
- const haystack = [item.id, item.name, item.category, ...(item.tags ?? [])]
304
- .join(' ')
305
- .toLowerCase();
306
- return terms.every((term) => haystack.includes(term));
307
- });
327
+ export function findCatalogItem(items, id) {
328
+ return items.find((item) => item.id === id);
308
329
  }
@@ -0,0 +1,10 @@
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
+ import type { SceneOperations } from '../operations/index.js';
3
+ import { type GeometryScriptHost } from './add-object.js';
4
+ /**
5
+ * `author_scene` on the MCP: every item's module compiled and stored through the host, the
6
+ * module and the adapter kept as the scene's provenance, the operation applied, Pascal's checks
7
+ * run, and the gate report answered. Replay is the same payload through the same operation.
8
+ */
9
+ export declare function registerAuthorScene(server: McpServer, bridge: SceneOperations, host: GeometryScriptHost | undefined): void;
10
+ //# sourceMappingURL=author-scene.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author-scene.d.ts","sourceRoot":"","sources":["../../src/tools/author-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAe7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAevE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,kBAAkB,GAAG,SAAS,GACnC,IAAI,CA0HN"}