@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
@@ -9,6 +9,11 @@ export declare const validateSceneOutput: {
9
9
  path: z.ZodString;
10
10
  message: z.ZodString;
11
11
  }, z.core.$strip>>;
12
+ warnings: z.ZodArray<z.ZodObject<{
13
+ nodeId: z.ZodString;
14
+ path: z.ZodString;
15
+ message: z.ZodString;
16
+ }, z.core.$strip>>;
12
17
  };
13
18
  export declare function registerValidateScene(server: McpServer, bridge: SceneOperations): void;
14
19
  //# sourceMappingURL=validate-scene.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-scene.d.ts","sourceRoot":"","sources":["../../src/tools/validate-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,eAAO,MAAM,kBAAkB,IAAK,CAAA;AAEpC,eAAO,MAAM,mBAAmB;;;;;;;CAS/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAmBtF"}
1
+ {"version":3,"file":"validate-scene.d.ts","sourceRoot":"","sources":["../../src/tools/validate-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,eAAO,MAAM,kBAAkB,IAAK,CAAA;AAQpC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAI/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAmBtF"}
@@ -1,18 +1,20 @@
1
1
  import { z } from 'zod';
2
2
  import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
3
3
  export const validateSceneInput = {};
4
+ const validationIssue = z.object({
5
+ nodeId: z.string(),
6
+ path: z.string(),
7
+ message: z.string(),
8
+ });
4
9
  export const validateSceneOutput = {
5
10
  valid: z.boolean(),
6
- errors: z.array(z.object({
7
- nodeId: z.string(),
8
- path: z.string(),
9
- message: z.string(),
10
- })),
11
+ errors: z.array(validationIssue),
12
+ warnings: z.array(validationIssue),
11
13
  };
12
14
  export function registerValidateScene(server, bridge) {
13
15
  server.registerTool('validate_scene', {
14
16
  title: 'Validate scene',
15
- description: 'Run Zod validation against every node in the scene. Returns `{ valid, errors }` where each error has `{ nodeId, path, message }`.',
17
+ description: 'Run Zod validation against every node in the scene. Returns `{ valid, errors, warnings }` where each entry has `{ nodeId, path, message }`. Warnings do not fail validation; for example a hidden Site, whose flag hides only its own ground and boundary while the buildings on it stay visible and exported.',
16
18
  inputSchema: validateSceneInput,
17
19
  outputSchema: validateSceneOutput,
18
20
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
@@ -31,7 +31,7 @@ function cloneGraph(graph) {
31
31
  }
32
32
  const WALL_THICKNESS_OPTIONS = [0.1, 0.15, 0.2, 0.25];
33
33
  const WALL_HEIGHT_OPTIONS = [2.4, 2.6, 2.7, 3.0];
34
- const FENCE_STYLES = ['privacy', 'slat', 'rail'];
34
+ const FENCE_STYLES = ['privacy', 'slat', 'rail', 'picket'];
35
35
  /** Fisher–Yates shuffle in place using the provided RNG. */
36
36
  function shuffleInPlace(arr, rng) {
37
37
  for (let i = arr.length - 1; i > 0; i--) {
@@ -0,0 +1,38 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type SceneViewCrop, type SceneViewInterior, type SceneViewPose } from '@pascal-app/core/agent-operations';
3
+ import type { SceneOperations } from '../operations/index.js';
4
+ /** A picture of the scene, rendered by an editor tab open on the project. */
5
+ export type SceneViewCapture = {
6
+ image: Uint8Array;
7
+ mimeType: string;
8
+ width: number;
9
+ height: number;
10
+ /** Which tab rendered it, and when: the scene as that tab showed it then. */
11
+ tab: string;
12
+ capturedAt: string;
13
+ };
14
+ /**
15
+ * Asks an editor open on the project for a picture, the MCP having no renderer of its own. Refuses
16
+ * `editor_tab_required` when none answers.
17
+ */
18
+ export type SceneViewHost = {
19
+ capture(request: {
20
+ projectId: string;
21
+ pose: SceneViewPose;
22
+ size: {
23
+ w: number;
24
+ h: number;
25
+ };
26
+ /** A look inside this floor: the editor leaves the rest out and cuts everything on it. */
27
+ interior?: SceneViewInterior;
28
+ }): Promise<SceneViewCapture>;
29
+ /** Crops the reference photo to a region; without it a view with a photo is refused. */
30
+ crop?(request: SceneViewCrop): Promise<{
31
+ image: Uint8Array;
32
+ mimeType: string;
33
+ width: number;
34
+ height: number;
35
+ }>;
36
+ };
37
+ export declare function registerViewScene(server: McpServer, operations: SceneOperations, views?: SceneViewHost): void;
38
+ //# sourceMappingURL=view-scene.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-scene.d.ts","sourceRoot":"","sources":["../../src/tools/view-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAGnB,MAAM,mCAAmC,CAAA;AAG1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,6EAA6E;AAC7E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,UAAU,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,CAAC,OAAO,EAAE;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,EAAE,aAAa,CAAA;QACnB,IAAI,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QAC9B,0FAA0F;QAC1F,QAAQ,CAAC,EAAE,iBAAiB,CAAA;KAC7B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC7B,wFAAwF;IACxF,IAAI,CAAC,CACH,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnF,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,eAAe,EAC3B,KAAK,CAAC,EAAE,aAAa,QAuEtB"}
@@ -0,0 +1,63 @@
1
+ import { sceneViewNote, sceneViewPlan, } from '@pascal-app/core/agent-operations';
2
+ import { refuse, viewSceneTool } from '@pascal-app/core/agent-tools';
3
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
4
+ import { refusalResult } from './errors.js';
5
+ export function registerViewScene(server, operations, views) {
6
+ server.registerTool(viewSceneTool.name, {
7
+ title: viewSceneTool.title,
8
+ description: viewSceneTool.description,
9
+ inputSchema: viewSceneTool.input,
10
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
11
+ }, async (input) => {
12
+ try {
13
+ if (!views)
14
+ refuse('view_unavailable', 'This Pascal server has no editor to render with: take sizes and counts from the tools.');
15
+ const projectId = operations.getActiveScene()?.projectId;
16
+ if (!projectId)
17
+ refuse('no_project', 'This session has no project open to look at: load or create one.');
18
+ const { pose, size, crop, interior } = sceneViewPlan(operations.getNodes(), input);
19
+ if (crop && !views.crop)
20
+ refuse('photo_crop_unavailable', 'This Pascal server cannot crop a photo: crop the region yourself and lay it beside the view.');
21
+ const [shot, cropped] = await Promise.all([
22
+ views.capture({ projectId: projectId, pose, size, ...(interior ? { interior } : {}) }),
23
+ crop ? views.crop(crop) : undefined,
24
+ ]);
25
+ const payload = {
26
+ status: 'viewed',
27
+ camera: pose,
28
+ size: { width: shot.width, height: shot.height },
29
+ tab: shot.tab,
30
+ capturedAt: shot.capturedAt,
31
+ note: sceneViewNote(),
32
+ ...(crop && cropped
33
+ ? {
34
+ photoRegion: crop.region,
35
+ photoCropSize: { width: cropped.width, height: cropped.height },
36
+ }
37
+ : {}),
38
+ };
39
+ const image = (data, mimeType) => ({
40
+ type: 'image',
41
+ data: Buffer.from(data).toString('base64'),
42
+ mimeType,
43
+ });
44
+ return {
45
+ content: cropped
46
+ ? [
47
+ { type: 'text', text: JSON.stringify(payload) },
48
+ { type: 'text', text: 'The model, from the view:' },
49
+ image(shot.image, shot.mimeType),
50
+ { type: 'text', text: 'The photo, cropped to the region:' },
51
+ image(cropped.image, cropped.mimeType),
52
+ ]
53
+ : [
54
+ image(shot.image, shot.mimeType),
55
+ { type: 'text', text: JSON.stringify(payload) },
56
+ ],
57
+ };
58
+ }
59
+ catch (error) {
60
+ return refusalResult(error);
61
+ }
62
+ });
63
+ }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,7 @@
1
+ /**
2
+ * The package version, for the server's handshake. Read from package.json
3
+ * at runtime under Node (and Bun) without a static `node:fs` import, so the
4
+ * server entry also bundles for the browser, where the editor runs it in-page.
5
+ */
1
6
  export declare const version: string;
2
7
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,QAMT,CAAA"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,OAAO,QACkE,CAAA"}
package/dist/version.js CHANGED
@@ -1,3 +1,17 @@
1
- import { readFileSync } from 'node:fs';
2
- export const version = process.env.PASCAL_MCP_VERSION ??
3
- JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
1
+ /**
2
+ * The package version, for the server's handshake. Read from package.json
3
+ * at runtime under Node (and Bun) without a static `node:fs` import, so the
4
+ * server entry also bundles for the browser, where the editor runs it in-page.
5
+ */
6
+ export const version = (typeof process !== 'undefined' && process.env?.PASCAL_MCP_VERSION) || readVersion();
7
+ function readVersion() {
8
+ try {
9
+ const fs = globalThis.process?.getBuiltinModule?.('node:fs');
10
+ if (!fs)
11
+ return '0.0.0';
12
+ return JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
13
+ }
14
+ catch {
15
+ return '0.0.0';
16
+ }
17
+ }
@@ -47,7 +47,7 @@ async function main(): Promise<void> {
47
47
 
48
48
  if (levelId) {
49
49
  const created = await client.callTool({
50
- name: 'create_wall',
50
+ name: 'add_wall',
51
51
  arguments: {
52
52
  levelId,
53
53
  start: [0, 0],
@@ -157,36 +157,34 @@ them by name:
157
157
 
158
158
  ### 5. Cut doors and windows
159
159
 
160
- The agent uses `cut_opening` to add entry doors on each interior partition
161
- and windows on the south and east façades:
160
+ The agent uses `add_door` for the entry doors on each interior partition and
161
+ `add_window` for the windows on the south and east façades:
162
162
 
163
163
  ```jsonc
164
- // tool: cut_opening (called once per opening)
164
+ // tool: add_window (called once per window)
165
165
  {
166
- "name": "cut_opening",
166
+ "name": "add_window",
167
167
  "arguments": {
168
168
  "wallId": "wall-south", // perimeter wall [0,0] → [10,0]
169
- "type": "window",
170
- "position": 0.25, // 25% along centerline
169
+ "t": 0.25, // 25% along centerline
171
170
  "width": 1.2,
172
171
  "height": 1.2
173
172
  }
174
173
  }
175
- // → { "openingId": "window-south-1" }
174
+ // → { "windowId": "window-south-1", ... }
176
175
  ```
177
176
 
178
177
  ```jsonc
179
178
  {
180
- "name": "cut_opening",
179
+ "name": "add_door",
181
180
  "arguments": {
182
181
  "wallId": "wall-bed1", // partition wall to Bedroom 1
183
- "type": "door",
184
- "position": 0.4,
182
+ "t": 0.4,
185
183
  "width": 0.9,
186
184
  "height": 2.1
187
185
  }
188
186
  }
189
- // → { "openingId": "door-bed1" }
187
+ // → { "doorId": "door-bed1", ... }
190
188
  ```
191
189
 
192
190
  The agent repeats this for Bedroom 2's door, the bathroom door, and two
@@ -110,7 +110,7 @@ operate on it without reloading:
110
110
  > **User:** add a door on the south wall between Living and Kitchen.
111
111
 
112
112
  The agent calls `find_nodes({ type: "wall" })`, picks the appropriate
113
- wall, and issues `cut_opening` — no extra wiring needed.
113
+ wall, and issues `add_door` — no extra wiring needed.
114
114
 
115
115
  ## Takeaways
116
116
 
@@ -129,16 +129,15 @@ Bedroom polygons from the floorplan rooms.
129
129
 
130
130
  ### 4. Cut the identified openings
131
131
 
132
- For each window the vision tool reported, the agent calls `cut_opening`
132
+ For each window the vision tool reported, the agent calls `add_window`
133
133
  against the corresponding perimeter wall:
134
134
 
135
135
  ```jsonc
136
136
  {
137
- "name": "cut_opening",
137
+ "name": "add_window",
138
138
  "arguments": {
139
139
  "wallId": "wall-south",
140
- "type": "window",
141
- "position": 0.5,
140
+ "t": 0.5,
142
141
  "width": 1.4,
143
142
  "height": 1.5
144
143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Model Context Protocol server for Pascal 3D editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,10 +35,15 @@
35
35
  "types": "./dist/bridge/index.d.ts",
36
36
  "import": "./dist/bridge/index.js",
37
37
  "default": "./dist/bridge/index.js"
38
+ },
39
+ "./tools/*": {
40
+ "types": "./dist/tools/*.d.ts",
41
+ "import": "./dist/tools/*.js",
42
+ "default": "./dist/tools/*.js"
38
43
  }
39
44
  },
40
45
  "bin": {
41
- "pascal-mcp": "./dist/bin/pascal-mcp.js"
46
+ "pascal-mcp": "dist/bin/pascal-mcp.js"
42
47
  },
43
48
  "files": [
44
49
  "dist",
@@ -47,7 +52,7 @@
47
52
  "CHANGELOG.md"
48
53
  ],
49
54
  "scripts": {
50
- "build": "tsc --build && bun ../../scripts/fix-node-esm-imports.ts ../core/dist && bun ../../scripts/fix-node-esm-imports.ts dist",
55
+ "build": "tsc --build && bun ../../scripts/fix-node-esm-imports.ts ../core/dist && bun ../../scripts/fix-node-esm-imports.ts ../geometry-script/dist && bun ../../scripts/fix-node-esm-imports.ts dist",
51
56
  "dev": "tsgo --build --watch",
52
57
  "start": "bun dist/bin/pascal-mcp.js",
53
58
  "test": "bun test",
@@ -56,15 +61,16 @@
56
61
  "prepublishOnly": "bun run build && bun test && bun run smoke:node"
57
62
  },
58
63
  "peerDependencies": {
59
- "@pascal-app/core": "^1.0.1"
64
+ "@pascal-app/core": "^1.0.4"
60
65
  },
61
66
  "dependencies": {
62
67
  "@modelcontextprotocol/sdk": "^1.30.0",
68
+ "@pascal-app/geometry-script": "^1.0.4",
63
69
  "@pascal-app/lingo": "^0.2.0",
64
70
  "zod": ">=4.5.4 <4.6"
65
71
  },
66
72
  "devDependencies": {
67
- "@pascal-app/core": "^1.0.1",
73
+ "@pascal-app/core": "^1.0.4",
68
74
  "@pascal/typescript-config": "*",
69
75
  "@types/node": "^22.19.20",
70
76
  "typescript": "6.0.3"
@@ -80,7 +86,7 @@
80
86
  ],
81
87
  "repository": {
82
88
  "type": "git",
83
- "url": "https://github.com/pascalorg/editor.git",
89
+ "url": "git+https://github.com/pascalorg/editor.git",
84
90
  "directory": "packages/mcp"
85
91
  },
86
92
  "license": "MIT",
@@ -1,21 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { z } from 'zod';
3
- import type { SceneOperations } from '../operations/index.js';
4
- export declare const createLevelInput: {
5
- buildingId: z.ZodString;
6
- elevation: z.ZodOptional<z.ZodNumber>;
7
- height: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
8
- label: z.ZodOptional<z.ZodString>;
9
- };
10
- export declare const createLevelOutput: {
11
- persistence: z.ZodOptional<z.ZodObject<{
12
- status: z.ZodEnum<{
13
- unbound: "unbound";
14
- events_unsupported: "events_unsupported";
15
- }>;
16
- warning: z.ZodString;
17
- }, z.core.$strip>>;
18
- levelId: z.ZodString;
19
- };
20
- export declare function registerCreateLevel(server: McpServer, bridge: SceneOperations): void;
21
- //# sourceMappingURL=create-level.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-level.d.ts","sourceRoot":"","sources":["../../src/tools/create-level.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,eAAe,CAAA;AAOpD,eAAO,MAAM,gBAAgB;;;;;CAW5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;CAG7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgDpF"}
@@ -1,63 +0,0 @@
1
- import { DEFAULT_LEVEL_HEIGHT } from '@pascal-app/core';
2
- import { LevelNode } from '@pascal-app/core/schema';
3
- import { z } from 'zod';
4
- import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
5
- import { ErrorCode, throwMcpError } from './errors.js';
6
- import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
7
- import { measurement } from './measurement.js';
8
- import { NodeIdSchema } from './schemas.js';
9
- export const createLevelInput = {
10
- buildingId: NodeIdSchema,
11
- elevation: z
12
- .number()
13
- .optional()
14
- .describe("Legacy parameter; new levels are appended above the building's current top level."),
15
- height: measurement('length', 'm', {
16
- min: 0,
17
- description: 'Stored floor-to-floor storey height.',
18
- }).optional(),
19
- label: z.string().optional(),
20
- };
21
- export const createLevelOutput = {
22
- levelId: z.string(),
23
- ...liveSyncOutput,
24
- };
25
- export function registerCreateLevel(server, bridge) {
26
- server.registerTool('create_level', {
27
- title: 'Create level',
28
- description: "Append a new level above the given building's current top level. height is stored as the level's floor-to-floor storey height.",
29
- inputSchema: createLevelInput,
30
- outputSchema: createLevelOutput,
31
- annotations: ADDITIVE_TOOL_ANNOTATIONS,
32
- }, async ({ buildingId, height, label }) => {
33
- const parent = bridge.getNode(buildingId);
34
- if (!parent) {
35
- throwMcpError(ErrorCode.InvalidParams, `Building not found: ${buildingId}`);
36
- }
37
- if (parent.type !== 'building') {
38
- throwMcpError(ErrorCode.InvalidParams, `Node ${buildingId} is a ${parent.type}, expected building`);
39
- }
40
- const metadata = {};
41
- if (label !== undefined)
42
- metadata.label = label;
43
- const existingOrdinals = bridge
44
- .getChildren(buildingId)
45
- .filter((node) => node.type === 'level')
46
- .map((node) => node.level);
47
- const nextOrdinal = Math.max(-1, ...existingOrdinals) + 1;
48
- const levelNode = LevelNode.parse({
49
- level: nextOrdinal,
50
- height: height ?? DEFAULT_LEVEL_HEIGHT,
51
- children: [],
52
- ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
53
- ...(label !== undefined ? { name: label } : {}),
54
- });
55
- const id = bridge.createNode(levelNode, buildingId);
56
- const persistence = await publishLiveSceneSnapshot(bridge, 'create_level');
57
- const payload = { levelId: id, ...persistencePayload(persistence) };
58
- return {
59
- content: [{ type: 'text', text: JSON.stringify(payload) }],
60
- structuredContent: payload,
61
- };
62
- });
63
- }
@@ -1,22 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { z } from 'zod';
3
- import type { SceneOperations } from '../operations/index.js';
4
- export declare const createWallInput: {
5
- levelId: z.ZodString;
6
- start: z.ZodArray<z.ZodNumber>;
7
- end: z.ZodArray<z.ZodNumber>;
8
- thickness: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
9
- height: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
10
- };
11
- export declare const createWallOutput: {
12
- persistence: z.ZodOptional<z.ZodObject<{
13
- status: z.ZodEnum<{
14
- unbound: "unbound";
15
- events_unsupported: "events_unsupported";
16
- }>;
17
- warning: z.ZodString;
18
- }, z.core.$strip>>;
19
- wallId: z.ZodString;
20
- };
21
- export declare function registerCreateWall(server: McpServer, bridge: SceneOperations): void;
22
- //# sourceMappingURL=create-wall.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-wall.d.ts","sourceRoot":"","sources":["../../src/tools/create-wall.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;AAOpD,eAAO,MAAM,eAAe;;;;;;CAS3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDnF"}
@@ -1,57 +0,0 @@
1
- import { WallNode } from '@pascal-app/core/schema';
2
- import { z } from 'zod';
3
- import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
4
- import { ErrorCode, throwMcpError } from './errors.js';
5
- import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
6
- import { measurement } from './measurement.js';
7
- import { NodeIdSchema, Vec2Schema } from './schemas.js';
8
- export const createWallInput = {
9
- levelId: NodeIdSchema,
10
- start: Vec2Schema,
11
- end: Vec2Schema,
12
- thickness: measurement('length', 'm', {
13
- positive: true,
14
- description: 'Wall thickness.',
15
- }).optional(),
16
- height: measurement('length', 'm', { positive: true, description: 'Wall height.' }).optional(),
17
- };
18
- export const createWallOutput = {
19
- wallId: z.string(),
20
- ...liveSyncOutput,
21
- };
22
- export function registerCreateWall(server, bridge) {
23
- server.registerTool('create_wall', {
24
- title: 'Create wall',
25
- description: 'Create a new wall on the given level between two 2D points. Thickness and height default to the core library defaults when omitted.',
26
- inputSchema: createWallInput,
27
- outputSchema: createWallOutput,
28
- annotations: ADDITIVE_TOOL_ANNOTATIONS,
29
- }, async ({ levelId, start, end, thickness, height }) => {
30
- const parent = bridge.getNode(levelId);
31
- if (!parent) {
32
- throwMcpError(ErrorCode.InvalidParams, `Level not found: ${levelId}`);
33
- }
34
- if (parent.type !== 'level') {
35
- throwMcpError(ErrorCode.InvalidParams, `Node ${levelId} is a ${parent.type}, expected level`);
36
- }
37
- if (typeof parent.metadata === 'object' &&
38
- parent.metadata !== null &&
39
- 'role' in parent.metadata &&
40
- parent.metadata.role === 'roof') {
41
- throwMcpError(ErrorCode.InvalidParams, `Roof support level ${levelId} is not an occupied story; create walls on an occupied level instead`);
42
- }
43
- const wall = WallNode.parse({
44
- start: start,
45
- end: end,
46
- ...(thickness !== undefined ? { thickness } : {}),
47
- ...(height !== undefined ? { height } : {}),
48
- });
49
- const id = bridge.createNode(wall, levelId);
50
- const persistence = await publishLiveSceneSnapshot(bridge, 'create_wall');
51
- const payload = { wallId: id, ...persistencePayload(persistence) };
52
- return {
53
- content: [{ type: 'text', text: JSON.stringify(payload) }],
54
- structuredContent: payload,
55
- };
56
- });
57
- }
@@ -1,25 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { z } from 'zod';
3
- import type { SceneOperations } from '../operations/index.js';
4
- export declare const cutOpeningInput: {
5
- wallId: z.ZodString;
6
- type: z.ZodEnum<{
7
- door: "door";
8
- window: "window";
9
- }>;
10
- position: z.ZodNumber;
11
- width: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
12
- height: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
13
- };
14
- export declare const cutOpeningOutput: {
15
- persistence: z.ZodOptional<z.ZodObject<{
16
- status: z.ZodEnum<{
17
- unbound: "unbound";
18
- events_unsupported: "events_unsupported";
19
- }>;
20
- warning: z.ZodString;
21
- }, z.core.$strip>>;
22
- openingId: z.ZodString;
23
- };
24
- export declare function registerCutOpening(server: McpServer, bridge: SceneOperations): void;
25
- //# sourceMappingURL=cut-opening.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cut-opening.d.ts","sourceRoot":"","sources":["../../src/tools/cut-opening.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;AAQpD,eAAO,MAAM,eAAe;;;;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0DnF"}
@@ -1,61 +0,0 @@
1
- import { DoorNode, WindowNode } from '@pascal-app/core/schema';
2
- import { z } from 'zod';
3
- import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
4
- import { ErrorCode, throwMcpError } from './errors.js';
5
- import { wallLength, wallLocalXFromT } from './geometry.js';
6
- import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
7
- import { measurement } from './measurement.js';
8
- import { NodeIdSchema } from './schemas.js';
9
- export const cutOpeningInput = {
10
- wallId: NodeIdSchema,
11
- type: z.enum(['door', 'window']),
12
- position: z.number().min(0).max(1),
13
- width: measurement('length', 'm', { positive: true, description: 'Opening width.' }),
14
- height: measurement('length', 'm', { positive: true, description: 'Opening height.' }),
15
- };
16
- export const cutOpeningOutput = {
17
- openingId: z.string(),
18
- ...liveSyncOutput,
19
- };
20
- export function registerCutOpening(server, bridge) {
21
- server.registerTool('cut_opening', {
22
- title: 'Cut opening',
23
- description: 'Cut a door or window opening into an existing wall. position is a parametric 0..1 offset along the wall centreline.',
24
- inputSchema: cutOpeningInput,
25
- outputSchema: cutOpeningOutput,
26
- annotations: ADDITIVE_TOOL_ANNOTATIONS,
27
- }, async ({ wallId, type, position, width, height }) => {
28
- const wall = bridge.getNode(wallId);
29
- if (!wall) {
30
- throwMcpError(ErrorCode.InvalidParams, `Wall not found: ${wallId}`);
31
- }
32
- if (wall.type !== 'wall') {
33
- throwMcpError(ErrorCode.InvalidParams, `Node ${wallId} is a ${wall.type}, expected wall`);
34
- }
35
- const length = wallLength(wall);
36
- if (length < width) {
37
- throwMcpError(ErrorCode.InvalidParams, `Wall ${wallId} is ${length.toFixed(2)}m long, too short for a ${width.toFixed(2)}m opening`);
38
- }
39
- // `position` is public MCP ergonomics: 0..1 along the wall. Door/window
40
- // nodes store wall-local meters in position[0], so convert before writing.
41
- const base = {
42
- wallId,
43
- width,
44
- height,
45
- position: [wallLocalXFromT(wall, position, width), height / 2, 0],
46
- };
47
- const opening = type === 'door'
48
- ? DoorNode.parse(base)
49
- : WindowNode.parse({
50
- ...base,
51
- position: [base.position[0], 0.9 + height / 2, 0],
52
- });
53
- const id = bridge.createNode(opening, wallId);
54
- const persistence = await publishLiveSceneSnapshot(bridge, 'cut_opening');
55
- const payload = { openingId: id, ...persistencePayload(persistence) };
56
- return {
57
- content: [{ type: 'text', text: JSON.stringify(payload) }],
58
- structuredContent: payload,
59
- };
60
- });
61
- }
@@ -1,19 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { z } from 'zod';
3
- import type { SceneOperations } from '../operations/index.js';
4
- export declare const deleteNodeInput: {
5
- id: z.ZodString;
6
- cascade: z.ZodOptional<z.ZodBoolean>;
7
- };
8
- export declare const deleteNodeOutput: {
9
- persistence: z.ZodOptional<z.ZodObject<{
10
- status: z.ZodEnum<{
11
- unbound: "unbound";
12
- events_unsupported: "events_unsupported";
13
- }>;
14
- warning: z.ZodString;
15
- }, z.core.$strip>>;
16
- deletedIds: z.ZodArray<z.ZodString>;
17
- };
18
- export declare function registerDeleteNode(server: McpServer, bridge: SceneOperations): void;
19
- //# sourceMappingURL=delete-node.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete-node.d.ts","sourceRoot":"","sources":["../../src/tools/delete-node.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;AAMpD,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA8BnF"}