@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,10 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_LEVEL_HEIGHT } from '@pascal-app/core';
|
|
2
2
|
import { LevelNode } from '@pascal-app/core/schema';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
5
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
6
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
7
|
+
import { measurement } from './measurement.js';
|
|
8
|
+
import { NodeIdSchema } from './schemas.js';
|
|
8
9
|
export const createLevelInput = {
|
|
9
10
|
buildingId: NodeIdSchema,
|
|
10
11
|
elevation: z
|
|
@@ -19,6 +20,7 @@ export const createLevelInput = {
|
|
|
19
20
|
};
|
|
20
21
|
export const createLevelOutput = {
|
|
21
22
|
levelId: z.string(),
|
|
23
|
+
...liveSyncOutput,
|
|
22
24
|
};
|
|
23
25
|
export function registerCreateLevel(server, bridge) {
|
|
24
26
|
server.registerTool('create_level', {
|
|
@@ -26,6 +28,7 @@ export function registerCreateLevel(server, bridge) {
|
|
|
26
28
|
description: "Append a new level above the given building's current top level. height is stored as the level's floor-to-floor storey height.",
|
|
27
29
|
inputSchema: createLevelInput,
|
|
28
30
|
outputSchema: createLevelOutput,
|
|
31
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
29
32
|
}, async ({ buildingId, height, label }) => {
|
|
30
33
|
const parent = bridge.getNode(buildingId);
|
|
31
34
|
if (!parent) {
|
|
@@ -50,8 +53,8 @@ export function registerCreateLevel(server, bridge) {
|
|
|
50
53
|
...(label !== undefined ? { name: label } : {}),
|
|
51
54
|
});
|
|
52
55
|
const id = bridge.createNode(levelNode, buildingId);
|
|
53
|
-
await publishLiveSceneSnapshot(bridge, 'create_level');
|
|
54
|
-
const payload = { levelId: id };
|
|
56
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_level');
|
|
57
|
+
const payload = { levelId: id, ...persistencePayload(persistence) };
|
|
55
58
|
return {
|
|
56
59
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
57
60
|
structuredContent: payload,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SceneOperations } from '../operations/index.js';
|
|
4
|
+
export declare const createUnitInput: {
|
|
5
|
+
buildingId: z.ZodString;
|
|
6
|
+
name: z.ZodString;
|
|
7
|
+
kind: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
apartment: "apartment";
|
|
9
|
+
"hotel-room": "hotel-room";
|
|
10
|
+
commercial: "commercial";
|
|
11
|
+
common: "common";
|
|
12
|
+
}>>>;
|
|
13
|
+
color: z.ZodOptional<z.ZodString>;
|
|
14
|
+
memberZoneIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
15
|
+
};
|
|
16
|
+
export declare const createUnitOutput: {
|
|
17
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
18
|
+
status: z.ZodEnum<{
|
|
19
|
+
unbound: "unbound";
|
|
20
|
+
events_unsupported: "events_unsupported";
|
|
21
|
+
}>;
|
|
22
|
+
warning: z.ZodString;
|
|
23
|
+
}, z.core.$strip>>;
|
|
24
|
+
unitId: z.ZodString;
|
|
25
|
+
};
|
|
26
|
+
export declare function registerCreateUnit(server: McpServer, bridge: SceneOperations): void;
|
|
27
|
+
//# sourceMappingURL=create-unit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-unit.d.ts","sourceRoot":"","sources":["../../src/tools/create-unit.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;AAMpD,eAAO,MAAM,eAAe;;;;;;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAuBnF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { UnitNode } from '@pascal-app/core/schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
5
|
+
import { NodeIdSchema } from './schemas.js';
|
|
6
|
+
import { validateUnitMembers } from './unit-members.js';
|
|
7
|
+
export const createUnitInput = {
|
|
8
|
+
buildingId: NodeIdSchema,
|
|
9
|
+
name: z.string(),
|
|
10
|
+
kind: UnitNode.shape.kind.optional(),
|
|
11
|
+
color: z.string().optional(),
|
|
12
|
+
memberZoneIds: z.array(NodeIdSchema).optional(),
|
|
13
|
+
};
|
|
14
|
+
export const createUnitOutput = {
|
|
15
|
+
unitId: z.string(),
|
|
16
|
+
...liveSyncOutput,
|
|
17
|
+
};
|
|
18
|
+
export function registerCreateUnit(server, bridge) {
|
|
19
|
+
server.registerTool('create_unit', {
|
|
20
|
+
title: 'Create unit',
|
|
21
|
+
description: 'Create a named unit in a building, optionally referencing zones across its levels.',
|
|
22
|
+
inputSchema: createUnitInput,
|
|
23
|
+
outputSchema: createUnitOutput,
|
|
24
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
25
|
+
}, async ({ buildingId, name, kind, color, memberZoneIds }) => {
|
|
26
|
+
const members = validateUnitMembers(bridge, buildingId, memberZoneIds ?? []);
|
|
27
|
+
const unit = UnitNode.parse({ name, kind, color, members });
|
|
28
|
+
const id = bridge.createNode(unit, buildingId);
|
|
29
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_unit');
|
|
30
|
+
const payload = { unitId: id, ...persistencePayload(persistence) };
|
|
31
|
+
return {
|
|
32
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
33
|
+
structuredContent: payload,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -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 createWallInput: {
|
|
5
5
|
levelId: z.ZodString;
|
|
6
6
|
start: z.ZodArray<z.ZodNumber>;
|
|
@@ -9,6 +9,13 @@ export declare const createWallInput: {
|
|
|
9
9
|
height: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
10
10
|
};
|
|
11
11
|
export declare const createWallOutput: {
|
|
12
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
status: z.ZodEnum<{
|
|
14
|
+
unbound: "unbound";
|
|
15
|
+
events_unsupported: "events_unsupported";
|
|
16
|
+
}>;
|
|
17
|
+
warning: z.ZodString;
|
|
18
|
+
}, z.core.$strip>>;
|
|
12
19
|
wallId: z.ZodString;
|
|
13
20
|
};
|
|
14
21
|
export declare function registerCreateWall(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +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;
|
|
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;AAOpD,eAAO,MAAM,eAAe;;;;;;CAS3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDnF"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { WallNode } from '@pascal-app/core/schema';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
5
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
6
|
+
import { measurement } from './measurement.js';
|
|
7
|
+
import { NodeIdSchema, Vec2Schema } from './schemas.js';
|
|
7
8
|
export const createWallInput = {
|
|
8
9
|
levelId: NodeIdSchema,
|
|
9
10
|
start: Vec2Schema,
|
|
@@ -16,6 +17,7 @@ export const createWallInput = {
|
|
|
16
17
|
};
|
|
17
18
|
export const createWallOutput = {
|
|
18
19
|
wallId: z.string(),
|
|
20
|
+
...liveSyncOutput,
|
|
19
21
|
};
|
|
20
22
|
export function registerCreateWall(server, bridge) {
|
|
21
23
|
server.registerTool('create_wall', {
|
|
@@ -23,6 +25,7 @@ export function registerCreateWall(server, bridge) {
|
|
|
23
25
|
description: 'Create a new wall on the given level between two 2D points. Thickness and height default to the core library defaults when omitted.',
|
|
24
26
|
inputSchema: createWallInput,
|
|
25
27
|
outputSchema: createWallOutput,
|
|
28
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
26
29
|
}, async ({ levelId, start, end, thickness, height }) => {
|
|
27
30
|
const parent = bridge.getNode(levelId);
|
|
28
31
|
if (!parent) {
|
|
@@ -44,8 +47,8 @@ export function registerCreateWall(server, bridge) {
|
|
|
44
47
|
...(height !== undefined ? { height } : {}),
|
|
45
48
|
});
|
|
46
49
|
const id = bridge.createNode(wall, levelId);
|
|
47
|
-
await publishLiveSceneSnapshot(bridge, 'create_wall');
|
|
48
|
-
const payload = { wallId: id };
|
|
50
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_wall');
|
|
51
|
+
const payload = { wallId: id, ...persistencePayload(persistence) };
|
|
49
52
|
return {
|
|
50
53
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
51
54
|
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 cutOpeningInput: {
|
|
5
5
|
wallId: z.ZodString;
|
|
6
6
|
type: z.ZodEnum<{
|
|
@@ -12,6 +12,13 @@ export declare const cutOpeningInput: {
|
|
|
12
12
|
height: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
|
|
13
13
|
};
|
|
14
14
|
export declare const cutOpeningOutput: {
|
|
15
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
status: z.ZodEnum<{
|
|
17
|
+
unbound: "unbound";
|
|
18
|
+
events_unsupported: "events_unsupported";
|
|
19
|
+
}>;
|
|
20
|
+
warning: z.ZodString;
|
|
21
|
+
}, z.core.$strip>>;
|
|
15
22
|
openingId: z.ZodString;
|
|
16
23
|
};
|
|
17
24
|
export declare function registerCutOpening(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cut-opening.d.ts","sourceRoot":"","sources":["../../src/tools/cut-opening.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":"cut-opening.d.ts","sourceRoot":"","sources":["../../src/tools/cut-opening.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,eAAe;;;;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0DnF"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DoorNode, WindowNode } from '@pascal-app/core/schema';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
5
|
+
import { wallLength, wallLocalXFromT } from './geometry.js';
|
|
6
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
7
|
+
import { measurement } from './measurement.js';
|
|
8
|
+
import { NodeIdSchema } from './schemas.js';
|
|
8
9
|
export const cutOpeningInput = {
|
|
9
10
|
wallId: NodeIdSchema,
|
|
10
11
|
type: z.enum(['door', 'window']),
|
|
@@ -14,6 +15,7 @@ export const cutOpeningInput = {
|
|
|
14
15
|
};
|
|
15
16
|
export const cutOpeningOutput = {
|
|
16
17
|
openingId: z.string(),
|
|
18
|
+
...liveSyncOutput,
|
|
17
19
|
};
|
|
18
20
|
export function registerCutOpening(server, bridge) {
|
|
19
21
|
server.registerTool('cut_opening', {
|
|
@@ -21,6 +23,7 @@ export function registerCutOpening(server, bridge) {
|
|
|
21
23
|
description: 'Cut a door or window opening into an existing wall. position is a parametric 0..1 offset along the wall centreline.',
|
|
22
24
|
inputSchema: cutOpeningInput,
|
|
23
25
|
outputSchema: cutOpeningOutput,
|
|
26
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
24
27
|
}, async ({ wallId, type, position, width, height }) => {
|
|
25
28
|
const wall = bridge.getNode(wallId);
|
|
26
29
|
if (!wall) {
|
|
@@ -48,8 +51,8 @@ export function registerCutOpening(server, bridge) {
|
|
|
48
51
|
position: [base.position[0], 0.9 + height / 2, 0],
|
|
49
52
|
});
|
|
50
53
|
const id = bridge.createNode(opening, wallId);
|
|
51
|
-
await publishLiveSceneSnapshot(bridge, 'cut_opening');
|
|
52
|
-
const payload = { openingId: id };
|
|
54
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'cut_opening');
|
|
55
|
+
const payload = { openingId: id, ...persistencePayload(persistence) };
|
|
53
56
|
return {
|
|
54
57
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
55
58
|
structuredContent: payload,
|
|
@@ -1,11 +1,18 @@
|
|
|
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 deleteNodeInput: {
|
|
5
5
|
id: z.ZodString;
|
|
6
6
|
cascade: z.ZodOptional<z.ZodBoolean>;
|
|
7
7
|
};
|
|
8
8
|
export declare const deleteNodeOutput: {
|
|
9
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
status: z.ZodEnum<{
|
|
11
|
+
unbound: "unbound";
|
|
12
|
+
events_unsupported: "events_unsupported";
|
|
13
|
+
}>;
|
|
14
|
+
warning: z.ZodString;
|
|
15
|
+
}, z.core.$strip>>;
|
|
9
16
|
deletedIds: z.ZodArray<z.ZodString>;
|
|
10
17
|
};
|
|
11
18
|
export declare function registerDeleteNode(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-node.d.ts","sourceRoot":"","sources":["../../src/tools/delete-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"delete-node.d.ts","sourceRoot":"","sources":["../../src/tools/delete-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA8BnF"}
|
|
@@ -1,13 +1,15 @@
|
|
|
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 { NodeIdSchema } from './schemas.js';
|
|
5
6
|
export const deleteNodeInput = {
|
|
6
7
|
id: NodeIdSchema,
|
|
7
8
|
cascade: z.boolean().optional(),
|
|
8
9
|
};
|
|
9
10
|
export const deleteNodeOutput = {
|
|
10
11
|
deletedIds: z.array(z.string()),
|
|
12
|
+
...liveSyncOutput,
|
|
11
13
|
};
|
|
12
14
|
export function registerDeleteNode(server, bridge) {
|
|
13
15
|
server.registerTool('delete_node', {
|
|
@@ -15,6 +17,7 @@ export function registerDeleteNode(server, bridge) {
|
|
|
15
17
|
description: 'Delete a node. If it has children, pass `cascade: true` to delete descendants recursively.',
|
|
16
18
|
inputSchema: deleteNodeInput,
|
|
17
19
|
outputSchema: deleteNodeOutput,
|
|
20
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
18
21
|
}, async ({ id, cascade }) => {
|
|
19
22
|
const node = bridge.getNode(id);
|
|
20
23
|
if (!node) {
|
|
@@ -22,8 +25,8 @@ export function registerDeleteNode(server, bridge) {
|
|
|
22
25
|
}
|
|
23
26
|
try {
|
|
24
27
|
const removed = bridge.deleteNode(id, cascade ?? false);
|
|
25
|
-
await publishLiveSceneSnapshot(bridge, 'delete_node');
|
|
26
|
-
const payload = { deletedIds: removed };
|
|
28
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'delete_node');
|
|
29
|
+
const payload = { deletedIds: removed, ...persistencePayload(persistence) };
|
|
27
30
|
return {
|
|
28
31
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
29
32
|
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 describeNodeInput: {
|
|
5
5
|
id: z.ZodString;
|
|
6
6
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-node.d.ts","sourceRoot":"","sources":["../../src/tools/describe-node.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":"describe-node.d.ts","sourceRoot":"","sources":["../../src/tools/describe-node.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;AAMpD,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,eAAO,MAAM,kBAAkB;;;;;;;;CAQ9B,CAAA;AA0CD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDrF"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { resolveCeilingHeight } from '@pascal-app/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
5
|
+
import { resolveReportedWallHeight } from './scene-query.js';
|
|
6
|
+
import { NodeIdSchema } from './schemas.js';
|
|
6
7
|
export const describeNodeInput = {
|
|
7
8
|
id: NodeIdSchema,
|
|
8
9
|
};
|
|
@@ -60,6 +61,7 @@ export function registerDescribeNode(server, bridge) {
|
|
|
60
61
|
description: 'Return a structured summary of a node including its ancestry, children IDs, key properties, and a short human description.',
|
|
61
62
|
inputSchema: describeNodeInput,
|
|
62
63
|
outputSchema: describeNodeOutput,
|
|
64
|
+
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
63
65
|
}, async ({ id }) => {
|
|
64
66
|
const node = bridge.getNode(id);
|
|
65
67
|
if (!node) {
|
|
@@ -6,14 +6,25 @@
|
|
|
6
6
|
*
|
|
7
7
|
* See docs/layout-clearance-error-log.md for pitfalls (levels, gap sign, scale).
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
10
|
-
import { type Vec2 } from './geometry';
|
|
9
|
+
import { type AnyNode } from '@pascal-app/core/schema';
|
|
10
|
+
import { type Vec2 } from './geometry.js';
|
|
11
11
|
export type PlanAabb = {
|
|
12
12
|
minX: number;
|
|
13
13
|
maxX: number;
|
|
14
14
|
minZ: number;
|
|
15
15
|
maxZ: number;
|
|
16
16
|
};
|
|
17
|
+
export type ItemFootprintFailureReason = 'missing_dimensions' | 'non_finite_position' | 'non_finite_rotation' | 'non_finite_dimensions' | 'non_positive_plan_dimensions' | 'non_finite_scale' | 'zero_plan_scale' | 'non_planar_rotation' | 'unsupported_attachment';
|
|
18
|
+
export type ItemFootprintInspection = {
|
|
19
|
+
ok: true;
|
|
20
|
+
aabb: PlanAabb;
|
|
21
|
+
sourceDimensions: [number, number, number];
|
|
22
|
+
effectiveDimensions: [number, number, number];
|
|
23
|
+
rotationY: number;
|
|
24
|
+
} | {
|
|
25
|
+
ok: false;
|
|
26
|
+
reason: ItemFootprintFailureReason;
|
|
27
|
+
};
|
|
17
28
|
export type DoorKeepout = {
|
|
18
29
|
doorId: string;
|
|
19
30
|
wallId: string;
|
|
@@ -63,8 +74,13 @@ export declare function resolveNodeLevelId(nodeId: string, byId: Map<string, Any
|
|
|
63
74
|
* Prefer scaled dimensions when the node is available.
|
|
64
75
|
*/
|
|
65
76
|
export declare function itemPlanAabb(position: [number, number, number] | number[], dimensions: [number, number, number] | number[] | undefined, rotationYRad?: number): PlanAabb;
|
|
66
|
-
/**
|
|
77
|
+
/** Scaled plan footprint used by legacy placement and door-clearance callers. */
|
|
67
78
|
export declare function itemNodePlanAabb(node: AnyNode): PlanAabb | null;
|
|
79
|
+
export declare function inspectItemPlanFootprint(node: Extract<AnyNode, {
|
|
80
|
+
type: 'item';
|
|
81
|
+
}>, options?: {
|
|
82
|
+
floorOnly?: boolean;
|
|
83
|
+
}): ItemFootprintInspection;
|
|
68
84
|
/**
|
|
69
85
|
* Build a rectangular keep-out around a wall door, extruded perpendicular to the wall
|
|
70
86
|
* on both faces so either swing side is protected.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/door-clearance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"door-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/door-clearance.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* See docs/layout-clearance-error-log.md for pitfalls (levels, gap sign, scale).
|
|
8
8
|
*/
|
|
9
9
|
import { getScaledDimensions } from '@pascal-app/core/schema';
|
|
10
|
-
import { wallLength } from './geometry';
|
|
10
|
+
import { wallLength } from './geometry.js';
|
|
11
11
|
/** Plan depth (m) cleared on each side of the wall face through the opening. */
|
|
12
12
|
export const DEFAULT_DOOR_CLEAR_DEPTH = 0.65;
|
|
13
13
|
/** Extra half-width (m) beyond the door leaf along the wall. */
|
|
@@ -34,12 +34,31 @@ export function resolveNodeLevelId(nodeId, byId) {
|
|
|
34
34
|
if (current.type === 'level')
|
|
35
35
|
return current.id;
|
|
36
36
|
const parentId = current.parentId;
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if (parentId && byId.has(parentId)) {
|
|
38
|
+
current = byId.get(parentId);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
current = findParentByChildren(current.id, byId);
|
|
40
42
|
}
|
|
41
43
|
return null;
|
|
42
44
|
}
|
|
45
|
+
function findParentByChildren(nodeId, byId) {
|
|
46
|
+
for (const candidate of byId.values()) {
|
|
47
|
+
if (!('children' in candidate) || !Array.isArray(candidate.children))
|
|
48
|
+
continue;
|
|
49
|
+
const containsNode = candidate.children.some((child) => {
|
|
50
|
+
if (typeof child === 'string')
|
|
51
|
+
return child === nodeId;
|
|
52
|
+
return (child !== null &&
|
|
53
|
+
typeof child === 'object' &&
|
|
54
|
+
'id' in child &&
|
|
55
|
+
child.id === nodeId);
|
|
56
|
+
});
|
|
57
|
+
if (containsNode)
|
|
58
|
+
return candidate;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
43
62
|
/**
|
|
44
63
|
* Axis-aligned item footprint in plan (x/z), rotation-aware.
|
|
45
64
|
* Prefer scaled dimensions when the node is available.
|
|
@@ -59,13 +78,88 @@ export function itemPlanAabb(position, dimensions, rotationYRad = 0) {
|
|
|
59
78
|
maxZ: z + halfD,
|
|
60
79
|
};
|
|
61
80
|
}
|
|
62
|
-
/**
|
|
81
|
+
/** Scaled plan footprint used by legacy placement and door-clearance callers. */
|
|
63
82
|
export function itemNodePlanAabb(node) {
|
|
64
83
|
if (node.type !== 'item')
|
|
65
84
|
return null;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
if (!Array.isArray(node.asset.dimensions))
|
|
86
|
+
return null;
|
|
87
|
+
const [width, height, depth] = getScaledDimensions(node);
|
|
88
|
+
const rotationY = Array.isArray(node.rotation) ? (node.rotation[1] ?? 0) : 0;
|
|
89
|
+
if (![node.position[0], node.position[2], width, height, depth, rotationY].every(Number.isFinite)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return itemPlanAabb(node.position, [Math.abs(width), Math.abs(height), Math.abs(depth)], rotationY);
|
|
93
|
+
}
|
|
94
|
+
export function inspectItemPlanFootprint(node, options) {
|
|
95
|
+
if (options?.floorOnly &&
|
|
96
|
+
(node.asset.attachTo === 'wall' ||
|
|
97
|
+
node.asset.attachTo === 'wall-side' ||
|
|
98
|
+
node.asset.attachTo === 'ceiling')) {
|
|
99
|
+
return { ok: false, reason: 'unsupported_attachment' };
|
|
100
|
+
}
|
|
101
|
+
const position = node.position;
|
|
102
|
+
if (!Array.isArray(position)) {
|
|
103
|
+
return { ok: false, reason: 'non_finite_position' };
|
|
104
|
+
}
|
|
105
|
+
const x = position[0];
|
|
106
|
+
const y = position[1];
|
|
107
|
+
const z = position[2];
|
|
108
|
+
if (!(Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z))) {
|
|
109
|
+
return { ok: false, reason: 'non_finite_position' };
|
|
110
|
+
}
|
|
111
|
+
const rotation = Array.isArray(node.rotation) ? node.rotation : [0, 0, 0];
|
|
112
|
+
const rotationX = rotation[0] ?? Number.NaN;
|
|
113
|
+
const rotationY = rotation[1] ?? Number.NaN;
|
|
114
|
+
const rotationZ = rotation[2] ?? Number.NaN;
|
|
115
|
+
if (!(Number.isFinite(rotationX) && Number.isFinite(rotationY) && Number.isFinite(rotationZ))) {
|
|
116
|
+
return { ok: false, reason: 'non_finite_rotation' };
|
|
117
|
+
}
|
|
118
|
+
if (Math.abs(rotationX) > 1e-6 || Math.abs(rotationZ) > 1e-6) {
|
|
119
|
+
return { ok: false, reason: 'non_planar_rotation' };
|
|
120
|
+
}
|
|
121
|
+
const dimensions = node.asset.dimensions;
|
|
122
|
+
if (!Array.isArray(dimensions) || dimensions.length !== 3) {
|
|
123
|
+
return { ok: false, reason: 'missing_dimensions' };
|
|
124
|
+
}
|
|
125
|
+
const width = dimensions[0] ?? Number.NaN;
|
|
126
|
+
const height = dimensions[1] ?? Number.NaN;
|
|
127
|
+
const depth = dimensions[2] ?? Number.NaN;
|
|
128
|
+
if (!(Number.isFinite(width) && Number.isFinite(height) && Number.isFinite(depth))) {
|
|
129
|
+
return { ok: false, reason: 'non_finite_dimensions' };
|
|
130
|
+
}
|
|
131
|
+
if (width <= 0 || depth <= 0) {
|
|
132
|
+
return { ok: false, reason: 'non_positive_plan_dimensions' };
|
|
133
|
+
}
|
|
134
|
+
const scale = Array.isArray(node.scale) ? node.scale : [1, 1, 1];
|
|
135
|
+
const scaleX = scale[0] ?? Number.NaN;
|
|
136
|
+
const scaleY = scale[1] ?? Number.NaN;
|
|
137
|
+
const scaleZ = scale[2] ?? Number.NaN;
|
|
138
|
+
if (!(Number.isFinite(scaleX) && Number.isFinite(scaleY) && Number.isFinite(scaleZ))) {
|
|
139
|
+
return { ok: false, reason: 'non_finite_scale' };
|
|
140
|
+
}
|
|
141
|
+
if (scaleX === 0 || scaleZ === 0) {
|
|
142
|
+
return { ok: false, reason: 'zero_plan_scale' };
|
|
143
|
+
}
|
|
144
|
+
const [scaledWidth, scaledHeight, scaledDepth] = getScaledDimensions(node);
|
|
145
|
+
const effectiveDimensions = [
|
|
146
|
+
Math.abs(scaledWidth),
|
|
147
|
+
Math.abs(scaledHeight),
|
|
148
|
+
Math.abs(scaledDepth),
|
|
149
|
+
];
|
|
150
|
+
if (!effectiveDimensions.every(Number.isFinite)) {
|
|
151
|
+
return { ok: false, reason: 'non_finite_dimensions' };
|
|
152
|
+
}
|
|
153
|
+
if (effectiveDimensions[0] <= 0 || effectiveDimensions[2] <= 0) {
|
|
154
|
+
return { ok: false, reason: 'non_positive_plan_dimensions' };
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
ok: true,
|
|
158
|
+
aabb: itemPlanAabb(node.position, effectiveDimensions, rotationY),
|
|
159
|
+
sourceDimensions: [width, height, depth],
|
|
160
|
+
effectiveDimensions,
|
|
161
|
+
rotationY,
|
|
162
|
+
};
|
|
69
163
|
}
|
|
70
164
|
/**
|
|
71
165
|
* Build a rectangular keep-out around a wall door, extruded perpendicular to the wall
|
|
@@ -1,10 +1,17 @@
|
|
|
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 duplicateLevelInput: {
|
|
5
5
|
levelId: z.ZodString;
|
|
6
6
|
};
|
|
7
7
|
export declare const duplicateLevelOutput: {
|
|
8
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
status: z.ZodEnum<{
|
|
10
|
+
unbound: "unbound";
|
|
11
|
+
events_unsupported: "events_unsupported";
|
|
12
|
+
}>;
|
|
13
|
+
warning: z.ZodString;
|
|
14
|
+
}, z.core.$strip>>;
|
|
8
15
|
newLevelId: z.ZodString;
|
|
9
16
|
newNodeIds: z.ZodArray<z.ZodString>;
|
|
10
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicate-level.d.ts","sourceRoot":"","sources":["../../src/tools/duplicate-level.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"duplicate-level.d.ts","sourceRoot":"","sources":["../../src/tools/duplicate-level.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,mBAAmB;;CAE/B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;CAIhC,CAAA;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwDvF"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { cloneLevelSubtree } from '@pascal-app/core/clone-scene-graph';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
+
import { ErrorCode, throwMcpError } from './errors.js';
|
|
5
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
6
|
+
import { NodeIdSchema } from './schemas.js';
|
|
6
7
|
export const duplicateLevelInput = {
|
|
7
8
|
levelId: NodeIdSchema,
|
|
8
9
|
};
|
|
9
10
|
export const duplicateLevelOutput = {
|
|
10
11
|
newLevelId: z.string(),
|
|
11
12
|
newNodeIds: z.array(z.string()),
|
|
13
|
+
...liveSyncOutput,
|
|
12
14
|
};
|
|
13
15
|
export function registerDuplicateLevel(server, bridge) {
|
|
14
16
|
server.registerTool('duplicate_level', {
|
|
@@ -16,6 +18,7 @@ export function registerDuplicateLevel(server, bridge) {
|
|
|
16
18
|
description: 'Clone a level and all its descendants into a new subtree attached to the same building.',
|
|
17
19
|
inputSchema: duplicateLevelInput,
|
|
18
20
|
outputSchema: duplicateLevelOutput,
|
|
21
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
19
22
|
}, async ({ levelId }) => {
|
|
20
23
|
const node = bridge.getNode(levelId);
|
|
21
24
|
if (!node) {
|
|
@@ -43,10 +46,11 @@ export function registerDuplicateLevel(server, bridge) {
|
|
|
43
46
|
return createOp;
|
|
44
47
|
});
|
|
45
48
|
const result = bridge.applyPatch(patches);
|
|
46
|
-
await publishLiveSceneSnapshot(bridge, 'duplicate_level');
|
|
49
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'duplicate_level');
|
|
47
50
|
const payload = {
|
|
48
51
|
newLevelId: newLevelId,
|
|
49
52
|
newNodeIds: result.createdIds,
|
|
53
|
+
...persistencePayload(persistence),
|
|
50
54
|
};
|
|
51
55
|
return {
|
|
52
56
|
content: [{ type: 'text', text: JSON.stringify(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 exportGlbInput: {};
|
|
5
5
|
export declare const exportGlbOutput: {
|
|
6
6
|
status: z.ZodLiteral<"not_implemented">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-glb.d.ts","sourceRoot":"","sources":["../../src/tools/export-glb.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":"export-glb.d.ts","sourceRoot":"","sources":["../../src/tools/export-glb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,eAAO,MAAM,cAAc,IAAK,CAAA;AAEhC,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAuBnF"}
|