@pascal-app/mcp 0.2.0
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/CHANGELOG.md +39 -0
- package/README.md +330 -0
- package/dist/bin/pascal-mcp.d.ts +3 -0
- package/dist/bin/pascal-mcp.d.ts.map +1 -0
- package/dist/bin/pascal-mcp.js +91 -0
- package/dist/bridge/index.d.ts +3 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +1 -0
- package/dist/bridge/node-shims.d.ts +16 -0
- package/dist/bridge/node-shims.d.ts.map +1 -0
- package/dist/bridge/node-shims.js +25 -0
- package/dist/bridge/scene-bridge.d.ts +158 -0
- package/dist/bridge/scene-bridge.d.ts.map +1 -0
- package/dist/bridge/scene-bridge.js +482 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/lib/rehydrate-site-children.d.ts +12 -0
- package/dist/lib/rehydrate-site-children.d.ts.map +1 -0
- package/dist/lib/rehydrate-site-children.js +40 -0
- package/dist/lib/safe-fetch.d.ts +19 -0
- package/dist/lib/safe-fetch.d.ts.map +1 -0
- package/dist/lib/safe-fetch.js +221 -0
- package/dist/operations/index.d.ts +2 -0
- package/dist/operations/index.d.ts.map +1 -0
- package/dist/operations/index.js +1 -0
- package/dist/operations/scene-operations.d.ts +67 -0
- package/dist/operations/scene-operations.d.ts.map +1 -0
- package/dist/operations/scene-operations.js +192 -0
- package/dist/prompts/from-brief.d.ts +11 -0
- package/dist/prompts/from-brief.d.ts.map +1 -0
- package/dist/prompts/from-brief.js +48 -0
- package/dist/prompts/index.d.ts +10 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +14 -0
- package/dist/prompts/iterate-on-feedback.d.ts +11 -0
- package/dist/prompts/iterate-on-feedback.d.ts.map +1 -0
- package/dist/prompts/iterate-on-feedback.js +41 -0
- package/dist/prompts/renovation-from-photos.d.ts +24 -0
- package/dist/prompts/renovation-from-photos.d.ts.map +1 -0
- package/dist/prompts/renovation-from-photos.js +135 -0
- package/dist/prompts/scene-guidance.d.ts +2 -0
- package/dist/prompts/scene-guidance.d.ts.map +1 -0
- package/dist/prompts/scene-guidance.js +24 -0
- package/dist/resources/agent-guide.d.ts +5 -0
- package/dist/resources/agent-guide.d.ts.map +1 -0
- package/dist/resources/agent-guide.js +85 -0
- package/dist/resources/catalog-items.d.ts +10 -0
- package/dist/resources/catalog-items.d.ts.map +1 -0
- package/dist/resources/catalog-items.js +29 -0
- package/dist/resources/constraints.d.ts +9 -0
- package/dist/resources/constraints.d.ts.map +1 -0
- package/dist/resources/constraints.js +69 -0
- package/dist/resources/index.d.ts +15 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +23 -0
- package/dist/resources/scene-current.d.ts +9 -0
- package/dist/resources/scene-current.d.ts.map +1 -0
- package/dist/resources/scene-current.js +20 -0
- package/dist/resources/scene-summary.d.ts +10 -0
- package/dist/resources/scene-summary.d.ts.map +1 -0
- package/dist/resources/scene-summary.js +196 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +18 -0
- package/dist/storage/index.d.ts +13 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +14 -0
- package/dist/storage/slug.d.ts +21 -0
- package/dist/storage/slug.d.ts.map +1 -0
- package/dist/storage/slug.js +62 -0
- package/dist/storage/sqlite-driver.d.ts +18 -0
- package/dist/storage/sqlite-driver.d.ts.map +1 -0
- package/dist/storage/sqlite-driver.js +32 -0
- package/dist/storage/sqlite-scene-store.d.ts +51 -0
- package/dist/storage/sqlite-scene-store.d.ts.map +1 -0
- package/dist/storage/sqlite-scene-store.js +526 -0
- package/dist/storage/types.d.ts +136 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +28 -0
- package/dist/templates/empty-studio.d.ts +8 -0
- package/dist/templates/empty-studio.d.ts.map +1 -0
- package/dist/templates/empty-studio.js +230 -0
- package/dist/templates/garden-house.d.ts +8 -0
- package/dist/templates/garden-house.d.ts.map +1 -0
- package/dist/templates/garden-house.js +242 -0
- package/dist/templates/index.d.ts +23 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +20 -0
- package/dist/templates/two-bedroom.d.ts +8 -0
- package/dist/templates/two-bedroom.d.ts.map +1 -0
- package/dist/templates/two-bedroom.js +270 -0
- package/dist/tools/apply-patch.d.ts +25 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +59 -0
- package/dist/tools/asset-catalog.d.ts +16 -0
- package/dist/tools/asset-catalog.d.ts.map +1 -0
- package/dist/tools/asset-catalog.js +308 -0
- package/dist/tools/check-collisions.d.ts +15 -0
- package/dist/tools/check-collisions.d.ts.map +1 -0
- package/dist/tools/check-collisions.js +61 -0
- package/dist/tools/construction-tools.d.ts +93 -0
- package/dist/tools/construction-tools.d.ts.map +1 -0
- package/dist/tools/construction-tools.js +369 -0
- package/dist/tools/create-level.d.ts +14 -0
- package/dist/tools/create-level.d.ts.map +1 -0
- package/dist/tools/create-level.js +48 -0
- package/dist/tools/create-wall.d.ts +15 -0
- package/dist/tools/create-wall.d.ts.map +1 -0
- package/dist/tools/create-wall.js +50 -0
- package/dist/tools/cut-opening.d.ts +18 -0
- package/dist/tools/cut-opening.d.ts.map +1 -0
- package/dist/tools/cut-opening.js +57 -0
- package/dist/tools/delete-node.d.ts +12 -0
- package/dist/tools/delete-node.d.ts.map +1 -0
- package/dist/tools/delete-node.js +37 -0
- package/dist/tools/describe-node.d.ts +17 -0
- package/dist/tools/describe-node.d.ts.map +1 -0
- package/dist/tools/describe-node.js +86 -0
- package/dist/tools/duplicate-level.d.ts +12 -0
- package/dist/tools/duplicate-level.d.ts.map +1 -0
- package/dist/tools/duplicate-level.js +56 -0
- package/dist/tools/errors.d.ts +20 -0
- package/dist/tools/errors.d.ts.map +1 -0
- package/dist/tools/errors.js +25 -0
- package/dist/tools/export-glb.d.ts +10 -0
- package/dist/tools/export-glb.d.ts.map +1 -0
- package/dist/tools/export-glb.js +24 -0
- package/dist/tools/export-json.d.ts +11 -0
- package/dist/tools/export-json.d.ts.map +1 -0
- package/dist/tools/export-json.js +23 -0
- package/dist/tools/find-nodes.d.ts +32 -0
- package/dist/tools/find-nodes.d.ts.map +1 -0
- package/dist/tools/find-nodes.js +105 -0
- package/dist/tools/geometry.d.ts +24 -0
- package/dist/tools/geometry.d.ts.map +1 -0
- package/dist/tools/geometry.js +94 -0
- package/dist/tools/get-node.d.ts +11 -0
- package/dist/tools/get-node.d.ts.map +1 -0
- package/dist/tools/get-node.js +27 -0
- package/dist/tools/get-scene.d.ts +11 -0
- package/dist/tools/get-scene.d.ts.map +1 -0
- package/dist/tools/get-scene.js +26 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +64 -0
- package/dist/tools/live-sync.d.ts +11 -0
- package/dist/tools/live-sync.d.ts.map +1 -0
- package/dist/tools/live-sync.js +62 -0
- package/dist/tools/measure.d.ts +14 -0
- package/dist/tools/measure.d.ts.map +1 -0
- package/dist/tools/measure.js +137 -0
- package/dist/tools/photo-to-scene/index.d.ts +11 -0
- package/dist/tools/photo-to-scene/index.d.ts.map +1 -0
- package/dist/tools/photo-to-scene/index.js +11 -0
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts +26 -0
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -0
- package/dist/tools/photo-to-scene/photo-to-scene.js +323 -0
- package/dist/tools/place-item.d.ts +15 -0
- package/dist/tools/place-item.d.ts.map +1 -0
- package/dist/tools/place-item.js +84 -0
- package/dist/tools/redo.d.ts +11 -0
- package/dist/tools/redo.d.ts.map +1 -0
- package/dist/tools/redo.js +25 -0
- package/dist/tools/room-tools.d.ts +97 -0
- package/dist/tools/room-tools.d.ts.map +1 -0
- package/dist/tools/room-tools.js +510 -0
- package/dist/tools/scene-lifecycle/create-project.d.ts +33 -0
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/create-project.js +70 -0
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +12 -0
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/delete-scene.js +42 -0
- package/dist/tools/scene-lifecycle/get-project-status.d.ts +31 -0
- package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/get-project-status.js +69 -0
- package/dist/tools/scene-lifecycle/index.d.ts +17 -0
- package/dist/tools/scene-lifecycle/index.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/index.js +29 -0
- package/dist/tools/scene-lifecycle/list-scenes.d.ts +27 -0
- package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/list-scenes.js +49 -0
- package/dist/tools/scene-lifecycle/load-scene.d.ts +31 -0
- package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/load-scene.js +55 -0
- package/dist/tools/scene-lifecycle/metadata.d.ts +49 -0
- package/dist/tools/scene-lifecycle/metadata.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/metadata.js +68 -0
- package/dist/tools/scene-lifecycle/rename-scene.d.ts +22 -0
- package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/rename-scene.js +63 -0
- package/dist/tools/scene-lifecycle/save-scene.d.ts +42 -0
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/save-scene.js +130 -0
- package/dist/tools/scene-lifecycle/test-utils.d.ts +39 -0
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -0
- package/dist/tools/scene-lifecycle/test-utils.js +211 -0
- package/dist/tools/scene-query.d.ts +57 -0
- package/dist/tools/scene-query.d.ts.map +1 -0
- package/dist/tools/scene-query.js +645 -0
- package/dist/tools/schemas.d.ts +52 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +42 -0
- package/dist/tools/set-zone.d.ts +14 -0
- package/dist/tools/set-zone.d.ts.map +1 -0
- package/dist/tools/set-zone.js +48 -0
- package/dist/tools/templates/create-from-template.d.ts +54 -0
- package/dist/tools/templates/create-from-template.d.ts.map +1 -0
- package/dist/tools/templates/create-from-template.js +145 -0
- package/dist/tools/templates/create-house-from-brief.d.ts +35 -0
- package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -0
- package/dist/tools/templates/create-house-from-brief.js +170 -0
- package/dist/tools/templates/index.d.ts +14 -0
- package/dist/tools/templates/index.d.ts.map +1 -0
- package/dist/tools/templates/index.js +18 -0
- package/dist/tools/templates/list-templates.d.ts +18 -0
- package/dist/tools/templates/list-templates.d.ts.map +1 -0
- package/dist/tools/templates/list-templates.js +36 -0
- package/dist/tools/undo.d.ts +11 -0
- package/dist/tools/undo.d.ts.map +1 -0
- package/dist/tools/undo.js +25 -0
- package/dist/tools/validate-scene.d.ts +14 -0
- package/dist/tools/validate-scene.d.ts.map +1 -0
- package/dist/tools/validate-scene.js +24 -0
- package/dist/tools/variants/generate-variants.d.ts +30 -0
- package/dist/tools/variants/generate-variants.d.ts.map +1 -0
- package/dist/tools/variants/generate-variants.js +160 -0
- package/dist/tools/variants/index.d.ts +9 -0
- package/dist/tools/variants/index.d.ts.map +1 -0
- package/dist/tools/variants/index.js +9 -0
- package/dist/tools/variants/mutations.d.ts +18 -0
- package/dist/tools/variants/mutations.d.ts.map +1 -0
- package/dist/tools/variants/mutations.js +324 -0
- package/dist/tools/vision/analyze-floorplan-image.d.ts +33 -0
- package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -0
- package/dist/tools/vision/analyze-floorplan-image.js +146 -0
- package/dist/tools/vision/analyze-room-photo.d.ts +29 -0
- package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -0
- package/dist/tools/vision/analyze-room-photo.js +130 -0
- package/dist/tools/vision/index.d.ts +12 -0
- package/dist/tools/vision/index.d.ts.map +1 -0
- package/dist/tools/vision/index.js +14 -0
- package/dist/transports/http.d.ts +36 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +203 -0
- package/dist/transports/stdio.d.ts +14 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +16 -0
- package/package.json +86 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SceneOperations } from '../operations';
|
|
4
|
+
export declare const checkCollisionsInput: {
|
|
5
|
+
levelId: z.ZodOptional<z.ZodString>;
|
|
6
|
+
};
|
|
7
|
+
export declare const checkCollisionsOutput: {
|
|
8
|
+
collisions: z.ZodArray<z.ZodObject<{
|
|
9
|
+
aId: z.ZodString;
|
|
10
|
+
bId: z.ZodString;
|
|
11
|
+
kind: z.ZodString;
|
|
12
|
+
}, z.core.$strip>>;
|
|
13
|
+
};
|
|
14
|
+
export declare function registerCheckCollisions(server: McpServer, bridge: SceneOperations): void;
|
|
15
|
+
//# sourceMappingURL=check-collisions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-collisions.d.ts","sourceRoot":"","sources":["../../src/tools/check-collisions.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;AAGpD,eAAO,MAAM,oBAAoB;;CAEhC,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;CAQjC,CAAA;AAqBD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsCxF"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getScaledDimensions } from '@pascal-app/core/schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { NodeIdSchema } from './schemas';
|
|
4
|
+
export const checkCollisionsInput = {
|
|
5
|
+
levelId: NodeIdSchema.optional(),
|
|
6
|
+
};
|
|
7
|
+
export const checkCollisionsOutput = {
|
|
8
|
+
collisions: z.array(z.object({
|
|
9
|
+
aId: z.string(),
|
|
10
|
+
bId: z.string(),
|
|
11
|
+
kind: z.string(),
|
|
12
|
+
})),
|
|
13
|
+
};
|
|
14
|
+
function itemAabb(item) {
|
|
15
|
+
const [x, , z] = item.position;
|
|
16
|
+
const [w, , d] = getScaledDimensions(item);
|
|
17
|
+
const halfW = w / 2;
|
|
18
|
+
const halfD = d / 2;
|
|
19
|
+
return {
|
|
20
|
+
minX: x - halfW,
|
|
21
|
+
maxX: x + halfW,
|
|
22
|
+
minZ: z - halfD,
|
|
23
|
+
maxZ: z + halfD,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function aabbOverlap(a, b) {
|
|
27
|
+
return a.minX < b.maxX && a.maxX > b.minX && a.minZ < b.maxZ && a.maxZ > b.minZ;
|
|
28
|
+
}
|
|
29
|
+
export function registerCheckCollisions(server, bridge) {
|
|
30
|
+
server.registerTool('check_collisions', {
|
|
31
|
+
title: 'Check collisions',
|
|
32
|
+
description: 'Detect overlapping item footprints via an axis-aligned 2D bounding-box test. Optionally scoped to a single level.',
|
|
33
|
+
inputSchema: checkCollisionsInput,
|
|
34
|
+
outputSchema: checkCollisionsOutput,
|
|
35
|
+
}, async ({ levelId }) => {
|
|
36
|
+
const filter = { type: 'item' };
|
|
37
|
+
if (levelId)
|
|
38
|
+
filter.levelId = levelId;
|
|
39
|
+
const items = bridge.findNodes(filter);
|
|
40
|
+
const boxes = items.map((i) => ({ item: i, aabb: itemAabb(i) }));
|
|
41
|
+
const collisions = [];
|
|
42
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
43
|
+
for (let j = i + 1; j < boxes.length; j++) {
|
|
44
|
+
const a = boxes[i];
|
|
45
|
+
const b = boxes[j];
|
|
46
|
+
if (aabbOverlap(a.aabb, b.aabb)) {
|
|
47
|
+
collisions.push({
|
|
48
|
+
aId: a.item.id,
|
|
49
|
+
bId: b.item.id,
|
|
50
|
+
kind: 'item-aabb',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const payload = { collisions };
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
58
|
+
structuredContent: payload,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SceneOperations } from '../operations';
|
|
4
|
+
export declare const createStoryShellInput: {
|
|
5
|
+
levelId: z.ZodString;
|
|
6
|
+
footprint: z.ZodArray<z.ZodArray<z.ZodNumber>>;
|
|
7
|
+
wallHeight: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
wallThickness: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
createSlab: z.ZodDefault<z.ZodBoolean>;
|
|
10
|
+
createCeiling: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
slabElevation: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
ceilingHeight: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
namePrefix: z.ZodOptional<z.ZodString>;
|
|
14
|
+
wallMaterialPreset: z.ZodOptional<z.ZodString>;
|
|
15
|
+
slabMaterialPreset: z.ZodOptional<z.ZodString>;
|
|
16
|
+
ceilingMaterialPreset: z.ZodOptional<z.ZodString>;
|
|
17
|
+
};
|
|
18
|
+
export declare const createStoryShellOutput: {
|
|
19
|
+
levelId: z.ZodString;
|
|
20
|
+
wallIds: z.ZodArray<z.ZodString>;
|
|
21
|
+
slabId: z.ZodNullable<z.ZodString>;
|
|
22
|
+
ceilingId: z.ZodNullable<z.ZodString>;
|
|
23
|
+
createdIds: z.ZodArray<z.ZodString>;
|
|
24
|
+
};
|
|
25
|
+
export declare const createRoofInput: {
|
|
26
|
+
levelId: z.ZodString;
|
|
27
|
+
roofLevelId: z.ZodOptional<z.ZodString>;
|
|
28
|
+
useDedicatedRoofLevel: z.ZodDefault<z.ZodBoolean>;
|
|
29
|
+
roofLevelLabel: z.ZodDefault<z.ZodString>;
|
|
30
|
+
roofLevelElevation: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
roofLevelHeight: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
center: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
33
|
+
width: z.ZodNumber;
|
|
34
|
+
depth: z.ZodNumber;
|
|
35
|
+
roofType: z.ZodDefault<z.ZodEnum<{
|
|
36
|
+
flat: "flat";
|
|
37
|
+
hip: "hip";
|
|
38
|
+
gable: "gable";
|
|
39
|
+
shed: "shed";
|
|
40
|
+
gambrel: "gambrel";
|
|
41
|
+
dutch: "dutch";
|
|
42
|
+
mansard: "mansard";
|
|
43
|
+
}>>;
|
|
44
|
+
roofHeight: z.ZodDefault<z.ZodNumber>;
|
|
45
|
+
wallHeight: z.ZodDefault<z.ZodNumber>;
|
|
46
|
+
wallThickness: z.ZodDefault<z.ZodNumber>;
|
|
47
|
+
overhang: z.ZodDefault<z.ZodNumber>;
|
|
48
|
+
materialPreset: z.ZodOptional<z.ZodString>;
|
|
49
|
+
name: z.ZodOptional<z.ZodString>;
|
|
50
|
+
};
|
|
51
|
+
export declare const createRoofOutput: {
|
|
52
|
+
referenceLevelId: z.ZodString;
|
|
53
|
+
roofLevelId: z.ZodString;
|
|
54
|
+
createdRoofLevelId: z.ZodNullable<z.ZodString>;
|
|
55
|
+
roofId: z.ZodString;
|
|
56
|
+
roofSegmentId: z.ZodString;
|
|
57
|
+
};
|
|
58
|
+
export declare const createStairBetweenLevelsInput: {
|
|
59
|
+
fromLevelId: z.ZodString;
|
|
60
|
+
toLevelId: z.ZodString;
|
|
61
|
+
position: z.ZodArray<z.ZodNumber>;
|
|
62
|
+
rotation: z.ZodDefault<z.ZodNumber>;
|
|
63
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
64
|
+
runLength: z.ZodDefault<z.ZodNumber>;
|
|
65
|
+
totalRise: z.ZodDefault<z.ZodNumber>;
|
|
66
|
+
stepCount: z.ZodDefault<z.ZodNumber>;
|
|
67
|
+
railingMode: z.ZodDefault<z.ZodEnum<{
|
|
68
|
+
none: "none";
|
|
69
|
+
left: "left";
|
|
70
|
+
right: "right";
|
|
71
|
+
both: "both";
|
|
72
|
+
}>>;
|
|
73
|
+
destinationSlabId: z.ZodOptional<z.ZodString>;
|
|
74
|
+
sourceCeilingId: z.ZodOptional<z.ZodString>;
|
|
75
|
+
createDestinationSlabOpening: z.ZodDefault<z.ZodBoolean>;
|
|
76
|
+
createSourceCeilingOpening: z.ZodDefault<z.ZodBoolean>;
|
|
77
|
+
openingWidth: z.ZodOptional<z.ZodNumber>;
|
|
78
|
+
openingLength: z.ZodOptional<z.ZodNumber>;
|
|
79
|
+
openingOffset: z.ZodDefault<z.ZodNumber>;
|
|
80
|
+
openingCenter: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
81
|
+
openingRotation: z.ZodOptional<z.ZodNumber>;
|
|
82
|
+
materialPreset: z.ZodOptional<z.ZodString>;
|
|
83
|
+
name: z.ZodOptional<z.ZodString>;
|
|
84
|
+
};
|
|
85
|
+
export declare const createStairBetweenLevelsOutput: {
|
|
86
|
+
stairId: z.ZodString;
|
|
87
|
+
stairSegmentId: z.ZodString;
|
|
88
|
+
destinationSlabId: z.ZodNullable<z.ZodString>;
|
|
89
|
+
sourceCeilingId: z.ZodNullable<z.ZodString>;
|
|
90
|
+
openingPolygon: z.ZodArray<z.ZodArray<z.ZodNumber>>;
|
|
91
|
+
};
|
|
92
|
+
export declare function registerConstructionTools(server: McpServer, bridge: SceneOperations): void;
|
|
93
|
+
//# sourceMappingURL=construction-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"construction-tools.d.ts","sourceRoot":"","sources":["../../src/tools/construction-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAYxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAOpD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;CAajC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;CAMlC,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;CAiB3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;CAM5B,CAAA;AAED,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBzC,CAAA;AAED,eAAO,MAAM,8BAA8B;;;;;;CAM1C,CAAA;AA4GD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA8S1F"}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { CeilingNode, LevelNode, RoofNode, RoofSegmentNode, SlabNode, StairNode, StairSegmentNode, WallNode, } from '@pascal-app/core/schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { publishLiveSceneSnapshot } from './live-sync';
|
|
4
|
+
import { NodeIdSchema, Vec2Schema, Vec3Schema } from './schemas';
|
|
5
|
+
const ROOF_TYPES = ['hip', 'gable', 'shed', 'gambrel', 'dutch', 'mansard', 'flat'];
|
|
6
|
+
const RAILING_MODES = ['none', 'left', 'right', 'both'];
|
|
7
|
+
export const createStoryShellInput = {
|
|
8
|
+
levelId: NodeIdSchema,
|
|
9
|
+
footprint: z.array(Vec2Schema).min(3),
|
|
10
|
+
wallHeight: z.number().positive().default(2.8),
|
|
11
|
+
wallThickness: z.number().positive().default(0.16),
|
|
12
|
+
createSlab: z.boolean().default(true),
|
|
13
|
+
createCeiling: z.boolean().default(true),
|
|
14
|
+
slabElevation: z.number().default(0.1),
|
|
15
|
+
ceilingHeight: z.number().positive().optional(),
|
|
16
|
+
namePrefix: z.string().optional(),
|
|
17
|
+
wallMaterialPreset: z.string().optional(),
|
|
18
|
+
slabMaterialPreset: z.string().optional(),
|
|
19
|
+
ceilingMaterialPreset: z.string().optional(),
|
|
20
|
+
};
|
|
21
|
+
export const createStoryShellOutput = {
|
|
22
|
+
levelId: z.string(),
|
|
23
|
+
wallIds: z.array(z.string()),
|
|
24
|
+
slabId: z.string().nullable(),
|
|
25
|
+
ceilingId: z.string().nullable(),
|
|
26
|
+
createdIds: z.array(z.string()),
|
|
27
|
+
};
|
|
28
|
+
export const createRoofInput = {
|
|
29
|
+
levelId: NodeIdSchema,
|
|
30
|
+
roofLevelId: NodeIdSchema.optional(),
|
|
31
|
+
useDedicatedRoofLevel: z.boolean().default(true),
|
|
32
|
+
roofLevelLabel: z.string().default('Roof'),
|
|
33
|
+
roofLevelElevation: z.number().optional(),
|
|
34
|
+
roofLevelHeight: z.number().positive().optional(),
|
|
35
|
+
center: Vec3Schema.optional(),
|
|
36
|
+
width: z.number().positive(),
|
|
37
|
+
depth: z.number().positive(),
|
|
38
|
+
roofType: z.enum(ROOF_TYPES).default('hip'),
|
|
39
|
+
roofHeight: z.number().positive().default(1.8),
|
|
40
|
+
wallHeight: z.number().min(0).default(0.35),
|
|
41
|
+
wallThickness: z.number().positive().default(0.16),
|
|
42
|
+
overhang: z.number().min(0).default(0.45),
|
|
43
|
+
materialPreset: z.string().optional(),
|
|
44
|
+
name: z.string().optional(),
|
|
45
|
+
};
|
|
46
|
+
export const createRoofOutput = {
|
|
47
|
+
referenceLevelId: z.string(),
|
|
48
|
+
roofLevelId: z.string(),
|
|
49
|
+
createdRoofLevelId: z.string().nullable(),
|
|
50
|
+
roofId: z.string(),
|
|
51
|
+
roofSegmentId: z.string(),
|
|
52
|
+
};
|
|
53
|
+
export const createStairBetweenLevelsInput = {
|
|
54
|
+
fromLevelId: NodeIdSchema,
|
|
55
|
+
toLevelId: NodeIdSchema,
|
|
56
|
+
position: Vec3Schema,
|
|
57
|
+
rotation: z.number().default(0),
|
|
58
|
+
width: z.number().positive().default(1),
|
|
59
|
+
runLength: z.number().positive().default(3),
|
|
60
|
+
totalRise: z.number().positive().default(2.8),
|
|
61
|
+
stepCount: z.number().int().positive().default(14),
|
|
62
|
+
railingMode: z.enum(RAILING_MODES).default('both'),
|
|
63
|
+
destinationSlabId: NodeIdSchema.optional(),
|
|
64
|
+
sourceCeilingId: NodeIdSchema.optional(),
|
|
65
|
+
createDestinationSlabOpening: z.boolean().default(true),
|
|
66
|
+
createSourceCeilingOpening: z.boolean().default(true),
|
|
67
|
+
openingWidth: z.number().positive().optional(),
|
|
68
|
+
openingLength: z.number().positive().optional(),
|
|
69
|
+
openingOffset: z.number().min(0).default(0.15),
|
|
70
|
+
openingCenter: Vec2Schema.optional(),
|
|
71
|
+
openingRotation: z.number().optional(),
|
|
72
|
+
materialPreset: z.string().optional(),
|
|
73
|
+
name: z.string().optional(),
|
|
74
|
+
};
|
|
75
|
+
export const createStairBetweenLevelsOutput = {
|
|
76
|
+
stairId: z.string(),
|
|
77
|
+
stairSegmentId: z.string(),
|
|
78
|
+
destinationSlabId: z.string().nullable(),
|
|
79
|
+
sourceCeilingId: z.string().nullable(),
|
|
80
|
+
openingPolygon: z.array(Vec2Schema),
|
|
81
|
+
};
|
|
82
|
+
function textResult(payload) {
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
85
|
+
structuredContent: payload,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function assertNode(bridge, id, type) {
|
|
89
|
+
const node = bridge.getNode(id);
|
|
90
|
+
if (!node)
|
|
91
|
+
throw new Error(`${type} not found: ${id}`);
|
|
92
|
+
if (node.type !== type)
|
|
93
|
+
throw new Error(`Node ${id} is a ${node.type}, expected ${type}`);
|
|
94
|
+
return node;
|
|
95
|
+
}
|
|
96
|
+
function getBuildingIdForLevel(bridge, levelId) {
|
|
97
|
+
const building = bridge.getAncestry(levelId).find((node) => node.type === 'building');
|
|
98
|
+
if (!building) {
|
|
99
|
+
throw new Error(`Building ancestor not found for level: ${levelId}`);
|
|
100
|
+
}
|
|
101
|
+
return building.id;
|
|
102
|
+
}
|
|
103
|
+
function isRoofLevel(level) {
|
|
104
|
+
return (level.type === 'level' &&
|
|
105
|
+
typeof level.metadata === 'object' &&
|
|
106
|
+
level.metadata !== null &&
|
|
107
|
+
'role' in level.metadata &&
|
|
108
|
+
level.metadata.role === 'roof');
|
|
109
|
+
}
|
|
110
|
+
function nextLevelIndex(bridge, buildingId, referenceLevel) {
|
|
111
|
+
const existing = bridge
|
|
112
|
+
.getChildren(buildingId)
|
|
113
|
+
.filter((node) => node.type === 'level')
|
|
114
|
+
.map((level) => level.level);
|
|
115
|
+
const referenceIndex = referenceLevel.type === 'level' ? referenceLevel.level : 0;
|
|
116
|
+
const candidate = referenceIndex + 1;
|
|
117
|
+
return existing.includes(candidate) ? Math.max(candidate, ...existing) + 1 : candidate;
|
|
118
|
+
}
|
|
119
|
+
function nodesOnLevel(bridge, levelId) {
|
|
120
|
+
return Object.values(bridge.getNodes()).filter((node) => node.id !== levelId && bridge.resolveLevelId(node.id) === levelId);
|
|
121
|
+
}
|
|
122
|
+
function firstNodeOnLevel(bridge, levelId, type) {
|
|
123
|
+
return nodesOnLevel(bridge, levelId).find((node) => node.type === type) ?? null;
|
|
124
|
+
}
|
|
125
|
+
function rotatePoint(x, z, rotation) {
|
|
126
|
+
const cos = Math.cos(rotation);
|
|
127
|
+
const sin = Math.sin(rotation);
|
|
128
|
+
return [x * cos + z * sin, -x * sin + z * cos];
|
|
129
|
+
}
|
|
130
|
+
function rectangularOpening(args) {
|
|
131
|
+
const width = args.width + args.offset * 2;
|
|
132
|
+
const length = args.length + args.offset * 2;
|
|
133
|
+
const center = args.center ?? [
|
|
134
|
+
args.position[0],
|
|
135
|
+
args.position[2] + args.length / 2,
|
|
136
|
+
];
|
|
137
|
+
const rotation = args.openingRotation ?? args.rotation;
|
|
138
|
+
const halfW = width / 2;
|
|
139
|
+
const halfL = length / 2;
|
|
140
|
+
const local = [
|
|
141
|
+
[-halfW, -halfL],
|
|
142
|
+
[halfW, -halfL],
|
|
143
|
+
[halfW, halfL],
|
|
144
|
+
[-halfW, halfL],
|
|
145
|
+
];
|
|
146
|
+
return local.map(([x, z]) => {
|
|
147
|
+
const [rx, rz] = rotatePoint(x, z, rotation);
|
|
148
|
+
return [center[0] + rx, center[1] + rz];
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function withHole(surface, hole) {
|
|
152
|
+
return {
|
|
153
|
+
holes: [...(surface.holes ?? []), hole],
|
|
154
|
+
holeMetadata: [...(surface.holeMetadata ?? []), { source: 'manual' }],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export function registerConstructionTools(server, bridge) {
|
|
158
|
+
server.registerTool('create_story_shell', {
|
|
159
|
+
title: 'Create story shell',
|
|
160
|
+
description: 'Create one level-owned building shell from a footprint: perimeter walls plus optional slab and ceiling. Use once per story; do not make first-floor walls span multiple stories.',
|
|
161
|
+
inputSchema: createStoryShellInput,
|
|
162
|
+
outputSchema: createStoryShellOutput,
|
|
163
|
+
}, async ({ levelId, footprint, wallHeight, wallThickness, createSlab, createCeiling, slabElevation, ceilingHeight, namePrefix, wallMaterialPreset, slabMaterialPreset, ceilingMaterialPreset, }) => {
|
|
164
|
+
const level = assertNode(bridge, levelId, 'level');
|
|
165
|
+
if (isRoofLevel(level)) {
|
|
166
|
+
throw new Error(`Cannot create a story shell on roof support level ${levelId}; create or choose an occupied story level instead`);
|
|
167
|
+
}
|
|
168
|
+
const points = footprint;
|
|
169
|
+
const wallIds = [];
|
|
170
|
+
const patches = [];
|
|
171
|
+
for (let i = 0; i < points.length; i++) {
|
|
172
|
+
const wall = WallNode.parse({
|
|
173
|
+
name: namePrefix ? `${namePrefix} Wall ${i + 1}` : undefined,
|
|
174
|
+
start: points[i],
|
|
175
|
+
end: points[(i + 1) % points.length],
|
|
176
|
+
thickness: wallThickness,
|
|
177
|
+
height: wallHeight,
|
|
178
|
+
frontSide: 'exterior',
|
|
179
|
+
backSide: 'interior',
|
|
180
|
+
...(wallMaterialPreset ? { materialPreset: wallMaterialPreset } : {}),
|
|
181
|
+
metadata: { role: 'exterior', storyShell: true },
|
|
182
|
+
});
|
|
183
|
+
wallIds.push(wall.id);
|
|
184
|
+
patches.push({ op: 'create', node: wall, parentId: levelId });
|
|
185
|
+
}
|
|
186
|
+
let slabId = null;
|
|
187
|
+
if (createSlab) {
|
|
188
|
+
const slab = SlabNode.parse({
|
|
189
|
+
name: namePrefix ? `${namePrefix} Slab` : undefined,
|
|
190
|
+
polygon: points,
|
|
191
|
+
elevation: slabElevation,
|
|
192
|
+
...(slabMaterialPreset ? { materialPreset: slabMaterialPreset } : {}),
|
|
193
|
+
metadata: { role: 'story-slab' },
|
|
194
|
+
});
|
|
195
|
+
slabId = slab.id;
|
|
196
|
+
patches.push({ op: 'create', node: slab, parentId: levelId });
|
|
197
|
+
}
|
|
198
|
+
let ceilingId = null;
|
|
199
|
+
if (createCeiling) {
|
|
200
|
+
const ceiling = CeilingNode.parse({
|
|
201
|
+
name: namePrefix ? `${namePrefix} Ceiling` : undefined,
|
|
202
|
+
polygon: points,
|
|
203
|
+
height: ceilingHeight ?? wallHeight,
|
|
204
|
+
...(ceilingMaterialPreset ? { materialPreset: ceilingMaterialPreset } : {}),
|
|
205
|
+
metadata: { role: 'story-ceiling' },
|
|
206
|
+
});
|
|
207
|
+
ceilingId = ceiling.id;
|
|
208
|
+
patches.push({ op: 'create', node: ceiling, parentId: levelId });
|
|
209
|
+
}
|
|
210
|
+
const result = bridge.applyPatch(patches);
|
|
211
|
+
await publishLiveSceneSnapshot(bridge, 'create_story_shell');
|
|
212
|
+
return textResult({
|
|
213
|
+
levelId,
|
|
214
|
+
wallIds,
|
|
215
|
+
slabId,
|
|
216
|
+
ceilingId,
|
|
217
|
+
createdIds: result.createdIds,
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
server.registerTool('create_roof', {
|
|
221
|
+
title: 'Create roof',
|
|
222
|
+
description: 'Create a roof container with one roof segment. By default creates a dedicated roof level above the reference level so exploded/solo level views can isolate the roof.',
|
|
223
|
+
inputSchema: createRoofInput,
|
|
224
|
+
outputSchema: createRoofOutput,
|
|
225
|
+
}, async ({ levelId, roofLevelId, useDedicatedRoofLevel, roofLevelLabel, roofLevelElevation, roofLevelHeight, center, width, depth, roofType, roofHeight, wallHeight, wallThickness, overhang, materialPreset, name, }) => {
|
|
226
|
+
const referenceLevel = assertNode(bridge, levelId, 'level');
|
|
227
|
+
const patches = [];
|
|
228
|
+
let targetRoofLevelId = levelId;
|
|
229
|
+
let createdRoofLevelId = null;
|
|
230
|
+
if (roofLevelId !== undefined) {
|
|
231
|
+
const roofLevel = assertNode(bridge, roofLevelId, 'level');
|
|
232
|
+
if (!isRoofLevel(roofLevel)) {
|
|
233
|
+
throw new Error(`roofLevelId ${roofLevelId} must reference a dedicated roof level with metadata.role = "roof"; omit roofLevelId to create one automatically`);
|
|
234
|
+
}
|
|
235
|
+
targetRoofLevelId = roofLevelId;
|
|
236
|
+
}
|
|
237
|
+
else if (useDedicatedRoofLevel && !isRoofLevel(referenceLevel)) {
|
|
238
|
+
const buildingId = getBuildingIdForLevel(bridge, levelId);
|
|
239
|
+
const roofLevel = LevelNode.parse({
|
|
240
|
+
name: roofLevelLabel,
|
|
241
|
+
level: roofLevelElevation ?? nextLevelIndex(bridge, buildingId, referenceLevel),
|
|
242
|
+
children: [],
|
|
243
|
+
metadata: {
|
|
244
|
+
role: 'roof',
|
|
245
|
+
label: roofLevelLabel,
|
|
246
|
+
referenceLevelId: levelId,
|
|
247
|
+
height: roofLevelHeight ?? Math.max(wallHeight + roofHeight, 0.2),
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
targetRoofLevelId = roofLevel.id;
|
|
251
|
+
createdRoofLevelId = roofLevel.id;
|
|
252
|
+
patches.push({ op: 'create', node: roofLevel, parentId: buildingId });
|
|
253
|
+
}
|
|
254
|
+
const segment = RoofSegmentNode.parse({
|
|
255
|
+
roofType,
|
|
256
|
+
width,
|
|
257
|
+
depth,
|
|
258
|
+
wallHeight,
|
|
259
|
+
roofHeight,
|
|
260
|
+
wallThickness,
|
|
261
|
+
overhang,
|
|
262
|
+
...(materialPreset ? { materialPreset } : {}),
|
|
263
|
+
});
|
|
264
|
+
const roof = RoofNode.parse({
|
|
265
|
+
name: name ?? 'Roof',
|
|
266
|
+
position: center ?? [0, 0, 0],
|
|
267
|
+
children: [segment.id],
|
|
268
|
+
...(materialPreset ? { materialPreset } : {}),
|
|
269
|
+
metadata: {
|
|
270
|
+
referenceLevelId: levelId,
|
|
271
|
+
roofLevelId: targetRoofLevelId,
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
bridge.applyPatch([
|
|
275
|
+
...patches,
|
|
276
|
+
{ op: 'create', node: roof, parentId: targetRoofLevelId },
|
|
277
|
+
{ op: 'create', node: segment, parentId: roof.id },
|
|
278
|
+
]);
|
|
279
|
+
await publishLiveSceneSnapshot(bridge, 'create_roof');
|
|
280
|
+
return textResult({
|
|
281
|
+
referenceLevelId: levelId,
|
|
282
|
+
roofLevelId: targetRoofLevelId,
|
|
283
|
+
createdRoofLevelId,
|
|
284
|
+
roofId: roof.id,
|
|
285
|
+
roofSegmentId: segment.id,
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
server.registerTool('create_stair_between_levels', {
|
|
289
|
+
title: 'Create stair between levels',
|
|
290
|
+
description: 'Create a straight stair and a single rectangular manual opening in the destination slab/source ceiling. This disables stair auto-opening mode to avoid duplicate or irregular holes.',
|
|
291
|
+
inputSchema: createStairBetweenLevelsInput,
|
|
292
|
+
outputSchema: createStairBetweenLevelsOutput,
|
|
293
|
+
}, async ({ fromLevelId, toLevelId, position, rotation, width, runLength, totalRise, stepCount, railingMode, destinationSlabId, sourceCeilingId, createDestinationSlabOpening, createSourceCeilingOpening, openingWidth, openingLength, openingOffset, openingCenter, openingRotation, materialPreset, name, }) => {
|
|
294
|
+
const fromLevel = assertNode(bridge, fromLevelId, 'level');
|
|
295
|
+
const toLevel = assertNode(bridge, toLevelId, 'level');
|
|
296
|
+
if (isRoofLevel(fromLevel) || isRoofLevel(toLevel)) {
|
|
297
|
+
throw new Error('Roof support levels are not occupied stories; create a separate occupied attic/story level if a stair-accessible attic is required');
|
|
298
|
+
}
|
|
299
|
+
const segment = StairSegmentNode.parse({
|
|
300
|
+
segmentType: 'stair',
|
|
301
|
+
width,
|
|
302
|
+
length: runLength,
|
|
303
|
+
height: totalRise,
|
|
304
|
+
stepCount,
|
|
305
|
+
...(materialPreset ? { materialPreset } : {}),
|
|
306
|
+
});
|
|
307
|
+
const stair = StairNode.parse({
|
|
308
|
+
name: name ?? 'Stair',
|
|
309
|
+
position: position,
|
|
310
|
+
rotation,
|
|
311
|
+
stairType: 'straight',
|
|
312
|
+
fromLevelId,
|
|
313
|
+
toLevelId,
|
|
314
|
+
slabOpeningMode: 'none',
|
|
315
|
+
openingOffset,
|
|
316
|
+
width,
|
|
317
|
+
totalRise,
|
|
318
|
+
stepCount,
|
|
319
|
+
railingMode,
|
|
320
|
+
children: [segment.id],
|
|
321
|
+
...(materialPreset ? { materialPreset } : {}),
|
|
322
|
+
metadata: {
|
|
323
|
+
openingManaged: 'manual-rectangular',
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
const openingPolygon = rectangularOpening({
|
|
327
|
+
position: position,
|
|
328
|
+
rotation,
|
|
329
|
+
width: openingWidth ?? width,
|
|
330
|
+
length: openingLength ?? runLength,
|
|
331
|
+
offset: openingOffset,
|
|
332
|
+
center: openingCenter,
|
|
333
|
+
openingRotation,
|
|
334
|
+
});
|
|
335
|
+
const patches = [
|
|
336
|
+
{ op: 'create', node: stair, parentId: fromLevelId },
|
|
337
|
+
{ op: 'create', node: segment, parentId: stair.id },
|
|
338
|
+
];
|
|
339
|
+
const destinationSlab = destinationSlabId !== undefined
|
|
340
|
+
? assertNode(bridge, destinationSlabId, 'slab')
|
|
341
|
+
: firstNodeOnLevel(bridge, toLevelId, 'slab');
|
|
342
|
+
if (createDestinationSlabOpening && destinationSlab?.type === 'slab') {
|
|
343
|
+
patches.push({
|
|
344
|
+
op: 'update',
|
|
345
|
+
id: destinationSlab.id,
|
|
346
|
+
data: withHole(destinationSlab, openingPolygon),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
const sourceCeiling = sourceCeilingId !== undefined
|
|
350
|
+
? assertNode(bridge, sourceCeilingId, 'ceiling')
|
|
351
|
+
: firstNodeOnLevel(bridge, fromLevelId, 'ceiling');
|
|
352
|
+
if (createSourceCeilingOpening && sourceCeiling?.type === 'ceiling') {
|
|
353
|
+
patches.push({
|
|
354
|
+
op: 'update',
|
|
355
|
+
id: sourceCeiling.id,
|
|
356
|
+
data: withHole(sourceCeiling, openingPolygon),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
bridge.applyPatch(patches);
|
|
360
|
+
await publishLiveSceneSnapshot(bridge, 'create_stair_between_levels');
|
|
361
|
+
return textResult({
|
|
362
|
+
stairId: stair.id,
|
|
363
|
+
stairSegmentId: segment.id,
|
|
364
|
+
destinationSlabId: destinationSlab?.id ?? null,
|
|
365
|
+
sourceCeilingId: sourceCeiling?.id ?? null,
|
|
366
|
+
openingPolygon,
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SceneOperations } from '../operations';
|
|
4
|
+
export declare const createLevelInput: {
|
|
5
|
+
buildingId: z.ZodString;
|
|
6
|
+
elevation: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
label: z.ZodOptional<z.ZodString>;
|
|
9
|
+
};
|
|
10
|
+
export declare const createLevelOutput: {
|
|
11
|
+
levelId: z.ZodString;
|
|
12
|
+
};
|
|
13
|
+
export declare function registerCreateLevel(server: McpServer, bridge: SceneOperations): void;
|
|
14
|
+
//# sourceMappingURL=create-level.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAKpD,eAAO,MAAM,gBAAgB;;;;;CAK5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0CpF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { LevelNode } from '@pascal-app/core/schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ErrorCode, throwMcpError } from './errors';
|
|
4
|
+
import { publishLiveSceneSnapshot } from './live-sync';
|
|
5
|
+
import { NodeIdSchema } from './schemas';
|
|
6
|
+
export const createLevelInput = {
|
|
7
|
+
buildingId: NodeIdSchema,
|
|
8
|
+
elevation: z.number().optional(),
|
|
9
|
+
height: z.number().optional(),
|
|
10
|
+
label: z.string().optional(),
|
|
11
|
+
};
|
|
12
|
+
export const createLevelOutput = {
|
|
13
|
+
levelId: z.string(),
|
|
14
|
+
};
|
|
15
|
+
export function registerCreateLevel(server, bridge) {
|
|
16
|
+
server.registerTool('create_level', {
|
|
17
|
+
title: 'Create level',
|
|
18
|
+
description: 'Create a new level node attached to the given building. height and label are stored in metadata.',
|
|
19
|
+
inputSchema: createLevelInput,
|
|
20
|
+
outputSchema: createLevelOutput,
|
|
21
|
+
}, async ({ buildingId, elevation, height, label }) => {
|
|
22
|
+
const parent = bridge.getNode(buildingId);
|
|
23
|
+
if (!parent) {
|
|
24
|
+
throwMcpError(ErrorCode.InvalidParams, `Building not found: ${buildingId}`);
|
|
25
|
+
}
|
|
26
|
+
if (parent.type !== 'building') {
|
|
27
|
+
throwMcpError(ErrorCode.InvalidParams, `Node ${buildingId} is a ${parent.type}, expected building`);
|
|
28
|
+
}
|
|
29
|
+
const metadata = {};
|
|
30
|
+
if (height !== undefined)
|
|
31
|
+
metadata.height = height;
|
|
32
|
+
if (label !== undefined)
|
|
33
|
+
metadata.label = label;
|
|
34
|
+
const levelNode = LevelNode.parse({
|
|
35
|
+
level: elevation ?? 0,
|
|
36
|
+
children: [],
|
|
37
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
38
|
+
...(label !== undefined ? { name: label } : {}),
|
|
39
|
+
});
|
|
40
|
+
const id = bridge.createNode(levelNode, buildingId);
|
|
41
|
+
await publishLiveSceneSnapshot(bridge, 'create_level');
|
|
42
|
+
const payload = { levelId: id };
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
45
|
+
structuredContent: payload,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SceneOperations } from '../operations';
|
|
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.ZodNumber>;
|
|
9
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
};
|
|
11
|
+
export declare const createWallOutput: {
|
|
12
|
+
wallId: z.ZodString;
|
|
13
|
+
};
|
|
14
|
+
export declare function registerCreateWall(server: McpServer, bridge: SceneOperations): void;
|
|
15
|
+
//# sourceMappingURL=create-wall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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;AAKpD,eAAO,MAAM,eAAe;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;CAE5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgDnF"}
|