@pascal-app/mcp 1.0.0-beta.6 → 1.0.1

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 (193) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1 -1
  4. package/dist/bin/pascal-mcp.d.ts +1 -1
  5. package/dist/bin/pascal-mcp.js +7 -7
  6. package/dist/bridge/index.d.ts +2 -2
  7. package/dist/bridge/index.js +1 -1
  8. package/dist/bridge/scene-bridge.d.ts +2 -2
  9. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  10. package/dist/bridge/scene-bridge.js +3 -3
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +4 -4
  13. package/dist/operations/index.d.ts +1 -1
  14. package/dist/operations/index.js +1 -1
  15. package/dist/operations/scene-operations.d.ts +2 -2
  16. package/dist/prompts/from-brief.d.ts +1 -1
  17. package/dist/prompts/from-brief.d.ts.map +1 -1
  18. package/dist/prompts/from-brief.js +2 -1
  19. package/dist/prompts/index.d.ts +1 -1
  20. package/dist/prompts/index.js +3 -3
  21. package/dist/prompts/iterate-on-feedback.d.ts +1 -1
  22. package/dist/prompts/iterate-on-feedback.js +1 -1
  23. package/dist/prompts/renovation-from-photos.d.ts +1 -1
  24. package/dist/resources/agent-guide.d.ts +1 -1
  25. package/dist/resources/agent-guide.d.ts.map +1 -1
  26. package/dist/resources/agent-guide.js +1 -0
  27. package/dist/resources/catalog-items.d.ts +1 -1
  28. package/dist/resources/catalog-items.js +1 -1
  29. package/dist/resources/constraints.d.ts +1 -1
  30. package/dist/resources/index.d.ts +1 -1
  31. package/dist/resources/index.js +5 -5
  32. package/dist/resources/scene-current.d.ts +1 -1
  33. package/dist/resources/scene-summary.d.ts +1 -1
  34. package/dist/server.d.ts +14 -3
  35. package/dist/server.d.ts.map +1 -1
  36. package/dist/server.js +60 -6
  37. package/dist/storage/index.d.ts +4 -4
  38. package/dist/storage/index.js +4 -4
  39. package/dist/storage/sqlite-scene-store.d.ts +1 -1
  40. package/dist/storage/sqlite-scene-store.js +3 -3
  41. package/dist/templates/index.js +3 -3
  42. package/dist/tools/annotations.d.ts +28 -0
  43. package/dist/tools/annotations.d.ts.map +1 -0
  44. package/dist/tools/annotations.js +27 -0
  45. package/dist/tools/apply-patch.d.ts +8 -1
  46. package/dist/tools/apply-patch.d.ts.map +1 -1
  47. package/dist/tools/apply-patch.js +9 -5
  48. package/dist/tools/check-collisions.d.ts +60 -1
  49. package/dist/tools/check-collisions.d.ts.map +1 -1
  50. package/dist/tools/check-collisions.js +242 -21
  51. package/dist/tools/construction-tools.d.ts +24 -2
  52. package/dist/tools/construction-tools.d.ts.map +1 -1
  53. package/dist/tools/construction-tools.js +36 -10
  54. package/dist/tools/create-level.d.ts +8 -1
  55. package/dist/tools/create-level.d.ts.map +1 -1
  56. package/dist/tools/create-level.js +9 -6
  57. package/dist/tools/create-unit.d.ts +27 -0
  58. package/dist/tools/create-unit.d.ts.map +1 -0
  59. package/dist/tools/create-unit.js +36 -0
  60. package/dist/tools/create-wall.d.ts +8 -1
  61. package/dist/tools/create-wall.d.ts.map +1 -1
  62. package/dist/tools/create-wall.js +9 -6
  63. package/dist/tools/cut-opening.d.ts +8 -1
  64. package/dist/tools/cut-opening.d.ts.map +1 -1
  65. package/dist/tools/cut-opening.js +10 -7
  66. package/dist/tools/delete-node.d.ts +8 -1
  67. package/dist/tools/delete-node.d.ts.map +1 -1
  68. package/dist/tools/delete-node.js +8 -5
  69. package/dist/tools/describe-node.d.ts +1 -1
  70. package/dist/tools/describe-node.d.ts.map +1 -1
  71. package/dist/tools/describe-node.js +5 -3
  72. package/dist/tools/door-clearance.d.ts +19 -3
  73. package/dist/tools/door-clearance.d.ts.map +1 -1
  74. package/dist/tools/door-clearance.js +102 -8
  75. package/dist/tools/duplicate-level.d.ts +8 -1
  76. package/dist/tools/duplicate-level.d.ts.map +1 -1
  77. package/dist/tools/duplicate-level.js +8 -4
  78. package/dist/tools/export-glb.d.ts +1 -1
  79. package/dist/tools/export-glb.d.ts.map +1 -1
  80. package/dist/tools/export-glb.js +4 -2
  81. package/dist/tools/export-json.d.ts +1 -1
  82. package/dist/tools/export-json.d.ts.map +1 -1
  83. package/dist/tools/export-json.js +2 -0
  84. package/dist/tools/find-nodes.d.ts +1 -1
  85. package/dist/tools/find-nodes.d.ts.map +1 -1
  86. package/dist/tools/find-nodes.js +3 -1
  87. package/dist/tools/get-node.d.ts +1 -1
  88. package/dist/tools/get-node.d.ts.map +1 -1
  89. package/dist/tools/get-node.js +4 -2
  90. package/dist/tools/get-scene.d.ts +1 -1
  91. package/dist/tools/get-scene.d.ts.map +1 -1
  92. package/dist/tools/get-scene.js +2 -0
  93. package/dist/tools/index.d.ts +1 -1
  94. package/dist/tools/index.d.ts.map +1 -1
  95. package/dist/tools/index.js +32 -26
  96. package/dist/tools/layout-clearance.d.ts +4 -2
  97. package/dist/tools/layout-clearance.d.ts.map +1 -1
  98. package/dist/tools/layout-clearance.js +7 -3
  99. package/dist/tools/list-units.d.ts +38 -0
  100. package/dist/tools/list-units.d.ts.map +1 -0
  101. package/dist/tools/list-units.js +61 -0
  102. package/dist/tools/live-sync.d.ts +34 -4
  103. package/dist/tools/live-sync.d.ts.map +1 -1
  104. package/dist/tools/live-sync.js +38 -6
  105. package/dist/tools/measure.d.ts +2 -1
  106. package/dist/tools/measure.d.ts.map +1 -1
  107. package/dist/tools/measure.js +6 -2
  108. package/dist/tools/normalize-schema-dialect.d.ts +3 -0
  109. package/dist/tools/normalize-schema-dialect.d.ts.map +1 -0
  110. package/dist/tools/normalize-schema-dialect.js +28 -0
  111. package/dist/tools/photo-to-scene/index.d.ts +2 -2
  112. package/dist/tools/photo-to-scene/index.js +2 -2
  113. package/dist/tools/photo-to-scene/photo-to-scene.d.ts +1 -1
  114. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  115. package/dist/tools/photo-to-scene/photo-to-scene.js +5 -3
  116. package/dist/tools/place-item.d.ts +8 -1
  117. package/dist/tools/place-item.d.ts.map +1 -1
  118. package/dist/tools/place-item.js +11 -7
  119. package/dist/tools/redo.d.ts +8 -1
  120. package/dist/tools/redo.d.ts.map +1 -1
  121. package/dist/tools/redo.js +6 -4
  122. package/dist/tools/room-tools.d.ts +29 -1
  123. package/dist/tools/room-tools.d.ts.map +1 -1
  124. package/dist/tools/room-tools.js +27 -12
  125. package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
  126. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
  127. package/dist/tools/scene-lifecycle/create-project.js +4 -2
  128. package/dist/tools/scene-lifecycle/delete-scene.d.ts +1 -1
  129. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
  130. package/dist/tools/scene-lifecycle/delete-scene.js +4 -2
  131. package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
  132. package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
  133. package/dist/tools/scene-lifecycle/get-project-status.js +4 -2
  134. package/dist/tools/scene-lifecycle/index.d.ts +8 -8
  135. package/dist/tools/scene-lifecycle/index.js +14 -14
  136. package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
  137. package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
  138. package/dist/tools/scene-lifecycle/list-scenes.js +3 -1
  139. package/dist/tools/scene-lifecycle/load-scene.d.ts +1 -1
  140. package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
  141. package/dist/tools/scene-lifecycle/load-scene.js +4 -2
  142. package/dist/tools/scene-lifecycle/metadata.d.ts +3 -3
  143. package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
  144. package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
  145. package/dist/tools/scene-lifecycle/rename-scene.js +4 -2
  146. package/dist/tools/scene-lifecycle/save-scene.d.ts +1 -1
  147. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
  148. package/dist/tools/scene-lifecycle/save-scene.js +6 -4
  149. package/dist/tools/scene-lifecycle/test-utils.d.ts +7 -3
  150. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
  151. package/dist/tools/scene-lifecycle/test-utils.js +28 -4
  152. package/dist/tools/scene-query.d.ts +1 -1
  153. package/dist/tools/scene-query.d.ts.map +1 -1
  154. package/dist/tools/scene-query.js +9 -3
  155. package/dist/tools/set-unit-members.d.ts +20 -0
  156. package/dist/tools/set-unit-members.d.ts.map +1 -0
  157. package/dist/tools/set-unit-members.js +43 -0
  158. package/dist/tools/set-zone.d.ts +8 -1
  159. package/dist/tools/set-zone.d.ts.map +1 -1
  160. package/dist/tools/set-zone.js +8 -5
  161. package/dist/tools/templates/create-from-template.d.ts +1 -1
  162. package/dist/tools/templates/create-from-template.d.ts.map +1 -1
  163. package/dist/tools/templates/create-from-template.js +7 -5
  164. package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
  165. package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
  166. package/dist/tools/templates/create-house-from-brief.js +7 -5
  167. package/dist/tools/templates/index.d.ts +4 -4
  168. package/dist/tools/templates/index.js +6 -6
  169. package/dist/tools/templates/list-templates.d.ts.map +1 -1
  170. package/dist/tools/templates/list-templates.js +3 -1
  171. package/dist/tools/undo.d.ts +8 -1
  172. package/dist/tools/undo.d.ts.map +1 -1
  173. package/dist/tools/undo.js +6 -4
  174. package/dist/tools/unit-members.d.ts +4 -0
  175. package/dist/tools/unit-members.d.ts.map +1 -0
  176. package/dist/tools/unit-members.js +24 -0
  177. package/dist/tools/validate-scene.d.ts +1 -1
  178. package/dist/tools/validate-scene.d.ts.map +1 -1
  179. package/dist/tools/validate-scene.js +2 -0
  180. package/dist/tools/variants/generate-variants.d.ts +1 -1
  181. package/dist/tools/variants/generate-variants.d.ts.map +1 -1
  182. package/dist/tools/variants/generate-variants.js +4 -2
  183. package/dist/tools/variants/index.d.ts +3 -3
  184. package/dist/tools/variants/index.js +3 -3
  185. package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
  186. package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
  187. package/dist/tools/vision/analyze-floorplan-image.js +3 -1
  188. package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
  189. package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
  190. package/dist/tools/vision/analyze-room-photo.js +3 -1
  191. package/dist/tools/vision/index.d.ts +3 -3
  192. package/dist/tools/vision/index.js +4 -4
  193. package/package.json +8 -7
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
2
3
  export const exportGlbInput = {};
