@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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/bin/pascal-mcp.d.ts +1 -1
- package/dist/bin/pascal-mcp.js +7 -7
- package/dist/bridge/index.d.ts +2 -2
- package/dist/bridge/index.js +1 -1
- package/dist/bridge/scene-bridge.d.ts +2 -2
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +3 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/operations/index.d.ts +1 -1
- package/dist/operations/index.js +1 -1
- package/dist/operations/scene-operations.d.ts +2 -2
- package/dist/prompts/from-brief.d.ts +1 -1
- package/dist/prompts/from-brief.d.ts.map +1 -1
- package/dist/prompts/from-brief.js +2 -1
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.js +3 -3
- package/dist/prompts/iterate-on-feedback.d.ts +1 -1
- package/dist/prompts/iterate-on-feedback.js +1 -1
- package/dist/prompts/renovation-from-photos.d.ts +1 -1
- package/dist/resources/agent-guide.d.ts +1 -1
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +1 -0
- package/dist/resources/catalog-items.d.ts +1 -1
- package/dist/resources/catalog-items.js +1 -1
- package/dist/resources/constraints.d.ts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.js +5 -5
- package/dist/resources/scene-current.d.ts +1 -1
- package/dist/resources/scene-summary.d.ts +1 -1
- package/dist/server.d.ts +14 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +60 -6
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +4 -4
- package/dist/storage/sqlite-scene-store.d.ts +1 -1
- package/dist/storage/sqlite-scene-store.js +3 -3
- package/dist/templates/index.js +3 -3
- package/dist/tools/annotations.d.ts +28 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +27 -0
- package/dist/tools/apply-patch.d.ts +8 -1
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +9 -5
- package/dist/tools/check-collisions.d.ts +60 -1
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +242 -21
- package/dist/tools/construction-tools.d.ts +24 -2
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +36 -10
- package/dist/tools/create-level.d.ts +8 -1
- package/dist/tools/create-level.d.ts.map +1 -1
- package/dist/tools/create-level.js +9 -6
- package/dist/tools/create-unit.d.ts +27 -0
- package/dist/tools/create-unit.d.ts.map +1 -0
- package/dist/tools/create-unit.js +36 -0
- package/dist/tools/create-wall.d.ts +8 -1
- package/dist/tools/create-wall.d.ts.map +1 -1
- package/dist/tools/create-wall.js +9 -6
- package/dist/tools/cut-opening.d.ts +8 -1
- package/dist/tools/cut-opening.d.ts.map +1 -1
- package/dist/tools/cut-opening.js +10 -7
- package/dist/tools/delete-node.d.ts +8 -1
- package/dist/tools/delete-node.d.ts.map +1 -1
- package/dist/tools/delete-node.js +8 -5
- package/dist/tools/describe-node.d.ts +1 -1
- package/dist/tools/describe-node.d.ts.map +1 -1
- package/dist/tools/describe-node.js +5 -3
- package/dist/tools/door-clearance.d.ts +19 -3
- package/dist/tools/door-clearance.d.ts.map +1 -1
- package/dist/tools/door-clearance.js +102 -8
- package/dist/tools/duplicate-level.d.ts +8 -1
- package/dist/tools/duplicate-level.d.ts.map +1 -1
- package/dist/tools/duplicate-level.js +8 -4
- package/dist/tools/export-glb.d.ts +1 -1
- package/dist/tools/export-glb.d.ts.map +1 -1
- package/dist/tools/export-glb.js +4 -2
- package/dist/tools/export-json.d.ts +1 -1
- package/dist/tools/export-json.d.ts.map +1 -1
- package/dist/tools/export-json.js +2 -0
- package/dist/tools/find-nodes.d.ts +1 -1
- package/dist/tools/find-nodes.d.ts.map +1 -1
- package/dist/tools/find-nodes.js +3 -1
- package/dist/tools/get-node.d.ts +1 -1
- package/dist/tools/get-node.d.ts.map +1 -1
- package/dist/tools/get-node.js +4 -2
- package/dist/tools/get-scene.d.ts +1 -1
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +2 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +32 -26
- package/dist/tools/layout-clearance.d.ts +4 -2
- package/dist/tools/layout-clearance.d.ts.map +1 -1
- package/dist/tools/layout-clearance.js +7 -3
- package/dist/tools/list-units.d.ts +38 -0
- package/dist/tools/list-units.d.ts.map +1 -0
- package/dist/tools/list-units.js +61 -0
- package/dist/tools/live-sync.d.ts +34 -4
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +38 -6
- package/dist/tools/measure.d.ts +2 -1
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +6 -2
- package/dist/tools/normalize-schema-dialect.d.ts +3 -0
- package/dist/tools/normalize-schema-dialect.d.ts.map +1 -0
- package/dist/tools/normalize-schema-dialect.js +28 -0
- package/dist/tools/photo-to-scene/index.d.ts +2 -2
- package/dist/tools/photo-to-scene/index.js +2 -2
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +5 -3
- package/dist/tools/place-item.d.ts +8 -1
- package/dist/tools/place-item.d.ts.map +1 -1
- package/dist/tools/place-item.js +11 -7
- package/dist/tools/redo.d.ts +8 -1
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +6 -4
- package/dist/tools/room-tools.d.ts +29 -1
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +27 -12
- package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +4 -2
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +4 -2
- package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.js +4 -2
- package/dist/tools/scene-lifecycle/index.d.ts +8 -8
- package/dist/tools/scene-lifecycle/index.js +14 -14
- package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.js +3 -1
- package/dist/tools/scene-lifecycle/load-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/load-scene.js +4 -2
- package/dist/tools/scene-lifecycle/metadata.d.ts +3 -3
- package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.js +4 -2
- package/dist/tools/scene-lifecycle/save-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +6 -4
- package/dist/tools/scene-lifecycle/test-utils.d.ts +7 -3
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/test-utils.js +28 -4
- package/dist/tools/scene-query.d.ts +1 -1
- package/dist/tools/scene-query.d.ts.map +1 -1
- package/dist/tools/scene-query.js +9 -3
- package/dist/tools/set-unit-members.d.ts +20 -0
- package/dist/tools/set-unit-members.d.ts.map +1 -0
- package/dist/tools/set-unit-members.js +43 -0
- package/dist/tools/set-zone.d.ts +8 -1
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +8 -5
- package/dist/tools/templates/create-from-template.d.ts +1 -1
- package/dist/tools/templates/create-from-template.d.ts.map +1 -1
- package/dist/tools/templates/create-from-template.js +7 -5
- package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
- package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
- package/dist/tools/templates/create-house-from-brief.js +7 -5
- package/dist/tools/templates/index.d.ts +4 -4
- package/dist/tools/templates/index.js +6 -6
- package/dist/tools/templates/list-templates.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.js +3 -1
- package/dist/tools/undo.d.ts +8 -1
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +6 -4
- package/dist/tools/unit-members.d.ts +4 -0
- package/dist/tools/unit-members.d.ts.map +1 -0
- package/dist/tools/unit-members.js +24 -0
- package/dist/tools/validate-scene.d.ts +1 -1
- package/dist/tools/validate-scene.d.ts.map +1 -1
- package/dist/tools/validate-scene.js +2 -0
- package/dist/tools/variants/generate-variants.d.ts +1 -1
- package/dist/tools/variants/generate-variants.d.ts.map +1 -1
- package/dist/tools/variants/generate-variants.js +4 -2
- package/dist/tools/variants/index.d.ts +3 -3
- package/dist/tools/variants/index.js +3 -3
- package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
- package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
- package/dist/tools/vision/analyze-floorplan-image.js +3 -1
- package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
- package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
- package/dist/tools/vision/analyze-room-photo.js +3 -1
- package/dist/tools/vision/index.d.ts +3 -3
- package/dist/tools/vision/index.js +4 -4
- package/package.json +8 -7
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
3
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
4
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
5
|
+
import { PatchSchema } from './schemas.js';
|
|
5
6
|
export const applyPatchInput = {
|
|
6
7
|
patches: z.array(PatchSchema),
|
|
7
8
|
};
|
|
@@ -9,13 +10,15 @@ export const applyPatchOutput = {
|
|
|
9
10
|
appliedOps: z.number(),
|
|
10
11
|
deletedIds: z.array(z.string()),
|
|
11
12
|
createdIds: z.array(z.string()),
|
|
13
|
+
...liveSyncOutput,
|
|
12
14
|
};
|
|
13
15
|
export function registerApplyPatch(server, bridge) {
|
|
14
16
|
server.registerTool('apply_patch', {
|
|
15
17
|
title: 'Apply patch',
|
|
16
|
-
description: 'Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step.',
|
|
18
|
+
description: 'Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls.',
|
|
17
19
|
inputSchema: applyPatchInput,
|
|
18
20
|
outputSchema: applyPatchOutput,
|
|
21
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
19
22
|
}, async ({ patches }) => {
|
|
20
23
|
const bridgePatches = patches.map((p) => {
|
|
21
24
|
if (p.op === 'create') {
|
|
@@ -40,11 +43,12 @@ export function registerApplyPatch(server, bridge) {
|
|
|
40
43
|
});
|
|
41
44
|
try {
|
|
42
45
|
const result = bridge.applyPatch(bridgePatches);
|
|
43
|
-
await publishLiveSceneSnapshot(bridge, 'apply_patch');
|
|
46
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'apply_patch');
|
|
44
47
|
const payload = {
|
|
45
48
|
appliedOps: result.appliedOps,
|
|
46
49
|
deletedIds: result.deletedIds,
|
|
47
50
|
createdIds: result.createdIds,
|
|
51
|
+
...persistencePayload(persistence),
|
|
48
52
|
};
|
|
49
53
|
return {
|
|
50
54
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
@@ -1,14 +1,73 @@
|
|
|
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 checkCollisionsInput: {
|
|
5
5
|
levelId: z.ZodOptional<z.ZodString>;
|
|
6
|
+
minimumClearance: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
7
|
+
floorOnly: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
candidate: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
id: z.ZodDefault<z.ZodString>;
|
|
10
|
+
name: z.ZodDefault<z.ZodString>;
|
|
11
|
+
levelId: z.ZodString;
|
|
12
|
+
position: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>, z.ZodTransform<[number, number, number], number[]>>;
|
|
13
|
+
dimensions: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>, z.ZodTransform<[number, number, number], number[]>>;
|
|
14
|
+
rotationY: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
15
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
assetId: z.ZodOptional<z.ZodString>;
|
|
17
|
+
uri: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>>;
|
|
19
|
+
}, z.core.$strip>>;
|
|
6
20
|
};
|
|
7
21
|
export declare const checkCollisionsOutput: {
|
|
22
|
+
status: z.ZodEnum<{
|
|
23
|
+
checked: "checked";
|
|
24
|
+
partial: "partial";
|
|
25
|
+
insufficient_evidence: "insufficient_evidence";
|
|
26
|
+
}>;
|
|
27
|
+
units: z.ZodLiteral<"meters">;
|
|
28
|
+
method: z.ZodLiteral<"rotation-aware-plan-aabb">;
|
|
29
|
+
assessmentGraphHash: z.ZodString;
|
|
30
|
+
minimumClearanceMeters: z.ZodNumber;
|
|
31
|
+
floorOnly: z.ZodBoolean;
|
|
32
|
+
candidateItemId: z.ZodNullable<z.ZodString>;
|
|
33
|
+
checkedItems: z.ZodArray<z.ZodObject<{
|
|
34
|
+
id: z.ZodString;
|
|
35
|
+
name: z.ZodString;
|
|
36
|
+
levelId: z.ZodNullable<z.ZodString>;
|
|
37
|
+
positionMeters: z.ZodArray<z.ZodNumber>;
|
|
38
|
+
rotationYRadians: z.ZodNumber;
|
|
39
|
+
source: z.ZodObject<{
|
|
40
|
+
assetId: z.ZodString;
|
|
41
|
+
uri: z.ZodString;
|
|
42
|
+
catalog: z.ZodString;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
sourceDimensionsMeters: z.ZodArray<z.ZodNumber>;
|
|
45
|
+
effectiveDimensionsMeters: z.ZodArray<z.ZodNumber>;
|
|
46
|
+
footprintBoundsMeters: z.ZodObject<{
|
|
47
|
+
minX: z.ZodNumber;
|
|
48
|
+
maxX: z.ZodNumber;
|
|
49
|
+
minZ: z.ZodNumber;
|
|
50
|
+
maxZ: z.ZodNumber;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
}, z.core.$strip>>;
|
|
53
|
+
skippedItems: z.ZodArray<z.ZodObject<{
|
|
54
|
+
id: z.ZodString;
|
|
55
|
+
name: z.ZodString;
|
|
56
|
+
reason: z.ZodString;
|
|
57
|
+
}, z.core.$strip>>;
|
|
58
|
+
unsupportedChecks: z.ZodArray<z.ZodObject<{
|
|
59
|
+
check: z.ZodString;
|
|
60
|
+
reason: z.ZodString;
|
|
61
|
+
}, z.core.$strip>>;
|
|
8
62
|
collisions: z.ZodArray<z.ZodObject<{
|
|
9
63
|
aId: z.ZodString;
|
|
10
64
|
bId: z.ZodString;
|
|
11
65
|
kind: z.ZodString;
|
|
66
|
+
violation: z.ZodEnum<{
|
|
67
|
+
overlap: "overlap";
|
|
68
|
+
clearance: "clearance";
|
|
69
|
+
}>;
|
|
70
|
+
minimumClearanceMeters: z.ZodNumber;
|
|
12
71
|
}, z.core.$strip>>;
|
|
13
72
|
};
|
|
14
73
|
export declare function registerCheckCollisions(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +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;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
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;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA8BpD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;CA+BhC,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDjC,CAAA;AA6BD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAyKxF"}
|
|
@@ -1,46 +1,267 @@
|
|
|
1
|
+
import { ItemNode } from '@pascal-app/core/schema';
|
|
1
2
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
+
import { inspectItemPlanFootprint, resolveNodeLevelId } from './door-clearance.js';
|
|
5
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
6
|
+
import { findItemItemCollisions } from './layout-clearance.js';
|
|
7
|
+
import { measurement } from './measurement.js';
|
|
8
|
+
import { computeGraphHash } from './scene-lifecycle/metadata.js';
|
|
9
|
+
import { NodeIdSchema } from './schemas.js';
|
|
10
|
+
const candidatePosition = measurement('length', 'm', {
|
|
11
|
+
description: 'Candidate position coordinate.',
|
|
12
|
+
});
|
|
13
|
+
const candidateDimension = measurement('length', 'm', {
|
|
14
|
+
positive: true,
|
|
15
|
+
description: 'Candidate declared size.',
|
|
16
|
+
});
|
|
17
|
+
const vector3 = (component) => z
|
|
18
|
+
.array(component)
|
|
19
|
+
.length(3)
|
|
20
|
+
.transform((values, ctx) => {
|
|
21
|
+
const [x, y, zValue] = values;
|
|
22
|
+
if (x === undefined || y === undefined || zValue === undefined) {
|
|
23
|
+
ctx.addIssue({ code: 'custom', message: 'Expected exactly three values.' });
|
|
24
|
+
return z.NEVER;
|
|
25
|
+
}
|
|
26
|
+
return [x, y, zValue];
|
|
27
|
+
});
|
|
28
|
+
const outputVector3 = z.array(z.number()).length(3);
|
|
4
29
|
export const checkCollisionsInput = {
|
|
5
30
|
levelId: NodeIdSchema.optional(),
|
|
31
|
+
minimumClearance: measurement('length', 'm', {
|
|
32
|
+
min: 0,
|
|
33
|
+
description: 'Minimum free plan-space required between item footprints.',
|
|
34
|
+
}).default(0),
|
|
35
|
+
floorOnly: z
|
|
36
|
+
.boolean()
|
|
37
|
+
.default(false)
|
|
38
|
+
.describe('When true, wall-, wall-side-, and ceiling-attached items are excluded.'),
|
|
39
|
+
candidate: z
|
|
40
|
+
.object({
|
|
41
|
+
id: z.string().min(1).max(120).default('candidate'),
|
|
42
|
+
name: z.string().min(1).max(200).default('Candidate item'),
|
|
43
|
+
levelId: NodeIdSchema,
|
|
44
|
+
position: vector3(candidatePosition),
|
|
45
|
+
dimensions: vector3(candidateDimension),
|
|
46
|
+
rotationY: measurement('angle', 'rad', {
|
|
47
|
+
description: 'Candidate yaw around the vertical axis.',
|
|
48
|
+
}).default(0),
|
|
49
|
+
source: z
|
|
50
|
+
.object({
|
|
51
|
+
assetId: z.string().min(1).optional(),
|
|
52
|
+
uri: z.string().min(1).optional(),
|
|
53
|
+
})
|
|
54
|
+
.optional(),
|
|
55
|
+
})
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('Read-only prospective furniture item. It is checked against the target level but never added to the scene.'),
|
|
6
58
|
};
|
|
7
59
|
export const checkCollisionsOutput = {
|
|
60
|
+
status: z.enum(['checked', 'partial', 'insufficient_evidence']),
|
|
61
|
+
units: z.literal('meters'),
|
|
62
|
+
method: z.literal('rotation-aware-plan-aabb'),
|
|
63
|
+
assessmentGraphHash: z.string(),
|
|
64
|
+
minimumClearanceMeters: z.number(),
|
|
65
|
+
floorOnly: z.boolean(),
|
|
66
|
+
candidateItemId: z.string().nullable(),
|
|
67
|
+
checkedItems: z.array(z.object({
|
|
68
|
+
id: z.string(),
|
|
69
|
+
name: z.string(),
|
|
70
|
+
levelId: z.string().nullable(),
|
|
71
|
+
positionMeters: outputVector3,
|
|
72
|
+
rotationYRadians: z.number(),
|
|
73
|
+
source: z.object({
|
|
74
|
+
assetId: z.string(),
|
|
75
|
+
uri: z.string(),
|
|
76
|
+
catalog: z.string(),
|
|
77
|
+
}),
|
|
78
|
+
sourceDimensionsMeters: outputVector3,
|
|
79
|
+
effectiveDimensionsMeters: outputVector3,
|
|
80
|
+
footprintBoundsMeters: z.object({
|
|
81
|
+
minX: z.number(),
|
|
82
|
+
maxX: z.number(),
|
|
83
|
+
minZ: z.number(),
|
|
84
|
+
maxZ: z.number(),
|
|
85
|
+
}),
|
|
86
|
+
})),
|
|
87
|
+
skippedItems: z.array(z.object({
|
|
88
|
+
id: z.string(),
|
|
89
|
+
name: z.string(),
|
|
90
|
+
reason: z.string(),
|
|
91
|
+
})),
|
|
92
|
+
unsupportedChecks: z.array(z.object({
|
|
93
|
+
check: z.string(),
|
|
94
|
+
reason: z.string(),
|
|
95
|
+
})),
|
|
8
96
|
collisions: z.array(z.object({
|
|
9
97
|
aId: z.string(),
|
|
10
98
|
bId: z.string(),
|
|
11
99
|
kind: z.string(),
|
|
100
|
+
violation: z.enum(['overlap', 'clearance']),
|
|
101
|
+
minimumClearanceMeters: z.number(),
|
|
12
102
|
})),
|
|
13
103
|
};
|
|
104
|
+
const unsupportedChecks = [
|
|
105
|
+
{
|
|
106
|
+
check: 'vertical_clearance',
|
|
107
|
+
reason: 'This check uses the X/Z plan footprint only and does not prove height clearance.',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
check: 'room_boundary_clearance',
|
|
111
|
+
reason: 'The tool compares items with other items and does not prove containment in a room.',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
check: 'door_swing_envelope',
|
|
115
|
+
reason: 'Door swing geometry is not evaluated by check_collisions.',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
check: 'delivery_path',
|
|
119
|
+
reason: 'No route, turning-radius, stair, or opening traversal is evaluated.',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
check: 'mesh_geometry',
|
|
123
|
+
reason: 'Asset meshes are not loaded; the check uses declared rectangular dimensions.',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
check: 'hosted_item_world_transform',
|
|
127
|
+
reason: 'Items positioned in a non-level parent frame are skipped instead of approximated.',
|
|
128
|
+
},
|
|
129
|
+
];
|
|
14
130
|
export function registerCheckCollisions(server, bridge) {
|
|
15
131
|
server.registerTool('check_collisions', {
|
|
16
132
|
title: 'Check collisions',
|
|
17
|
-
description: '
|
|
133
|
+
description: 'Assess declared item footprints with a rotation-aware plan AABB test. Supports an explicit minimum clearance and reports units, source dimensions, skipped evidence, and unsupported checks. Optionally scope to one level or floor-standing items.',
|
|
18
134
|
inputSchema: checkCollisionsInput,
|
|
19
135
|
outputSchema: checkCollisionsOutput,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (levelId) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
136
|
+
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
137
|
+
}, async ({ levelId, minimumClearance, floorOnly, candidate }) => {
|
|
138
|
+
const sceneNodes = Object.values(bridge.getNodes());
|
|
139
|
+
if (candidate && levelId && candidate.levelId !== levelId) {
|
|
140
|
+
throwMcpError(ErrorCode.InvalidParams, 'candidate.levelId must match levelId when both are provided');
|
|
141
|
+
}
|
|
142
|
+
const scopeLevelId = candidate?.levelId ?? levelId;
|
|
143
|
+
if (scopeLevelId) {
|
|
144
|
+
const target = sceneNodes.find((node) => node.id === scopeLevelId);
|
|
145
|
+
if (target?.type !== 'level') {
|
|
146
|
+
throwMcpError(ErrorCode.InvalidParams, `Level not found: ${scopeLevelId}`);
|
|
147
|
+
}
|
|
28
148
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
149
|
+
if (candidate) {
|
|
150
|
+
if (sceneNodes.some((node) => node.id === candidate.id)) {
|
|
151
|
+
throwMcpError(ErrorCode.InvalidParams, `Candidate id already exists: ${candidate.id}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const candidateNode = candidate
|
|
155
|
+
? ItemNode.parse({
|
|
156
|
+
object: 'node',
|
|
157
|
+
type: 'item',
|
|
158
|
+
parentId: candidate.levelId,
|
|
159
|
+
visible: true,
|
|
160
|
+
metadata: {},
|
|
161
|
+
name: candidate.name,
|
|
162
|
+
position: candidate.position,
|
|
163
|
+
rotation: [0, candidate.rotationY, 0],
|
|
164
|
+
scale: [1, 1, 1],
|
|
165
|
+
children: [],
|
|
166
|
+
asset: {
|
|
167
|
+
id: candidate.source?.assetId ?? 'supplied-dimensions',
|
|
168
|
+
name: candidate.name,
|
|
169
|
+
category: 'furniture',
|
|
170
|
+
thumbnail: '',
|
|
171
|
+
source: 'library',
|
|
172
|
+
src: 'asset://supplied-dimensions',
|
|
173
|
+
dimensions: candidate.dimensions,
|
|
174
|
+
offset: [0, 0, 0],
|
|
175
|
+
rotation: [0, 0, 0],
|
|
176
|
+
scale: [1, 1, 1],
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
: null;
|
|
180
|
+
const nodes = candidateNode ? [...sceneNodes, candidateNode] : sceneNodes;
|
|
181
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
182
|
+
const scoped = nodes.filter((node) => {
|
|
183
|
+
if (node.type !== 'item' || !scopeLevelId)
|
|
184
|
+
return true;
|
|
185
|
+
return resolveNodeLevelId(node.id, byId) === scopeLevelId;
|
|
186
|
+
});
|
|
187
|
+
const checkedItems = [];
|
|
188
|
+
const skippedItems = [];
|
|
189
|
+
for (const node of scoped) {
|
|
190
|
+
if (node.type !== 'item')
|
|
191
|
+
continue;
|
|
192
|
+
const resolvedLevelId = node === candidateNode ? candidate.levelId : bridge.resolveLevelId(node.id);
|
|
193
|
+
if (!resolvedLevelId) {
|
|
194
|
+
skippedItems.push({
|
|
195
|
+
id: node.id,
|
|
196
|
+
name: node.name ?? node.asset.name ?? node.id,
|
|
197
|
+
reason: 'unresolved_level',
|
|
198
|
+
});
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const immediateParent = node === candidateNode ? byId.get(candidate.levelId) : bridge.getAncestry(node.id)[1];
|
|
202
|
+
if (immediateParent && immediateParent.type !== 'level') {
|
|
203
|
+
skippedItems.push({
|
|
204
|
+
id: node.id,
|
|
205
|
+
name: node.name ?? node.asset.name ?? node.id,
|
|
206
|
+
reason: floorOnly ? 'unsupported_attachment' : 'parent_local_coordinates',
|
|
207
|
+
});
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const inspected = inspectItemPlanFootprint(node, { floorOnly });
|
|
211
|
+
const name = node.name ?? node.asset.name ?? node.id;
|
|
212
|
+
if (!inspected.ok) {
|
|
213
|
+
skippedItems.push({ id: node.id, name, reason: inspected.reason });
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
checkedItems.push({
|
|
217
|
+
id: node === candidateNode ? candidate.id : node.id,
|
|
218
|
+
name,
|
|
219
|
+
levelId: resolvedLevelId,
|
|
220
|
+
positionMeters: node.position,
|
|
221
|
+
rotationYRadians: inspected.rotationY,
|
|
222
|
+
source: {
|
|
223
|
+
assetId: node === candidateNode
|
|
224
|
+
? (candidate.source?.assetId ?? 'supplied-dimensions')
|
|
225
|
+
: node.asset.id,
|
|
226
|
+
uri: node === candidateNode
|
|
227
|
+
? (candidate.source?.uri ?? 'supplied://dimensions')
|
|
228
|
+
: node.asset.src,
|
|
229
|
+
catalog: node === candidateNode ? 'supplied' : (node.asset.source ?? 'unknown'),
|
|
230
|
+
},
|
|
231
|
+
sourceDimensionsMeters: inspected.sourceDimensions,
|
|
232
|
+
effectiveDimensionsMeters: inspected.effectiveDimensions,
|
|
233
|
+
footprintBoundsMeters: inspected.aabb,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const checkedItemIds = new Set(checkedItems.map((item) => (item.id === candidate?.id ? candidateNode.id : item.id)));
|
|
33
237
|
const found = findItemItemCollisions({
|
|
34
|
-
nodes: scoped,
|
|
35
|
-
|
|
36
|
-
gap: 0,
|
|
238
|
+
nodes: scoped.filter((node) => (node.type === 'item' ? checkedItemIds.has(node.id) : true)),
|
|
239
|
+
gap: minimumClearance,
|
|
37
240
|
});
|
|
38
241
|
const collisions = found.map((c) => ({
|
|
39
|
-
aId: c.aId,
|
|
40
|
-
bId: c.bId,
|
|
242
|
+
aId: c.aId === candidateNode?.id ? candidate.id : c.aId,
|
|
243
|
+
bId: c.bId === candidateNode?.id ? candidate.id : c.bId,
|
|
41
244
|
kind: c.kind,
|
|
245
|
+
violation: c.violation,
|
|
246
|
+
minimumClearanceMeters: c.minimumClearanceMeters,
|
|
42
247
|
}));
|
|
43
|
-
const payload = {
|
|
248
|
+
const payload = {
|
|
249
|
+
status: checkedItems.length === 0 && skippedItems.length > 0
|
|
250
|
+
? 'insufficient_evidence'
|
|
251
|
+
: skippedItems.length > 0
|
|
252
|
+
? 'partial'
|
|
253
|
+
: 'checked',
|
|
254
|
+
units: 'meters',
|
|
255
|
+
method: 'rotation-aware-plan-aabb',
|
|
256
|
+
assessmentGraphHash: computeGraphHash(bridge.exportJSON()),
|
|
257
|
+
minimumClearanceMeters: minimumClearance,
|
|
258
|
+
floorOnly,
|
|
259
|
+
candidateItemId: candidate?.id ?? null,
|
|
260
|
+
checkedItems,
|
|
261
|
+
skippedItems,
|
|
262
|
+
unsupportedChecks: [...unsupportedChecks],
|
|
263
|
+
collisions,
|
|
264
|
+
};
|
|
44
265
|
return {
|
|
45
266
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
46
267
|
structuredContent: payload,
|
|
@@ -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 createStoryShellInput: {
|
|
5
5
|
levelId: z.ZodString;
|
|
6
6
|
footprint: z.ZodArray<z.ZodArray<z.ZodNumber>>;
|
|
@@ -16,6 +16,13 @@ export declare const createStoryShellInput: {
|
|
|
16
16
|
ceilingMaterialPreset: z.ZodOptional<z.ZodString>;
|
|
17
17
|
};
|
|
18
18
|
export declare const createStoryShellOutput: {
|
|
19
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
20
|
+
status: z.ZodEnum<{
|
|
21
|
+
unbound: "unbound";
|
|
22
|
+
events_unsupported: "events_unsupported";
|
|
23
|
+
}>;
|
|
24
|
+
warning: z.ZodString;
|
|
25
|
+
}, z.core.$strip>>;
|
|
19
26
|
levelId: z.ZodString;
|
|
20
27
|
wallIds: z.ZodArray<z.ZodString>;
|
|
21
28
|
slabId: z.ZodNullable<z.ZodString>;
|
|
@@ -33,13 +40,14 @@ export declare const createRoofInput: {
|
|
|
33
40
|
width: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
|
|
34
41
|
depth: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
|
|
35
42
|
roofType: z.ZodDefault<z.ZodEnum<{
|
|
43
|
+
gable: "gable";
|
|
36
44
|
flat: "flat";
|
|
37
45
|
hip: "hip";
|
|
38
|
-
gable: "gable";
|
|
39
46
|
shed: "shed";
|
|
40
47
|
gambrel: "gambrel";
|
|
41
48
|
dutch: "dutch";
|
|
42
49
|
mansard: "mansard";
|
|
50
|
+
conical: "conical";
|
|
43
51
|
}>>;
|
|
44
52
|
pitch: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
45
53
|
wallHeight: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
@@ -49,6 +57,13 @@ export declare const createRoofInput: {
|
|
|
49
57
|
name: z.ZodOptional<z.ZodString>;
|
|
50
58
|
};
|
|
51
59
|
export declare const createRoofOutput: {
|
|
60
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
61
|
+
status: z.ZodEnum<{
|
|
62
|
+
unbound: "unbound";
|
|
63
|
+
events_unsupported: "events_unsupported";
|
|
64
|
+
}>;
|
|
65
|
+
warning: z.ZodString;
|
|
66
|
+
}, z.core.$strip>>;
|
|
52
67
|
referenceLevelId: z.ZodString;
|
|
53
68
|
roofLevelId: z.ZodString;
|
|
54
69
|
createdRoofLevelId: z.ZodNullable<z.ZodString>;
|
|
@@ -83,6 +98,13 @@ export declare const createStairBetweenLevelsInput: {
|
|
|
83
98
|
name: z.ZodOptional<z.ZodString>;
|
|
84
99
|
};
|
|
85
100
|
export declare const createStairBetweenLevelsOutput: {
|
|
101
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
102
|
+
status: z.ZodEnum<{
|
|
103
|
+
unbound: "unbound";
|
|
104
|
+
events_unsupported: "events_unsupported";
|
|
105
|
+
}>;
|
|
106
|
+
warning: z.ZodString;
|
|
107
|
+
}, z.core.$strip>>;
|
|
86
108
|
stairId: z.ZodString;
|
|
87
109
|
stairSegmentId: z.ZodString;
|
|
88
110
|
destinationSlabId: z.ZodNullable<z.ZodString>;
|
|
@@ -1 +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;AAcxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
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;AAcxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAkBpD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;CAsBjC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;CAOlC,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;CAO5B,CAAA;AAED,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCzC,CAAA;AAED,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;CAO1C,CAAA;AA4GD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgV1F"}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { resolveStairTotalRise } from '@pascal-app/core';
|
|
2
2
|
import { CeilingNode, getActiveRoofHeight, LevelNode, RoofNode, RoofSegmentNode, SlabNode, StairNode, StairSegmentNode, WallNode, } from '@pascal-app/core/schema';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
4
|
+
import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
5
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
6
|
+
import { measurement } from './measurement.js';
|
|
7
|
+
import { NodeIdSchema, Vec2Schema, Vec3Schema } from './schemas.js';
|
|
8
|
+
const ROOF_TYPES = [
|
|
9
|
+
'hip',
|
|
10
|
+
'gable',
|
|
11
|
+
'shed',
|
|
12
|
+
'gambrel',
|
|
13
|
+
'dutch',
|
|
14
|
+
'mansard',
|
|
15
|
+
'flat',
|
|
16
|
+
'conical',
|
|
17
|
+
];
|
|
8
18
|
const RAILING_MODES = ['none', 'left', 'right', 'both'];
|
|
9
19
|
export const createStoryShellInput = {
|
|
10
20
|
levelId: NodeIdSchema,
|
|
@@ -35,6 +45,7 @@ export const createStoryShellOutput = {
|
|
|
35
45
|
slabId: z.string().nullable(),
|
|
36
46
|
ceilingId: z.string().nullable(),
|
|
37
47
|
createdIds: z.array(z.string()),
|
|
48
|
+
...liveSyncOutput,
|
|
38
49
|
};
|
|
39
50
|
export const createRoofInput = {
|
|
40
51
|
levelId: NodeIdSchema,
|
|
@@ -67,6 +78,7 @@ export const createRoofOutput = {
|
|
|
67
78
|
createdRoofLevelId: z.string().nullable(),
|
|
68
79
|
roofId: z.string(),
|
|
69
80
|
roofSegmentId: z.string(),
|
|
81
|
+
...liveSyncOutput,
|
|
70
82
|
};
|
|
71
83
|
export const createStairBetweenLevelsInput = {
|
|
72
84
|
fromLevelId: NodeIdSchema,
|
|
@@ -108,6 +120,7 @@ export const createStairBetweenLevelsOutput = {
|
|
|
108
120
|
destinationSlabId: z.string().nullable(),
|
|
109
121
|
sourceCeilingId: z.string().nullable(),
|
|
110
122
|
openingPolygon: z.array(Vec2Schema),
|
|
123
|
+
...liveSyncOutput,
|
|
111
124
|
};
|
|
112
125
|
function textResult(payload) {
|
|
113
126
|
return {
|
|
@@ -190,6 +203,7 @@ export function registerConstructionTools(server, bridge) {
|
|
|
190
203
|
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.',
|
|
191
204
|
inputSchema: createStoryShellInput,
|
|
192
205
|
outputSchema: createStoryShellOutput,
|
|
206
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
193
207
|
}, async ({ levelId, footprint, wallHeight, wallThickness, createSlab, createCeiling, slabElevation, ceilingHeight, namePrefix, wallMaterialPreset, slabMaterialPreset, ceilingMaterialPreset, }) => {
|
|
194
208
|
const level = assertNode(bridge, levelId, 'level');
|
|
195
209
|
if (isRoofLevel(level)) {
|
|
@@ -246,13 +260,14 @@ export function registerConstructionTools(server, bridge) {
|
|
|
246
260
|
patches.push({ op: 'create', node: ceiling, parentId: levelId });
|
|
247
261
|
}
|
|
248
262
|
const result = bridge.applyPatch(patches);
|
|
249
|
-
await publishLiveSceneSnapshot(bridge, 'create_story_shell');
|
|
263
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_story_shell');
|
|
250
264
|
return textResult({
|
|
251
265
|
levelId,
|
|
252
266
|
wallIds,
|
|
253
267
|
slabId,
|
|
254
268
|
ceilingId,
|
|
255
269
|
createdIds: result.createdIds,
|
|
270
|
+
...persistencePayload(persistence),
|
|
256
271
|
});
|
|
257
272
|
});
|
|
258
273
|
server.registerTool('create_roof', {
|
|
@@ -260,10 +275,18 @@ export function registerConstructionTools(server, bridge) {
|
|
|
260
275
|
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.',
|
|
261
276
|
inputSchema: createRoofInput,
|
|
262
277
|
outputSchema: createRoofOutput,
|
|
278
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
263
279
|
}, async ({ levelId, roofLevelId, useDedicatedRoofLevel, roofLevelLabel, roofLevelElevation, roofLevelHeight, center, width, depth, roofType, pitch, wallHeight, wallThickness, overhang, materialPreset, name, }) => {
|
|
280
|
+
const effectiveWidth = roofType === 'conical' ? Math.max(width, depth) : width;
|
|
281
|
+
const effectiveDepth = roofType === 'conical' ? effectiveWidth : depth;
|
|
264
282
|
// Peak height is derived from pitch + footprint + type; we still
|
|
265
283
|
// need it to size the auto-generated roof level container below.
|
|
266
|
-
const peakHeight = getActiveRoofHeight({
|
|
284
|
+
const peakHeight = getActiveRoofHeight({
|
|
285
|
+
roofType,
|
|
286
|
+
pitch,
|
|
287
|
+
width: effectiveWidth,
|
|
288
|
+
depth: effectiveDepth,
|
|
289
|
+
});
|
|
267
290
|
const referenceLevel = assertNode(bridge, levelId, 'level');
|
|
268
291
|
const patches = [];
|
|
269
292
|
let targetRoofLevelId = levelId;
|
|
@@ -294,8 +317,8 @@ export function registerConstructionTools(server, bridge) {
|
|
|
294
317
|
}
|
|
295
318
|
const segment = RoofSegmentNode.parse({
|
|
296
319
|
roofType,
|
|
297
|
-
width,
|
|
298
|
-
depth,
|
|
320
|
+
width: effectiveWidth,
|
|
321
|
+
depth: effectiveDepth,
|
|
299
322
|
wallHeight,
|
|
300
323
|
pitch,
|
|
301
324
|
wallThickness,
|
|
@@ -317,13 +340,14 @@ export function registerConstructionTools(server, bridge) {
|
|
|
317
340
|
{ op: 'create', node: roof, parentId: targetRoofLevelId },
|
|
318
341
|
{ op: 'create', node: segment, parentId: roof.id },
|
|
319
342
|
]);
|
|
320
|
-
await publishLiveSceneSnapshot(bridge, 'create_roof');
|
|
343
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_roof');
|
|
321
344
|
return textResult({
|
|
322
345
|
referenceLevelId: levelId,
|
|
323
346
|
roofLevelId: targetRoofLevelId,
|
|
324
347
|
createdRoofLevelId,
|
|
325
348
|
roofId: roof.id,
|
|
326
349
|
roofSegmentId: segment.id,
|
|
350
|
+
...persistencePayload(persistence),
|
|
327
351
|
});
|
|
328
352
|
});
|
|
329
353
|
server.registerTool('create_stair_between_levels', {
|
|
@@ -331,6 +355,7 @@ export function registerConstructionTools(server, bridge) {
|
|
|
331
355
|
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.',
|
|
332
356
|
inputSchema: createStairBetweenLevelsInput,
|
|
333
357
|
outputSchema: createStairBetweenLevelsOutput,
|
|
358
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
334
359
|
}, async ({ fromLevelId, toLevelId, position, rotation, width, runLength, totalRise, stepCount, railingMode, destinationSlabId, sourceCeilingId, createDestinationSlabOpening, createSourceCeilingOpening, openingWidth, openingLength, openingOffset, openingCenter, openingRotation, materialPreset, name, }) => {
|
|
335
360
|
const fromLevel = assertNode(bridge, fromLevelId, 'level');
|
|
336
361
|
const toLevel = assertNode(bridge, toLevelId, 'level');
|
|
@@ -408,13 +433,14 @@ export function registerConstructionTools(server, bridge) {
|
|
|
408
433
|
});
|
|
409
434
|
}
|
|
410
435
|
bridge.applyPatch(patches);
|
|
411
|
-
await publishLiveSceneSnapshot(bridge, 'create_stair_between_levels');
|
|
436
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_stair_between_levels');
|
|
412
437
|
return textResult({
|
|
413
438
|
stairId: stair.id,
|
|
414
439
|
stairSegmentId: segment.id,
|
|
415
440
|
destinationSlabId: destinationSlab?.id ?? null,
|
|
416
441
|
sourceCeilingId: sourceCeiling?.id ?? null,
|
|
417
442
|
openingPolygon,
|
|
443
|
+
...persistencePayload(persistence),
|
|
418
444
|
});
|
|
419
445
|
});
|
|
420
446
|
}
|
|
@@ -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 createLevelInput: {
|
|
5
5
|
buildingId: z.ZodString;
|
|
6
6
|
elevation: z.ZodOptional<z.ZodNumber>;
|
|
@@ -8,6 +8,13 @@ export declare const createLevelInput: {
|
|
|
8
8
|
label: z.ZodOptional<z.ZodString>;
|
|
9
9
|
};
|
|
10
10
|
export declare const createLevelOutput: {
|
|
11
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
status: z.ZodEnum<{
|
|
13
|
+
unbound: "unbound";
|
|
14
|
+
events_unsupported: "events_unsupported";
|
|
15
|
+
}>;
|
|
16
|
+
warning: z.ZodString;
|
|
17
|
+
}, z.core.$strip>>;
|
|
11
18
|
levelId: z.ZodString;
|
|
12
19
|
};
|
|
13
20
|
export declare function registerCreateLevel(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +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;AAIxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"create-level.d.ts","sourceRoot":"","sources":["../../src/tools/create-level.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAIxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAOpD,eAAO,MAAM,gBAAgB;;;;;CAW5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;CAG7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgDpF"}
|