@pascal-app/mcp 0.3.0 → 0.3.2

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 (40) hide show
  1. package/README.md +71 -0
  2. package/dist/bin/pascal-mcp.js +0 -0
  3. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  4. package/dist/bridge/scene-bridge.js +6 -0
  5. package/dist/operations/scene-operations.js +1 -0
  6. package/dist/resources/constraints.js +1 -1
  7. package/dist/tools/construction-tools.d.ts +21 -21
  8. package/dist/tools/construction-tools.d.ts.map +1 -1
  9. package/dist/tools/construction-tools.js +45 -19
  10. package/dist/tools/create-level.d.ts +1 -1
  11. package/dist/tools/create-level.d.ts.map +1 -1
  12. package/dist/tools/create-level.js +5 -1
  13. package/dist/tools/create-wall.d.ts +2 -2
  14. package/dist/tools/create-wall.d.ts.map +1 -1
  15. package/dist/tools/create-wall.js +6 -2
  16. package/dist/tools/cut-opening.d.ts +3 -3
  17. package/dist/tools/cut-opening.d.ts.map +1 -1
  18. package/dist/tools/cut-opening.js +3 -2
  19. package/dist/tools/find-nodes.d.ts +2 -2
  20. package/dist/tools/find-nodes.js +1 -1
  21. package/dist/tools/measurement.d.ts +37 -0
  22. package/dist/tools/measurement.d.ts.map +1 -0
  23. package/dist/tools/measurement.js +94 -0
  24. package/dist/tools/photo-to-scene/photo-to-scene.d.ts +2 -2
  25. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  26. package/dist/tools/photo-to-scene/photo-to-scene.js +9 -2
  27. package/dist/tools/place-item.d.ts +1 -1
  28. package/dist/tools/place-item.d.ts.map +1 -1
  29. package/dist/tools/place-item.js +2 -1
  30. package/dist/tools/room-tools.d.ts +8 -8
  31. package/dist/tools/room-tools.d.ts.map +1 -1
  32. package/dist/tools/room-tools.js +17 -7
  33. package/dist/tools/scene-query.js +2 -2
  34. package/examples/coordinate-conventions-demo.json +968 -0
  35. package/examples/coordinate-conventions-demo.md +68 -0
  36. package/examples/embed-in-agent.ts +82 -0
  37. package/examples/generate-apartment.md +242 -0
  38. package/examples/photo-to-scene.md +119 -0
  39. package/examples/renovate-from-photos.md +202 -0
  40. package/package.json +8 -6
@@ -2,6 +2,7 @@ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { AnyNode, BuildingNode, LevelNode, SiteNode, WallNode, ZoneNode, } from '@pascal-app/core/schema';
3
3
  import { z } from 'zod';
4
4
  import { appendLiveSceneEvent } from '../live-sync';