3
4
  export const exportGlbOutput = {
4
5
  status: z.literal('not_implemented'),
@@ -7,9 +8,10 @@ export const exportGlbOutput = {
7
8
  export function registerExportGlb(server, _bridge) {
8
9
  server.registerTool('export_glb', {
9
10
  title: 'Export GLB',
10
- description: 'GLB export is not available in headless mode — it requires the Three.js renderer, which is browser-only. Returns a structured `not_implemented` response.',
11
+ description: 'GLB export is not available in headless mode — it requires the Three.js renderer, which is browser-only. Returns a structured `not_implemented` tool error.',
11
12
  inputSchema: exportGlbInput,
12
13
  outputSchema: exportGlbOutput,
14
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
13
15
  }, async () => {
14
16
  const payload = {
15
17
  status: 'not_implemented',
@@ -18,7 +20,7 @@ export function registerExportGlb(server, _bridge) {
18
20
  return {
19
21
  content: [{ type: 'text', text: JSON.stringify(payload) }],
20
22
  structuredContent: payload,
21
- isError: false,
23
+ isError: true,
22
24
  };
23
25
  });
24
26
  }
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
- import type { SceneOperations } from '../operations';
3
+ import type { SceneOperations } from '../operations/index.js';
4
4
  export declare const exportJsonInput: {
5
5
  pretty: z.ZodOptional<z.ZodBoolean>;
6
6
  };
@@ -1 +1 @@
1
- {"version":3,"file":"export-json.d.ts","sourceRoot":"","sources":["../../src/tools/export-json.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;AAEpD,eAAO,MAAM,eAAe;;CAE3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;CAE5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAoBnF"}
1
+ {"version":3,"file":"export-json.d.ts","sourceRoot":"","sources":["../../src/tools/export-json.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,eAAe;;CAE3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;CAE5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqBnF"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
2
3
  export const exportJsonInput = {
3
4
  pretty: z.boolean().optional(),
4
5
  };
@@ -11,6 +12,7 @@ export function registerExportJson(server, bridge) {
11
12
  description: 'Return the scene as a serialized JSON string. Pass `pretty: true` to indent with 2 spaces.',
12
13
  inputSchema: exportJsonInput,
13
14
  outputSchema: exportJsonOutput,
15
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
14
16
  }, async ({ pretty }) => {
15
17
  const scene = bridge.exportJSON();
16
18
  const json = JSON.stringify(scene, null, pretty ? 2 : 0);
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
- import type { SceneOperations } from '../operations';
3
+ import type { SceneOperations } from '../operations/index.js';
4
4
  export declare const findNodesInput: {
5
5
  type: z.ZodOptional<z.ZodEnum<{
6
6
  site: "site";
@@ -1 +1 @@
1
- {"version":3,"file":"find-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/find-nodes.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;AAuBpD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;CAK1B,CAAA;AAED,eAAO,MAAM,eAAe;;CAE3B,CAAA;AAkCD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAuDlF"}
1
+ {"version":3,"file":"find-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/find-nodes.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;AAwBpD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;CAK1B,CAAA;AAED,eAAO,MAAM,eAAe;;CAE3B,CAAA;AAkCD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwDlF"}
@@ -1,6 +1,7 @@
1
1
  import { pointInPolygon } from '@pascal-app/core/spatial-grid';
2
2
  import { z } from 'zod';
3
- import { NodeIdSchema } from './schemas';
3
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
4
+ import { NodeIdSchema } from './schemas.js';
4
5
  const ALL_NODE_TYPES = [
5
6
  'site',
6
7
  'building',
@@ -65,6 +66,7 @@ export function registerFindNodes(server, bridge) {
65
66
  description: 'Find nodes matching any combination of type, parentId, levelId, or zoneId filters.',
66
67
  inputSchema: findNodesInput,
67
68
  outputSchema: findNodesOutput,
69
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
68
70
  }, async (args) => {
69
71
  const { type, parentId, levelId, zoneId } = args;
70
72
  // Delegate type/parent/level filtering to the bridge.
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
- import type { SceneOperations } from '../operations';
3
+ import type { SceneOperations } from '../operations/index.js';
4
4
  export declare const getNodeInput: {
5
5
  id: z.ZodString;
6
6
  };
@@ -1 +1 @@
1
- {"version":3,"file":"get-node.d.ts","sourceRoot":"","sources":["../../src/tools/get-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;AAIpD,eAAO,MAAM,YAAY;;CAExB,CAAA;AAED,eAAO,MAAM,aAAa;;CAEzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqBhF"}
1
+ {"version":3,"file":"get-node.d.ts","sourceRoot":"","sources":["../../src/tools/get-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;AAKpD,eAAO,MAAM,YAAY;;CAExB,CAAA;AAED,eAAO,MAAM,aAAa;;CAEzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsBhF"}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
- import { ErrorCode, throwMcpError } from './errors';
3
- import { NodeIdSchema } from './schemas';
2
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
3
+ import { ErrorCode, throwMcpError } from './errors.js';
4
+ import { NodeIdSchema } from './schemas.js';
4
5
  export const getNodeInput = {
5
6
  id: NodeIdSchema,
6
7
  };
@@ -13,6 +14,7 @@ export function registerGetNode(server, bridge) {
13
14
  description: 'Return the full node payload for the given ID.',
14
15
  inputSchema: getNodeInput,
15
16
  outputSchema: getNodeOutput,
17
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
16
18
  }, async ({ id }) => {
17
19
  const node = bridge.getNode(id);
18
20
  if (!node) {
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
- import type { SceneOperations } from '../operations';
3
+ import type { SceneOperations } from '../operations/index.js';
4
4
  export declare const getSceneInput: {};
5
5
  export declare const getSceneOutput: {
6
6
  nodes: z.ZodRecord<z.ZodString, z.ZodUnknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"get-scene.d.ts","sourceRoot":"","sources":["../../src/tools/get-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,eAAO,MAAM,aAAa,IAAK,CAAA;AAE/B,eAAO,MAAM,cAAc;;;;CAI1B,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAuBjF"}
1
+ {"version":3,"file":"get-scene.d.ts","sourceRoot":"","sources":["../../src/tools/get-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,eAAO,MAAM,aAAa,IAAK,CAAA;AAE/B,eAAO,MAAM,cAAc;;;;CAI1B,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwBjF"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
2
3
  export const getSceneInput = {};
3
4
  export const getSceneOutput = {
4
5
  nodes: z.record(z.string(), z.unknown()),
@@ -11,6 +12,7 @@ export function registerGetScene(server, bridge) {
11
12
  description: 'Returns the full scene graph: flat node dictionary, root node IDs, and collections.',
12
13
  inputSchema: getSceneInput,
13
14
  outputSchema: getSceneOutput,
15
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
14
16
  }, async () => {
15
17
  const scene = bridge.exportJSON();
16
18
  const payload = {
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { SceneOperations } from '../operations';
2
+ import type { SceneOperations } from '../operations/index.js';
3
3
  /**
4
4
  * Register every non-vision MCP tool against the given server.
5
5
  * Vision tools (analyze_floorplan_image, analyze_room_photo) are registered
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA4BpD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA6BlF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA+BpD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAgClF"}
@@ -1,29 +1,32 @@
1
- import { registerApplyPatch } from './apply-patch';
2
- import { registerCheckCollisions } from './check-collisions';
3
- import { registerConstructionTools } from './construction-tools';
4
- import { registerCreateLevel } from './create-level';
5
- import { registerCreateWall } from './create-wall';
6
- import { registerCutOpening } from './cut-opening';
7
- import { registerDeleteNode } from './delete-node';
8
- import { registerDescribeNode } from './describe-node';
9
- import { registerDuplicateLevel } from './duplicate-level';
10
- import { registerExportGlb } from './export-glb';
11
- import { registerExportJson } from './export-json';
12
- import { registerFindNodes } from './find-nodes';
13
- import { registerGetNode } from './get-node';
14
- import { registerGetScene } from './get-scene';
15
- import { registerMeasure } from './measure';
16
- import { registerPhotoToSceneTool } from './photo-to-scene';
17
- import { registerPlaceItem } from './place-item';
18
- import { registerRedo } from './redo';
19
- import { registerRoomTools } from './room-tools';
20
- import { registerSceneLifecycleTools } from './scene-lifecycle';
21
- import { registerSceneQueryTools } from './scene-query';
22
- import { registerSetZone } from './set-zone';
23
- import { registerTemplateTools } from './templates';
24
- import { registerUndo } from './undo';
25
- import { registerValidateScene } from './validate-scene';
26
- import { registerVariantTools } from './variants';
1
+ import { registerApplyPatch } from './apply-patch.js';
2
+ import { registerCheckCollisions } from './check-collisions.js';
3
+ import { registerConstructionTools } from './construction-tools.js';
4
+ import { registerCreateLevel } from './create-level.js';
5
+ import { registerCreateUnit } from './create-unit.js';
6
+ import { registerCreateWall } from './create-wall.js';
7
+ import { registerCutOpening } from './cut-opening.js';
8
+ import { registerDeleteNode } from './delete-node.js';
9
+ import { registerDescribeNode } from './describe-node.js';
10
+ import { registerDuplicateLevel } from './duplicate-level.js';
11
+ import { registerExportGlb } from './export-glb.js';
12
+ import { registerExportJson } from './export-json.js';
13
+ import { registerFindNodes } from './find-nodes.js';
14
+ import { registerGetNode } from './get-node.js';
15
+ import { registerGetScene } from './get-scene.js';
16
+ import { registerListUnits } from './list-units.js';
17
+ import { registerMeasure } from './measure.js';
18
+ import { registerPhotoToSceneTool } from './photo-to-scene/index.js';
19
+ import { registerPlaceItem } from './place-item.js';
20
+ import { registerRedo } from './redo.js';
21
+ import { registerRoomTools } from './room-tools.js';
22
+ import { registerSceneLifecycleTools } from './scene-lifecycle/index.js';
23
+ import { registerSceneQueryTools } from './scene-query.js';
24
+ import { registerSetUnitMembers } from './set-unit-members.js';
25
+ import { registerSetZone } from './set-zone.js';
26
+ import { registerTemplateTools } from './templates/index.js';
27
+ import { registerUndo } from './undo.js';
28
+ import { registerValidateScene } from './validate-scene.js';
29
+ import { registerVariantTools } from './variants/index.js';
27
30
  /**
28
31
  * Register every non-vision MCP tool against the given server.
29
32
  * Vision tools (analyze_floorplan_image, analyze_room_photo) are registered
@@ -43,6 +46,9 @@ export function registerTools(server, operations) {
43
46
  registerRoomTools(server, operations);
44
47
  registerApplyPatch(server, operations);
45
48
  registerCreateLevel(server, operations);
49
+ registerCreateUnit(server, operations);
50
+ registerSetUnitMembers(server, operations);
51
+ registerListUnits(server, operations);
46
52
  registerCreateWall(server, operations);
47
53
  registerPlaceItem(server, operations);
48
54
  registerCutOpening(server, operations);
@@ -8,8 +8,8 @@
8
8
  * See docs/layout-clearance-error-log.md for regression checklist.
9
9
  */
10
10
  import type { AnyNode } from '@pascal-app/core/schema';
11
- import { type PlanAabb } from './door-clearance';
12
- export { aabbsOverlap, collectDoorKeepouts, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, type PlanAabb, resolveNodeLevelId, } from './door-clearance';
11
+ import { type PlanAabb } from './door-clearance.js';
12
+ export { aabbsOverlap, collectDoorKeepouts, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, type PlanAabb, resolveNodeLevelId, } from './door-clearance.js';
13
13
  /** Minimum free space (m) required between item footprints. */
14
14
  export declare const DEFAULT_ITEM_GAP = 0.08;
15
15
  export type OccupiedFootprint = {
@@ -25,6 +25,8 @@ export type ItemCollision = {
25
25
  bName?: string;
26
26
  levelId?: string | null;
27
27
  kind: 'item-aabb';
28
+ violation: 'overlap' | 'clearance';
29
+ minimumClearanceMeters: number;
28
30
  message: string;
29
31
  };
30
32
  export declare function nodeItemAabb(node: AnyNode): PlanAabb | null;
@@ -1 +1 @@
1
- {"version":3,"file":"layout-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/layout-clearance.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"layout-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/layout-clearance.ts"],"names":[],"mappings":"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"}
@@ -7,8 +7,8 @@
7
7
  *
8
8
  * See docs/layout-clearance-error-log.md for regression checklist.
9
9
  */
10
- import { aabbsOverlap, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, resolveNodeLevelId, } from './door-clearance';
11
- export { aabbsOverlap, collectDoorKeepouts, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, resolveNodeLevelId, } from './door-clearance';
10
+ import { aabbsOverlap, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, resolveNodeLevelId, } from './door-clearance.js';
11
+ export { aabbsOverlap, collectDoorKeepouts, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, resolveNodeLevelId, } from './door-clearance.js';
12
12
  /** Minimum free space (m) required between item footprints. */
13
13
  export const DEFAULT_ITEM_GAP = 0.08;
14
14
  export function nodeItemAabb(node) {
@@ -71,7 +71,11 @@ export function findItemItemCollisions(args) {
71
71
  bName: b.name,
72
72
  levelId: a.levelId ?? b.levelId,
73
73
  kind: 'item-aabb',
74
- message: `Items overlap: ${a.name ?? a.id} (${a.id}) and ${b.name ?? b.id} (${b.id})`,
74
+ violation: aabbsOverlap(a.aabb, b.aabb, 0) ? 'overlap' : 'clearance',
75
+ minimumClearanceMeters: gap,
76
+ message: aabbsOverlap(a.aabb, b.aabb, 0)
77
+ ? `Items overlap: ${a.name ?? a.id} (${a.id}) and ${b.name ?? b.id} (${b.id})`
78
+ : `Items are closer than ${gap} m: ${a.name ?? a.id} (${a.id}) and ${b.name ?? b.id} (${b.id})`,
75
79
  });
76
80
  }
77
81
  }
@@ -0,0 +1,38 @@
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 listUnitsInput: {
5
+ buildingId: z.ZodOptional<z.ZodString>;
6
+ };
7
+ export declare const listUnitsOutput: {
8
+ units: z.ZodArray<z.ZodObject<{
9
+ unitId: z.ZodString;
10
+ name: z.ZodString;
11
+ kind: z.ZodDefault<z.ZodEnum<{
12
+ apartment: "apartment";
13
+ "hotel-room": "hotel-room";
14
+ commercial: "commercial";
15
+ common: "common";
16
+ }>>;
17
+ color: z.ZodString;
18
+ members: z.ZodArray<z.ZodString>;
19
+ report: z.ZodObject<{
20
+ memberCount: z.ZodNumber;
21
+ levelSpan: z.ZodNullable<z.ZodObject<{
22
+ minOrdinal: z.ZodNumber;
23
+ maxOrdinal: z.ZodNumber;
24
+ count: z.ZodNumber;
25
+ }, z.core.$strip>>;
26
+ grossAreaM2: z.ZodNumber;
27
+ members: z.ZodArray<z.ZodObject<{
28
+ zoneId: z.ZodString;
29
+ name: z.ZodString;
30
+ levelId: z.ZodNullable<z.ZodString>;
31
+ levelOrdinal: z.ZodNullable<z.ZodNumber>;
32
+ areaM2: z.ZodNumber;
33
+ }, z.core.$strip>>;
34
+ }, z.core.$strip>;
35
+ }, z.core.$strip>>;
36
+ };
37
+ export declare function registerListUnits(server: McpServer, bridge: SceneOperations): void;
38
+ //# sourceMappingURL=list-units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-units.d.ts","sourceRoot":"","sources":["../../src/tools/list-units.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;AAKpD,eAAO,MAAM,cAAc;;CAE1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgClF"}
@@ -0,0 +1,61 @@
1
+ import { buildUnitReport } from '@pascal-app/core';
2
+ import { UnitNode } from '@pascal-app/core/schema';
3
+ import { z } from 'zod';
4
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
5
+ import { NodeIdSchema } from './schemas.js';
6
+ import { validateUnitMembers } from './unit-members.js';
7
+ export const listUnitsInput = {
8
+ buildingId: NodeIdSchema.optional(),
9
+ };
10
+ export const listUnitsOutput = {
11
+ units: z.array(z.object({
12
+ unitId: z.string(),
13
+ name: z.string(),
14
+ kind: UnitNode.shape.kind,
15
+ color: z.string(),
16
+ members: z.array(z.string()),
17
+ report: z.object({
18
+ memberCount: z.number(),
19
+ levelSpan: z
20
+ .object({ minOrdinal: z.number(), maxOrdinal: z.number(), count: z.number() })
21
+ .nullable(),
22
+ grossAreaM2: z.number(),
23
+ members: z.array(z.object({
24
+ zoneId: z.string(),
25
+ name: z.string(),
26
+ levelId: z.string().nullable(),
27
+ levelOrdinal: z.number().nullable(),
28
+ areaM2: z.number(),
29
+ })),
30
+ }),
31
+ })),
32
+ };
33
+ export function registerListUnits(server, bridge) {
34
+ server.registerTool('list_units', {
35
+ title: 'List units',
36
+ description: 'List units with their member zones and current area reports, optionally filtered by building.',
37
+ inputSchema: listUnitsInput,
38
+ outputSchema: listUnitsOutput,
39
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
40
+ }, async ({ buildingId }) => {
41
+ if (buildingId !== undefined)
42
+ validateUnitMembers(bridge, buildingId, []);
43
+ const nodes = bridge.getNodes();
44
+ const units = Object.values(nodes)
45
+ .filter((node) => node.type === 'unit')
46
+ .filter((unit) => buildingId === undefined || unit.parentId === buildingId)
47
+ .map((unit) => ({
48
+ unitId: unit.id,
49
+ name: unit.name,
50
+ kind: unit.kind,
51
+ color: unit.color,
52
+ members: unit.members,
53
+ report: buildUnitReport(unit, nodes),
54
+ }));
55
+ const payload = { units };
56
+ return {
57
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
58
+ structuredContent: payload,
59
+ };
60
+ });
61
+ }
@@ -1,11 +1,41 @@
1
1
  import type { SceneGraph } from '@pascal-app/core/clone-scene-graph';
2
- import type { SceneOperations } from '../operations';
2
+ import { z } from 'zod';
3
+ import type { SceneOperations } from '../operations/index.js';
3
4
  export declare function syncDerivedStairOpenings(operations: SceneOperations): number;
5
+ export type LiveSyncStatus = 'published' | 'unbound' | 'events_unsupported';
6
+ type LiveSyncSkip = Exclude<LiveSyncStatus, 'published'>;
7
+ /**
8
+ * Output-schema fragment for every tool that mutates the scene. Spread into
9
+ * the tool's `outputSchema` so `persistencePayload` fields survive the SDK's
10
+ * structured-content validation.
11
+ */
12
+ export declare const liveSyncOutput: {
13
+ persistence: z.ZodOptional<z.ZodObject<{
14
+ status: z.ZodEnum<{
15
+ unbound: "unbound";
16
+ events_unsupported: "events_unsupported";
17
+ }>;
18
+ warning: z.ZodString;
19
+ }, z.core.$strip>>;
20
+ };
21
+ /**
22
+ * Payload fragment matching `liveSyncOutput`: empty after a successful
23
+ * publish, a `persistence` warning when the mutation stayed in-memory.
24
+ */
25
+ export declare function persistencePayload(status: LiveSyncStatus): {
26
+ persistence?: {
27
+ status: LiveSyncSkip;
28
+ warning: string;
29
+ };
30
+ };
4
31
  /**
5
32
  * Persist the bridge's current graph to the active scene and append a live
6
- * event for browser subscribers. No-ops when the MCP session is not currently
7
- * bound to a saved scene.
33
+ * event for browser subscribers. Skips persistence — reporting why — when the
34
+ * MCP session is not currently bound to a saved scene or the store cannot
35
+ * append scene events; callers surface that through `persistencePayload` so
36
+ * the skip is never silent (#725).
8
37
  */
9
- export declare function publishLiveSceneSnapshot(operations: SceneOperations, kind: string): Promise<void>;
38
+ export declare function publishLiveSceneSnapshot(operations: SceneOperations, kind: string): Promise<LiveSyncStatus>;
10
39
  export declare function appendLiveSceneEvent(operations: SceneOperations, sceneId: string, version: number, kind: string, graph: SceneGraph): Promise<void>;
40
+ export {};
11
41
  //# sourceMappingURL=live-sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"live-sync.d.ts","sourceRoot":"","sources":["../../src/tools/live-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,eAAe,GAAG,MAAM,CAW5E;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAsCf;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf"}
1
+ {"version":3,"file":"live-sync.d.ts","sourceRoot":"","sources":["../../src/tools/live-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAEpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,eAAe,GAAG,MAAM,CAW5E;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,oBAAoB,CAAA;AAE3E,KAAK,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;AAExD;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;CAO1B,CAAA;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG;IAC1D,WAAW,CAAC,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CACxD,CAGA;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,cAAc,CAAC,CAwCzB;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -1,6 +1,7 @@
1
1
  import { syncAutoStairOpenings } from '@pascal-app/core/stair-openings';
2
- import { SceneVersionConflictError } from '../storage/types';
3
- import { ErrorCode, throwMcpError } from './errors';
2
+ import { z } from 'zod';
3
+ import { SceneVersionConflictError } from '../storage/types.js';
4
+ import { ErrorCode, throwMcpError } from './errors.js';
4
5
  export function syncDerivedStairOpenings(operations) {
5
6
  const updates = syncAutoStairOpenings(operations.getNodes());
6
7
  if (updates.length === 0)
@@ -12,16 +13,46 @@ export function syncDerivedStairOpenings(operations) {
12
13
  })));
13
14
  return updates.length;
14
15
  }
16
+ /**
17
+ * Output-schema fragment for every tool that mutates the scene. Spread into
18
+ * the tool's `outputSchema` so `persistencePayload` fields survive the SDK's
19
+ * structured-content validation.
20
+ */
21
+ export const liveSyncOutput = {
22
+ persistence: z
23
+ .object({
24
+ status: z.enum(['unbound', 'events_unsupported']),
25
+ warning: z.string(),
26
+ })
27
+ .optional(),
28
+ };
29
+ const LIVE_SYNC_WARNINGS = {
30
+ unbound: 'The change was applied to the in-memory session only: no active scene is bound, so nothing was persisted and no live event reached subscribers. Bind a scene with save_scene or load_scene to persist changes.',
31
+ events_unsupported: 'The change was applied to the in-memory session only: the attached scene store does not support live scene events, so nothing was persisted.',
32
+ };
33
+ /**
34
+ * Payload fragment matching `liveSyncOutput`: empty after a successful
35
+ * publish, a `persistence` warning when the mutation stayed in-memory.
36
+ */
37
+ export function persistencePayload(status) {
38
+ if (status === 'published')
39
+ return {};
40
+ return { persistence: { status, warning: LIVE_SYNC_WARNINGS[status] } };
41
+ }
15
42
  /**
16
43
  * Persist the bridge's current graph to the active scene and append a live
17
- * event for browser subscribers. No-ops when the MCP session is not currently
18
- * bound to a saved scene.
44
+ * event for browser subscribers. Skips persistence — reporting why — when the
45
+ * MCP session is not currently bound to a saved scene or the store cannot
46
+ * append scene events; callers surface that through `persistencePayload` so
47
+ * the skip is never silent (#725).
19
48
  */
20
49
  export async function publishLiveSceneSnapshot(operations, kind) {
21
50
  syncDerivedStairOpenings(operations);
22
51
  const active = operations.getActiveScene();
23
- if (!(active && operations.canAppendSceneEvents))
24
- return;
52
+ if (!active)
53
+ return 'unbound';
54
+ if (!operations.canAppendSceneEvents)
55
+ return 'events_unsupported';
25
56
  const graph = operations.exportSceneGraph();
26
57
  try {
27
58
  const meta = await operations.saveScene({
@@ -54,6 +85,7 @@ export async function publishLiveSceneSnapshot(operations, kind) {
54
85
  const message = error instanceof Error ? error.message : String(error);
55
86
  throwMcpError(ErrorCode.InternalError, `live_sync_failed: ${message}`);
56
87
  }
88
+ return 'published';
57
89
  }
58
90
  export async function appendLiveSceneEvent(operations, sceneId, version, kind, graph) {
59
91
  if (!operations.canAppendSceneEvents)
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
- import type { SceneOperations } from '../operations';
3
+ import type { SceneOperations } from '../operations/index.js';
4
4
  export declare const measureInput: {
5
5
  fromId: z.ZodString;
6
6
  toId: z.ZodString;
@@ -9,6 +9,7 @@ export declare const measureOutput: {
9
9
  distanceMeters: z.ZodNumber;
10
10
  areaSqMeters: z.ZodOptional<z.ZodNumber>;
11
11
  units: z.ZodLiteral<"meters">;
12
+ areaUnits: z.ZodOptional<z.ZodLiteral<"square_meters">>;
12
13
  };
13
14
  export declare function registerMeasure(server: McpServer, bridge: SceneOperations): void;
14
15
  //# sourceMappingURL=measure.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../src/tools/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,eAAO,MAAM,YAAY;;;CAGxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;CAIzB,CAAA;AAsED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAuEhF"}
1
+ {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../src/tools/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAKpD,eAAO,MAAM,YAAY;;;CAGxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;CAKzB,CAAA;AAsED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAyEhF"}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
- import { ErrorCode, throwMcpError } from './errors';
3
- import { NodeIdSchema } from './schemas';
2
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
3
+ import { ErrorCode, throwMcpError } from './errors.js';
4
+ import { NodeIdSchema } from './schemas.js';
4
5
  export const measureInput = {
5
6
  fromId: NodeIdSchema,
6
7
  toId: NodeIdSchema,
@@ -9,6 +10,7 @@ export const measureOutput = {
9
10
  distanceMeters: z.number(),
10
11
  areaSqMeters: z.number().optional(),
11
12
  units: z.literal('meters'),
13
+ areaUnits: z.literal('square_meters').optional(),
12
14
  };
13
15
  /**
14
16
  * Compute a 3D centre point in level-coordinate space for distance measurement.
@@ -85,6 +87,7 @@ export function registerMeasure(server, bridge) {
85
87
  description: 'Measure distance (in meters) between two nodes, or the net area of a polygon node when fromId === toId.',
86
88
  inputSchema: measureInput,
87
89
  outputSchema: measureOutput,
90
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
88
91
  }, async ({ fromId, toId }) => {
89
92
  const from = bridge.getNode(fromId);
90
93
  if (!from) {
@@ -107,6 +110,7 @@ export function registerMeasure(server, bridge) {
107
110
  distanceMeters: 0,
108
111
  areaSqMeters: area,
109
112
  units: 'meters',
113
+ areaUnits: 'square_meters',
110
114
  };
111
115
  return {
112
116
  content: [{ type: 'text', text: JSON.stringify(payload) }],
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function normalizeToolSchemaDialect(server: McpServer): void;
3
+ //# sourceMappingURL=normalize-schema-dialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-schema-dialect.d.ts","sourceRoot":"","sources":["../../src/tools/normalize-schema-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAuBxE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAWlE"}
@@ -0,0 +1,28 @@
1
+ import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
2
+ const DIALECT_2020_12 = 'https://json-schema.org/draft/2020-12/schema';
3
+ function retargetDialect(value) {
4
+ if (Array.isArray(value)) {
5
+ for (const item of value)
6
+ retargetDialect(item);
7
+ return;
8
+ }
9
+ if (value && typeof value === 'object') {
10
+ const record = value;
11
+ if (typeof record.$schema === 'string')
12
+ record.$schema = DIALECT_2020_12;
13
+ for (const key of Object.keys(record))
14
+ retargetDialect(record[key]);
15
+ }
16
+ }
17
+ export function normalizeToolSchemaDialect(server) {
18
+ const registry = server.server;
19
+ const original = registry._requestHandlers.get('tools/list');
20
+ if (!original)
21
+ return;
22
+ server.server.removeRequestHandler('tools/list');
23
+ server.server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
24
+ const result = (await original(request, extra));
25
+ retargetDialect(result.tools);
26
+ return result;
27
+ });
28
+ }