@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.
- package/README.md +71 -0
- package/dist/bin/pascal-mcp.js +0 -0
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +6 -0
- package/dist/operations/scene-operations.js +1 -0
- package/dist/resources/constraints.js +1 -1
- package/dist/tools/construction-tools.d.ts +21 -21
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +45 -19
- package/dist/tools/create-level.d.ts +1 -1
- package/dist/tools/create-level.d.ts.map +1 -1
- package/dist/tools/create-level.js +5 -1
- package/dist/tools/create-wall.d.ts +2 -2
- package/dist/tools/create-wall.d.ts.map +1 -1
- package/dist/tools/create-wall.js +6 -2
- package/dist/tools/cut-opening.d.ts +3 -3
- package/dist/tools/cut-opening.d.ts.map +1 -1
- package/dist/tools/cut-opening.js +3 -2
- package/dist/tools/find-nodes.d.ts +2 -2
- package/dist/tools/find-nodes.js +1 -1
- package/dist/tools/measurement.d.ts +37 -0
- package/dist/tools/measurement.d.ts.map +1 -0
- package/dist/tools/measurement.js +94 -0
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts +2 -2
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +9 -2
- package/dist/tools/place-item.d.ts +1 -1
- package/dist/tools/place-item.d.ts.map +1 -1
- package/dist/tools/place-item.js +2 -1
- package/dist/tools/room-tools.d.ts +8 -8
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +17 -7
- package/dist/tools/scene-query.js +2 -2
- package/examples/coordinate-conventions-demo.json +968 -0
- package/examples/coordinate-conventions-demo.md +68 -0
- package/examples/embed-in-agent.ts +82 -0
- package/examples/generate-apartment.md +242 -0
- package/examples/photo-to-scene.md +119 -0
- package/examples/renovate-from-photos.md +202 -0
- 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:
|
|
15
|
-
|
|
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;
|
|
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"}
|
package/dist/tools/place-item.js
CHANGED
|
@@ -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:
|
|
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;
|
|
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"}
|
package/dist/tools/room-tools.js
CHANGED
|
@@ -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:
|
|
33
|
-
|
|
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:
|
|
47
|
-
height:
|
|
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:
|
|
65
|
-
height:
|
|
66
|
-
sillHeight:
|
|
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 (
|
|
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 (
|
|
544
|
+
if (parent?.type !== 'wall') {
|
|
545
545
|
issues.push(`${node.type} ${node.id} is not parented to a wall`);
|
|
546
546
|
continue;
|
|
547
547
|
}
|