5
+ import { measurement } from '../measurement';
5
6
  /**
6
7
  * Input shape for the `photo_to_scene` orchestrator. `image` matches the
7
8
  * contract documented on `analyze_floorplan_image` — base64 or http(s) URL.
@@ -11,8 +12,14 @@ export const photoToSceneInput = {
11
12
  scaleHint: z.string().optional().describe('e.g. "1 cm = 1 m" or "approx 80 m²"'),
12
13
  name: z.string().default('Scene from photo'),
13
14
  save: z.boolean().default(true),
14
- defaultWallThickness: z.number().default(0.2),
15
- defaultWallHeight: z.number().default(2.6),
15
+ defaultWallThickness: measurement('length', 'm', {
16
+ positive: true,
17
+ description: 'Default wall thickness.',
18
+ }).default(0.2),
19
+ defaultWallHeight: measurement('length', 'm', {
20
+ positive: true,
21
+ description: 'Default wall height.',
22
+ }).default(2.6),
16
23
  };
17
24
  export const photoToSceneOutput = {
18
25
  sceneId: z.string().optional(),
@@ -5,7 +5,7 @@ export declare const placeItemInput: {
5
5
  catalogItemId: z.ZodString;
6
6
  targetNodeId: z.ZodString;
7
7
  position: z.ZodArray<z.ZodNumber>;
8
- rotation: z.ZodOptional<z.ZodNumber>;
8
+ rotation: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
9
9
  };
10
10
  export declare const placeItemOutput: {
11
11
  itemId: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"place-item.d.ts","sourceRoot":"","sources":["../../src/tools/place-item.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,cAAc;;;;;CAK1B,CAAA;AAED,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsFlF"}
1
+ {"version":3,"file":"place-item.d.ts","sourceRoot":"","sources":["../../src/tools/place-item.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,cAAc;;;;;CAK1B,CAAA;AAED,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsFlF"}
@@ -4,12 +4,13 @@ import { findCatalogItem } from './asset-catalog';
4
4
  import { ErrorCode, throwMcpError } from './errors';
5
5
  import { projectWorldPointToWallLocalX, wallLength } from './geometry';
6
6
  import { publishLiveSceneSnapshot } from './live-sync';
7
+ import { measurement } from './measurement';
7
8
  import { NodeIdSchema, Vec3Schema } from './schemas';
8
9
  export const placeItemInput = {
9
10
  catalogItemId: z.string().min(1),
10
11
  targetNodeId: NodeIdSchema,
11
12
  position: Vec3Schema,
12
- rotation: z.number().optional(),
13
+ rotation: measurement('angle', 'rad', { description: 'Y-axis rotation.' }).optional(),
13
14
  };
14
15
  export const placeItemOutput = {
15
16
  itemId: z.string(),
@@ -14,8 +14,8 @@ export declare const createRoomInput: {
14
14
  name: z.ZodString;
15
15
  polygon: z.ZodArray<z.ZodArray<z.ZodNumber>>;
16
16
  color: z.ZodOptional<z.ZodString>;
17
- wallHeight: z.ZodOptional<z.ZodNumber>;
18
- wallThickness: z.ZodOptional<z.ZodNumber>;
17
+ wallHeight: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
18
+ wallThickness: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
19
19
  };
20
20
  export declare const createRoomOutput: {
21
21
  zoneId: z.ZodString;
@@ -28,11 +28,11 @@ export declare const addDoorInput: {
28
28
  wallId: z.ZodString;
29
29
  t: z.ZodOptional<z.ZodNumber>;
30
30
  position: z.ZodOptional<z.ZodNumber>;
31
- width: z.ZodOptional<z.ZodNumber>;
32
- height: z.ZodOptional<z.ZodNumber>;
31
+ width: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
32
+ height: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
33
33
  hingesSide: z.ZodOptional<z.ZodEnum<{
34
- left: "left";
35
34
  right: "right";
35
+ left: "left";
36
36
  }>>;
37
37
  swingDirection: z.ZodOptional<z.ZodEnum<{
38
38
  inward: "inward";
@@ -52,9 +52,9 @@ export declare const addWindowInput: {
52
52
  wallId: z.ZodString;
53
53
  t: z.ZodOptional<z.ZodNumber>;
54
54
  position: z.ZodOptional<z.ZodNumber>;
55
- width: z.ZodOptional<z.ZodNumber>;
56
- height: z.ZodOptional<z.ZodNumber>;
57
- sillHeight: z.ZodOptional<z.ZodNumber>;
55
+ width: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
56
+ height: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
57
+ sillHeight: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
58
58
  };
59
59
  export declare const addWindowOutput: {
60
60
  windowId: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"room-tools.d.ts","sourceRoot":"","sources":["../../src/tools/room-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAWxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA0BpD,eAAO,MAAM,iBAAiB;;;CAG7B,CAAA;AAED,eAAO,MAAM,kBAAkB;;;CAG9B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;CAO3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;CAM5B,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAQxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;CAQzB,CAAA;AAED,eAAO,MAAM,cAAc;;;;;;;CAO1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;CAS3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;CAM5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;CAI7B,CAAA;AA+PD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB5D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqDnF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA2ChF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0ClF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsEpF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAMlF"}
1
+ {"version":3,"file":"room-tools.d.ts","sourceRoot":"","sources":["../../src/tools/room-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAWxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA2BpD,eAAO,MAAM,iBAAiB;;;CAG7B,CAAA;AAED,eAAO,MAAM,kBAAkB;;;CAG9B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;CAa3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;CAM5B,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAQxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;CAQzB,CAAA;AAED,eAAO,MAAM,cAAc;;;;;;;CAU1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;CAS3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;CAM5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;CAI7B,CAAA;AA+PD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB5D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqDnF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA2ChF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0ClF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsEpF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAMlF"}
@@ -4,6 +4,7 @@ import { findCatalogItem, searchCatalogItems } from './asset-catalog';
4
4
  import { ErrorCode, throwMcpError } from './errors';
5
5
  import { pointInBoundsWithPadding, polygonArea, polygonBounds, wallLength, wallLocalXFromT, } from './geometry';
6
6
  import { publishLiveSceneSnapshot } from './live-sync';
7
+ import { measurement } from './measurement';
7
8
  import { NodeIdSchema, Vec2Schema } from './schemas';
8
9
  const ROOM_TYPES = [
9
10
  'bedroom',
@@ -29,8 +30,14 @@ export const createRoomInput = {
29
30
  name: z.string().min(1),
30
31
  polygon: z.array(Vec2Schema).min(3),
31
32
  color: z.string().optional(),
32
- wallHeight: z.number().positive().optional(),
33
- wallThickness: z.number().positive().optional(),
33
+ wallHeight: measurement('length', 'm', {
34
+ positive: true,
35
+ description: 'Wall height.',
36
+ }).optional(),
37
+ wallThickness: measurement('length', 'm', {
38
+ positive: true,
39
+ description: 'Wall thickness.',
40
+ }).optional(),
34
41
  };
35
42
  export const createRoomOutput = {
36
43
  zoneId: z.string(),
@@ -43,8 +50,8 @@ export const addDoorInput = {
43
50
  wallId: NodeIdSchema,
44
51
  t: z.number().min(0).max(1).optional(),
45
52
  position: z.number().min(0).max(1).optional(),
46
- width: z.number().positive().optional(),
47
- height: z.number().positive().optional(),
53
+ width: measurement('length', 'm', { positive: true, description: 'Door width.' }).optional(),
54
+ height: measurement('length', 'm', { positive: true, description: 'Door height.' }).optional(),
48
55
  hingesSide: z.enum(['left', 'right']).optional(),
49
56
  swingDirection: z.enum(['inward', 'outward']).optional(),
50
57
  };
@@ -61,9 +68,12 @@ export const addWindowInput = {
61
68
  wallId: NodeIdSchema,
62
69
  t: z.number().min(0).max(1).optional(),
63
70
  position: z.number().min(0).max(1).optional(),
64
- width: z.number().positive().optional(),
65
- height: z.number().positive().optional(),
66
- sillHeight: z.number().min(0).optional(),
71
+ width: measurement('length', 'm', { positive: true, description: 'Window width.' }).optional(),
72
+ height: measurement('length', 'm', { positive: true, description: 'Window height.' }).optional(),
73
+ sillHeight: measurement('length', 'm', {
74
+ min: 0,
75
+ description: 'Sill height above floor.',
76
+ }).optional(),
67
77
  };
68
78
  export const addWindowOutput = {
69
79
  windowId: z.string(),
@@ -305,7 +305,7 @@ function parentListsChild(parent, childId) {
305
305
  }
306
306
  function levelSummary(bridge, levelId) {
307
307
  const level = bridge.getNode(levelId);
308
- if (!level || level.type !== 'level') {
308
+ if (level?.type !== 'level') {
309
309
  throw new Error(`Level not found: ${levelId}`);
310
310
  }
311
311
  const nodes = nodesOnLevel(bridge, levelId);
@@ -541,7 +541,7 @@ export function registerVerifyScene(server, bridge) {
541
541
  for (const node of Object.values(bridge.getNodes())) {
542
542
  if (node.type === 'door' || node.type === 'window') {
543
543
  const parent = node.parentId ? bridge.getNode(node.parentId) : null;
544
- if (!parent || parent.type !== 'wall') {
544
+ if (parent?.type !== 'wall') {
545
545
  issues.push(`${node.type} ${node.id} is not parented to a wall`);
546
546
  continue;
547
547
  }