@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 +1 @@
1
- {"version":3,"file":"create-project.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/create-project.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,kBAAkB;;;;CAI9B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;CAsB/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAiD1F"}
1
+ {"version":3,"file":"create-project.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/create-project.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,kBAAkB;;;;CAI9B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;CAsB/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA4E1F"}
@@ -42,6 +42,7 @@ export function registerCreateProject(server, operations) {
42
42
  throwMcpError(ErrorCode.InvalidRequest, 'create_project_unavailable: this MCP store cannot create hosted projects');
43
43
  }
44
44
  try {
45
+ const bound = operations.getActiveScene();
45
46
  const status = await operations.createProject({
46
47
  name,
47
48
  ...(id !== undefined ? { id } : {}),
@@ -55,8 +56,32 @@ export function registerCreateProject(server, operations) {
55
56
  thumbnailUrl: status.thumbnailUrl,
56
57
  version: status.version,
57
58
  });
59
+ // A session bound to another project starts the new one empty: carried over, that
60
+ // project's scene was saved into the new one. Work bound to no project, a saved scene of
61
+ // none included, is kept: it is what the new project is for. Emptied once bound to the new
62
+ // project, so nothing that follows the scene can write the empty scene to the old one.
63
+ if (bound?.projectId && bound.projectId !== status.projectId) {
64
+ operations.loadJSON({ nodes: {}, rootNodeIds: [] });
65
+ operations.loadDefault();
66
+ operations.clearHistory();
67
+ }
68
+ // A scene from the first call: its first draft is what this session holds (the empty
69
+ // scene, or the unbound work it was made for), so any session can load it at once. Unsaved,
70
+ // another session's load_scene answered scene_not_found until a first save.
71
+ operations.setActiveScene(await operations.saveScene({
72
+ id: status.id,
73
+ name: status.name,
74
+ projectId: status.projectId,
75
+ ownerId: status.ownerId,
76
+ thumbnailUrl: status.thumbnailUrl,
77
+ graph: operations.exportSceneGraph(),
78
+ saveMode: 'draft',
79
+ publish: false,
80
+ operation: 'create_project',
81
+ }));
82
+ const saved = (await operations.getProjectStatus(status.id)) ?? status;
58
83
  const payload = {
59
- ...projectStatusPayload(status, 'The project is now bound to this MCP session. Open editorUrl now; semantic tools will update the browser-visible draft. Call save_scene with saveMode: "checkpoint" only when you want a meaningful version.'),
84
+ ...projectStatusPayload(saved, 'The project is now bound to this MCP session. Open editorUrl now; semantic tools will update the browser-visible draft. Call save_scene with saveMode: "checkpoint" only when you want a meaningful version.'),
60
85
  ...currentLevelContext(operations),
61
86
  };
62
87
  return {
@@ -7,6 +7,8 @@ export declare const deleteSceneInput: {
7
7
  };
8
8
  export declare const deleteSceneOutput: {
9
9
  deleted: z.ZodBoolean;
10
+ hidden: z.ZodBoolean;
11
+ message: z.ZodOptional<z.ZodString>;
10
12
  };
11
13
  export declare function registerDeleteScene(server: McpServer, operations: SceneOperations): void;
12
14
  //# sourceMappingURL=delete-scene.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/delete-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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAoCxF"}
1
+ {"version":3,"file":"delete-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/delete-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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;CAI7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAmCxF"}
@@ -8,20 +8,21 @@ export const deleteSceneInput = {
8
8
  };
9
9
  export const deleteSceneOutput = {
10
10
  deleted: z.boolean(),
11
+ hidden: z.boolean(),
12
+ message: z.string().optional(),
11
13
  };
12
14
  export function registerDeleteScene(server, operations) {
13
15
  server.registerTool('delete_scene', {
14
16
  title: 'Delete scene',
15
- description: 'Delete a scene from the SceneStore by id. Optionally pass `expectedVersion` for optimistic concurrency.',
17
+ description: "Remove a stored scene by id. In Pascal this never deletes the project: it hides it from Pascal's public listings until it is next saved with content, and answers `deleted: false, hidden: true` with a message; its owner can delete it from the projects page. Only someone who can manage the project may hide it. A local scene store deletes the scene. Optionally pass `expectedVersion` for optimistic concurrency.",
16
18
  inputSchema: deleteSceneInput,
17
19
  outputSchema: deleteSceneOutput,
18
20
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
19
21
  }, async ({ id, expectedVersion }) => {
20
22
  try {
21
- const deleted = await operations.deleteStoredScene(id, {
23
+ const payload = await operations.deleteStoredScene(id, {
22
24
  ...(expectedVersion !== undefined ? { expectedVersion } : {}),
23
25
  });
24
- const payload = { deleted };
25
26
  return {
26
27
  content: [{ type: 'text', text: JSON.stringify(payload) }],
27
28
  structuredContent: payload,
@@ -6,6 +6,7 @@ export declare const saveSceneInput: {
6
6
  name: z.ZodString;
7
7
  projectId: z.ZodOptional<z.ZodString>;
8
8
  expectedVersion: z.ZodOptional<z.ZodNumber>;
9
+ replace: z.ZodOptional<z.ZodBoolean>;
9
10
  saveMode: z.ZodDefault<z.ZodEnum<{
10
11
  draft: "draft";
11
12
  checkpoint: "checkpoint";
@@ -1 +1 @@
1
- {"version":3,"file":"save-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/save-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,cAAc;;;;;;;;;;;;;CA0B1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAkGlF"}
1
+ {"version":3,"file":"save-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/save-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAIxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;CAkC1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAA;AAgCD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgHlF"}
@@ -1,3 +1,4 @@
1
+ import { migrateLegacyWallAssemblies } from '@pascal-app/core/scene-migrations';
1
2
  import { AnyNode } from '@pascal-app/core/schema';
2
3
  import { z } from 'zod';
3
4
  import { SceneVersionConflictError } from '../../storage/types.js';
@@ -10,14 +11,18 @@ export const saveSceneInput = {
10
11
  name: z.string().min(1).max(200),
11
12
  projectId: z.string().optional(),
12
13
  expectedVersion: z.number().int().positive().optional(),
14
+ replace: z
15
+ .boolean()
16
+ .optional()
17
+ .describe("Write this session's scene over a project or scene it was not loaded from, replacing what that one holds."),
13
18
  saveMode: z
14
19
  .enum(['draft', 'checkpoint'])
15
20
  .default('draft')
16
- .describe('`draft` updates the browser-visible working model without polluting version history. `checkpoint` creates a meaningful saved version.'),
21
+ .describe('`draft` updates the browser-visible working model without polluting version history. `checkpoint` creates a meaningful saved version; it does not publish.'),
17
22
  publish: z
18
23
  .boolean()
19
24
  .optional()
20
- .describe('For checkpoint saves, publish the checkpoint as the browser-visible version.'),
25
+ .describe("Hosted Pascal: also publish this checkpoint, making it the version the project's viewers see. Only when the user asks to publish."),
21
26
  thumbnail: z.string().url().optional(),
22
27
  includeCurrentScene: z
23
28
  .boolean()
@@ -48,15 +53,49 @@ export const saveSceneOutput = {
48
53
  levelIds: z.array(z.string()),
49
54
  defaultLevelId: z.string().nullable(),
50
55
  };
56
+ /** A saved scene that is more than the default site, building and level. */
57
+ const HOLDS_CONTENT = 3;
58
+ /**
59
+ * The session's scene goes over a project or scene only when it was loaded from it (or created
60
+ * for it). After a server reload a session starts over on a blank scene, and saving it by id wrote
61
+ * it over the project's draft (2026-10-03: 8 levels and 10 imported plans lost).
62
+ */
63
+ async function requireSceneLoadedFrom(bridge, target) {
64
+ if (target.id === undefined && target.projectId === undefined)
65
+ return;
66
+ const active = bridge.getActiveScene();
67
+ if (target.id !== undefined ? active?.id === target.id : active?.projectId === target.projectId)
68
+ return;
69
+ const nodeCount = target.projectId !== undefined && bridge.canGetProjectStatus
70
+ ? ((await bridge.getProjectStatus(target.projectId))?.nodeCount ?? 0)
71
+ : target.id !== undefined
72
+ ? Object.keys((await bridge.loadStoredScene(target.id))?.graph.nodes ?? {}).length
73
+ : 0;
74
+ if (nodeCount <= HOLDS_CONTENT)
75
+ return;
76
+ throwMcpError(ErrorCode.InvalidRequest, `scene_not_loaded: this session's scene was not loaded from ${target.projectId ?? target.id}, which holds ${nodeCount} nodes — the server may have reloaded and this session started over. Call load_scene with it first, or pass replace: true to write this scene over it.`, { ...target, nodeCount });
77
+ }
51
78
  export function registerSaveScene(server, bridge) {
52
79
  server.registerTool('save_scene', {
53
80
  title: 'Save scene',
54
- description: 'Save the current scene (or a provided graph) to the SceneStore. Defaults to a browser-visible draft save so agents can iterate without creating many project versions. Use saveMode: "checkpoint" for meaningful version history.',
81
+ description: 'Save the current scene (or a provided graph) to the SceneStore. Without id or projectId it saves to the project this session is bound to (create_project or load_scene). Defaults to a browser-visible draft save so agents can iterate without creating many project versions. Use saveMode: "checkpoint" for meaningful version history.',
55
82
  inputSchema: saveSceneInput,
56
83
  outputSchema: saveSceneOutput,
57
84
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
58
- }, async ({ id, name, projectId, expectedVersion, saveMode, publish, thumbnail, includeCurrentScene, graph, }) => {
85
+ }, async ({ id, name, projectId, expectedVersion, saveMode, publish, thumbnail, includeCurrentScene, graph, replace, }) => {
86
+ // Without a target, the session's scene goes where it came from (create_project, load_scene,
87
+ // an earlier save). An agent was refused "Call create_project first" on its first save, right
88
+ // after create_project bound the session to its project.
89
+ const bound = includeCurrentScene && id === undefined && projectId === undefined
90
+ ? bridge.getActiveScene()
91
+ : null;
92
+ if (bound) {
93
+ id = bound.id;
94
+ projectId = bound.projectId ?? undefined;
95
+ }
59
96
  let sceneGraph;
97
+ if (includeCurrentScene && !replace)
98
+ await requireSceneLoadedFrom(bridge, { id, projectId });
60
99
  if (includeCurrentScene) {
61
100
  const validation = bridge.validateScene();
62
101
  if (!validation.valid) {
@@ -75,8 +114,9 @@ export function registerSaveScene(server, bridge) {
75
114
  if (!rawNodes || typeof rawNodes !== 'object') {
76
115
  throwMcpError(ErrorCode.InvalidParams, 'graph.nodes must be an object');
77
116
  }
117
+ const migration = migrateLegacyWallAssemblies(rawNodes);
78
118
  const errors = [];
79
- for (const [nodeId, node] of Object.entries(rawNodes)) {
119
+ for (const [nodeId, node] of Object.entries(migration.nodes)) {
80
120
  const res = AnyNode.safeParse(node);
81
121
  if (!res.success) {
82
122
  for (const issue of res.error.issues) {
@@ -91,7 +131,7 @@ export function registerSaveScene(server, bridge) {
91
131
  if (errors.length > 0) {
92
132
  throwMcpError(ErrorCode.InvalidParams, 'graph_invalid', { errors });
93
133
  }
94
- sceneGraph = graph;
134
+ sceneGraph = { ...graph, nodes: migration.nodes };
95
135
  }
96
136
  try {
97
137
  const meta = await bridge.saveScene({
@@ -1,6 +1,6 @@
1
1
  import { SceneBridge } from '../../bridge/scene-bridge.js';
2
2
  import { type SceneOperations } from '../../operations/index.js';
3
- import { type ProjectCreateOptions, type ProjectStatus, type SceneEvent, type SceneEventAppendOptions, type SceneEventListOptions, type SceneListOptions, type SceneMeta, type SceneMutateOptions, type SceneSaveOptions, type SceneStore, type SceneWithGraph } from '../../storage/types.js';
3
+ import { type ProjectCreateOptions, type ProjectStatus, type SceneDeleteResult, type SceneEvent, type SceneEventAppendOptions, type SceneEventListOptions, type SceneListOptions, type SceneMeta, type SceneMutateOptions, type SceneSaveOptions, type SceneStore, type SceneWithGraph } from '../../storage/types.js';
4
4
  export type StoredTextContent = {
5
5
  type: string;
6
6
  text: string;
@@ -32,7 +32,7 @@ export declare class InMemorySceneStore implements SceneStore {
32
32
  save(opts: SceneSaveOptions): Promise<SceneMeta>;
33
33
  load(id: string): Promise<SceneWithGraph | null>;
34
34
  list(opts?: SceneListOptions): Promise<SceneMeta[]>;
35
- delete(id: string, opts?: SceneMutateOptions): Promise<boolean>;
35
+ delete(id: string, opts?: SceneMutateOptions): Promise<SceneDeleteResult>;
36
36
  rename(id: string, newName: string, opts?: SceneMutateOptions): Promise<SceneMeta>;
37
37
  appendSceneEvent(opts: SceneEventAppendOptions): Promise<SceneEvent>;
38
38
  listSceneEvents(sceneId: string, opts?: SceneEventListOptions): Promise<SceneEvent[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEf,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAG5B,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAClD,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAC3B,GAAG;IACF,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,kBAAkB,CAAA;IACzB,UAAU,EAAE,eAAe,CAAA;CAC5B,CAKA;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAWtB;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,YAAY,CAAI;IAElB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAejE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK3D,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IA+DhD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAShD,IAAI,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAanD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAW/D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC;IAmBlF,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC;IAapE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAU3F,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,eAAe;CA0BxB"}
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEf,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAG5B,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAClD,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAC3B,GAAG;IACF,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,kBAAkB,CAAA;IACzB,UAAU,EAAE,eAAe,CAAA;CAC5B,CAKA;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAWtB;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,YAAY,CAAI;IAElB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAejE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK3D,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IA+DhD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAShD,IAAI,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAanD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAYzE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC;IAmBlF,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC;IAapE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAU3F,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,eAAe;CA0BxB"}
@@ -130,7 +130,8 @@ export class InMemorySceneStore {
130
130
  if (opts?.expectedVersion !== undefined && rec.version !== opts.expectedVersion) {
131
131
  throw new SceneVersionConflictError(`Expected version ${opts.expectedVersion}, have ${rec.version}`);
132
132
  }
133
- return this.data.delete(id);
133
+ this.data.delete(id);
134
+ return { deleted: true, hidden: false };
134
135
  }
135
136
  async rename(id, newName, opts) {
136
137
  const rec = this.data.get(id);
@@ -6,6 +6,8 @@ export declare const setUnitMembersInput: {
6
6
  memberZoneIds: z.ZodArray<z.ZodString>;
7
7
  };
8
8
  export declare const setUnitMembersOutput: {
9
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
10
+ unsaved: z.ZodOptional<z.ZodString>;
9
11
  persistence: z.ZodOptional<z.ZodObject<{
10
12
  status: z.ZodEnum<{
11
13
  unbound: "unbound";
@@ -1 +1 @@
1
- {"version":3,"file":"set-unit-members.d.ts","sourceRoot":"","sources":["../../src/tools/set-unit-members.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;AAOpD,eAAO,MAAM,mBAAmB;;;CAG/B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;CAIhC,CAAA;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgCvF"}
1
+ {"version":3,"file":"set-unit-members.d.ts","sourceRoot":"","sources":["../../src/tools/set-unit-members.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;AAOpD,eAAO,MAAM,mBAAmB;;;CAG/B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAIhC,CAAA;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgCvF"}
@@ -8,6 +8,8 @@ export declare const setZoneInput: {
8
8
  properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
9
9
  };
10
10
  export declare const setZoneOutput: {
11
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
12
+ unsaved: z.ZodOptional<z.ZodString>;
11
13
  persistence: z.ZodOptional<z.ZodObject<{
12
14
  status: z.ZodEnum<{
13
15
  unbound: "unbound";
@@ -1 +1 @@
1
- {"version":3,"file":"set-zone.d.ts","sourceRoot":"","sources":["../../src/tools/set-zone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,YAAY;;;;;CAKxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;;CAGzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDhF"}
1
+ {"version":3,"file":"set-zone.d.ts","sourceRoot":"","sources":["../../src/tools/set-zone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,YAAY;;;;;CAKxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;CAGzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDhF"}
@@ -1,4 +1,4 @@
1
- import { ZoneNode } from '@pascal-app/core/schema';
1
+ import { newZone } from '@pascal-app/core';
2
2
  import { z } from 'zod';
3
3
  import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
4
4
  import { ErrorCode, throwMcpError } from './errors.js';
@@ -35,7 +35,7 @@ export function registerSetZone(server, bridge) {
35
35
  parent.metadata.role === 'roof') {
36
36
  throwMcpError(ErrorCode.InvalidParams, `Roof support level ${levelId} is not an occupied story; create zones on an occupied level instead`);
37
37
  }
38
- const zone = ZoneNode.parse({
38
+ const zone = newZone({
39
39
  name: label,
40
40
  polygon: polygon,
41
41
  metadata: properties ?? {},
@@ -0,0 +1,30 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type AgentOperation, type SceneChanges } from '@pascal-app/core/agent-operations';
3
+ import { type AgentToolAnnotations, type AgentToolContract } from '@pascal-app/core/agent-tools';
4
+ import { z } from 'zod';
5
+ import type { Patch } from '../bridge/scene-bridge.js';
6
+ import type { SceneOperations } from '../operations/index.js';
7
+ import { type AssetCatalog } from './asset-catalog.js';
8
+ /**
9
+ * A tool the server runs as a core-style operation: parse with the contract, plan with the
10
+ * operation, apply its changes through the bridge in one history step, answer with what the scene
11
+ * then holds. `annotations` are the host's to choose from its own review of the operation; a
12
+ * plugin contract's `annotations` are only its hints.
13
+ */
14
+ export type OperationTool = {
15
+ contract: AgentToolContract;
16
+ operation: AgentOperation;
17
+ annotations: AgentToolAnnotations;
18
+ outputSchema?: Record<string, z.ZodType>;
19
+ envelope?: (bridge: SceneOperations) => Record<string, unknown>;
20
+ /** Reads the host's item library: only these calls wait for it (the hosted one is a query). */
21
+ catalog?: true;
22
+ };
23
+ export declare function toPatches(changes: SceneChanges): Patch[];
24
+ export declare function registerSharedTools(server: McpServer, bridge: SceneOperations, catalog?: AssetCatalog): void;
25
+ /**
26
+ * Registers one operation tool, as the shared tools are: a host adds a plugin's `'operation'`
27
+ * tools with it, from the plugin's `./agent-tools` contract and `./agent-operations` operation.
28
+ */
29
+ export declare function registerOperationTool(server: McpServer, bridge: SceneOperations, tool: OperationTool, catalog?: AssetCatalog): void;
30
+ //# sourceMappingURL=shared-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-tools.d.ts","sourceRoot":"","sources":["../../src/tools/shared-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAGL,KAAK,cAAc,EAGnB,KAAK,YAAY,EAElB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAsBvB,MAAM,8BAA8B,CAAA;AAOrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,iBAAiB,CAAA;AASnE;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,cAAc,CAAA;IACzB,WAAW,EAAE,oBAAoB,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/D,+FAA+F;IAC/F,OAAO,CAAC,EAAE,IAAI,CAAA;CACf,CAAA;AAmRD,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,EAAE,CAmBxD;AAUD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,YAA6B,GACrC,IAAI,CAGN;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,aAAa,EACnB,OAAO,GAAE,YAA6B,GACrC,IAAI,CAkEN"}