@pascal-app/mcp 1.0.0-beta.5 → 1.0.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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  3. package/dist/bridge/scene-bridge.js +2 -2
  4. package/dist/server.d.ts +11 -0
  5. package/dist/server.d.ts.map +1 -1
  6. package/dist/server.js +52 -0
  7. package/dist/tools/annotations.d.ts +28 -0
  8. package/dist/tools/annotations.d.ts.map +1 -0
  9. package/dist/tools/annotations.js +27 -0
  10. package/dist/tools/apply-patch.d.ts +7 -0
  11. package/dist/tools/apply-patch.d.ts.map +1 -1
  12. package/dist/tools/apply-patch.js +6 -2
  13. package/dist/tools/check-collisions.d.ts +59 -0
  14. package/dist/tools/check-collisions.d.ts.map +1 -1
  15. package/dist/tools/check-collisions.js +240 -19
  16. package/dist/tools/construction-tools.d.ts +23 -1
  17. package/dist/tools/construction-tools.d.ts.map +1 -1
  18. package/dist/tools/construction-tools.js +34 -8
  19. package/dist/tools/create-level.d.ts +7 -0
  20. package/dist/tools/create-level.d.ts.map +1 -1
  21. package/dist/tools/create-level.js +6 -3
  22. package/dist/tools/create-wall.d.ts +7 -0
  23. package/dist/tools/create-wall.d.ts.map +1 -1
  24. package/dist/tools/create-wall.js +6 -3
  25. package/dist/tools/cut-opening.d.ts +7 -0
  26. package/dist/tools/cut-opening.d.ts.map +1 -1
  27. package/dist/tools/cut-opening.js +6 -3
  28. package/dist/tools/delete-node.d.ts +7 -0
  29. package/dist/tools/delete-node.d.ts.map +1 -1
  30. package/dist/tools/delete-node.js +6 -3
  31. package/dist/tools/describe-node.d.ts.map +1 -1
  32. package/dist/tools/describe-node.js +2 -0
  33. package/dist/tools/door-clearance.d.ts +18 -2
  34. package/dist/tools/door-clearance.d.ts.map +1 -1
  35. package/dist/tools/door-clearance.js +101 -7
  36. package/dist/tools/duplicate-level.d.ts +7 -0
  37. package/dist/tools/duplicate-level.d.ts.map +1 -1
  38. package/dist/tools/duplicate-level.js +6 -2
  39. package/dist/tools/export-glb.d.ts.map +1 -1
  40. package/dist/tools/export-glb.js +4 -2
  41. package/dist/tools/export-json.d.ts.map +1 -1
  42. package/dist/tools/export-json.js +2 -0
  43. package/dist/tools/find-nodes.d.ts.map +1 -1
  44. package/dist/tools/find-nodes.js +2 -0
  45. package/dist/tools/get-node.d.ts.map +1 -1
  46. package/dist/tools/get-node.js +2 -0
  47. package/dist/tools/get-scene.d.ts.map +1 -1
  48. package/dist/tools/get-scene.js +2 -0
  49. package/dist/tools/layout-clearance.d.ts +2 -0
  50. package/dist/tools/layout-clearance.d.ts.map +1 -1
  51. package/dist/tools/layout-clearance.js +5 -1
  52. package/dist/tools/live-sync.d.ts +33 -3
  53. package/dist/tools/live-sync.d.ts.map +1 -1
  54. package/dist/tools/live-sync.js +36 -4
  55. package/dist/tools/measure.d.ts +1 -0
  56. package/dist/tools/measure.d.ts.map +1 -1
  57. package/dist/tools/measure.js +4 -0
  58. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  59. package/dist/tools/photo-to-scene/photo-to-scene.js +2 -0
  60. package/dist/tools/place-item.d.ts +7 -0
  61. package/dist/tools/place-item.d.ts.map +1 -1
  62. package/dist/tools/place-item.js +6 -2
  63. package/dist/tools/redo.d.ts +7 -0
  64. package/dist/tools/redo.d.ts.map +1 -1
  65. package/dist/tools/redo.js +6 -4
  66. package/dist/tools/room-tools.d.ts +28 -0
  67. package/dist/tools/room-tools.d.ts.map +1 -1
  68. package/dist/tools/room-tools.js +20 -5
  69. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
  70. package/dist/tools/scene-lifecycle/create-project.js +2 -0
  71. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
  72. package/dist/tools/scene-lifecycle/delete-scene.js +2 -0
  73. package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
  74. package/dist/tools/scene-lifecycle/get-project-status.js +2 -0
  75. package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
  76. package/dist/tools/scene-lifecycle/list-scenes.js +2 -0
  77. package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
  78. package/dist/tools/scene-lifecycle/load-scene.js +2 -0
  79. package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
  80. package/dist/tools/scene-lifecycle/rename-scene.js +2 -0
  81. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
  82. package/dist/tools/scene-lifecycle/save-scene.js +2 -0
  83. package/dist/tools/scene-lifecycle/test-utils.d.ts +5 -1
  84. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
  85. package/dist/tools/scene-lifecycle/test-utils.js +24 -0
  86. package/dist/tools/scene-query.d.ts.map +1 -1
  87. package/dist/tools/scene-query.js +6 -0
  88. package/dist/tools/set-zone.d.ts +7 -0
  89. package/dist/tools/set-zone.d.ts.map +1 -1
  90. package/dist/tools/set-zone.js +6 -3
  91. package/dist/tools/templates/create-from-template.d.ts.map +1 -1
  92. package/dist/tools/templates/create-from-template.js +2 -0
  93. package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
  94. package/dist/tools/templates/create-house-from-brief.js +2 -0
  95. package/dist/tools/templates/list-templates.d.ts.map +1 -1
  96. package/dist/tools/templates/list-templates.js +2 -0
  97. package/dist/tools/undo.d.ts +7 -0
  98. package/dist/tools/undo.d.ts.map +1 -1
  99. package/dist/tools/undo.js +6 -4
  100. package/dist/tools/validate-scene.d.ts.map +1 -1
  101. package/dist/tools/validate-scene.js +2 -0
  102. package/dist/tools/variants/generate-variants.d.ts.map +1 -1
  103. package/dist/tools/variants/generate-variants.js +2 -0
  104. package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
  105. package/dist/tools/vision/analyze-floorplan-image.js +2 -0
  106. package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
  107. package/dist/tools/vision/analyze-room-photo.js +2 -0
  108. package/package.json +5 -5
@@ -1,11 +1,12 @@
1
1
  import { CeilingNode, DoorNode, ItemNode, SlabNode, WallNode, WindowNode, ZoneNode, } from '@pascal-app/core/schema';
2
2
  import { z } from 'zod';
3
+ import { ADDITIVE_TOOL_ANNOTATIONS, READ_ONLY_TOOL_ANNOTATIONS } from './annotations';
3
4
  import { findCatalogItem, searchCatalogItems } from './asset-catalog';
4
5
  import { keepoutCoversPlanned, keepoutForPolygonEdge } from './door-clearance';
5
6
  import { ErrorCode, throwMcpError } from './errors';
6
7
  import { polygonArea, polygonBounds, wallLength, wallLocalXFromT } from './geometry';
7
8
  import { collectDoorKeepouts, collectOccupiedFootprints, findValidPlacement, itemPlanAabb, } from './layout-clearance';
8
- import { publishLiveSceneSnapshot } from './live-sync';
9
+ import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot, } from './live-sync';
9
10
  import { measurement } from './measurement';
10
11
  import { NodeIdSchema, Vec2Schema } from './schemas';
11
12
  const ROOM_TYPES = [
@@ -47,6 +48,7 @@ export const createRoomOutput = {
47
48
  ceilingId: z.string(),
48
49
  wallIds: z.array(z.string()),
49
50
  areaSqMeters: z.number(),
51
+ ...liveSyncOutput,
50
52
  };
51
53
  export const addDoorInput = {
52
54
  wallId: NodeIdSchema,
@@ -65,6 +67,7 @@ export const addDoorOutput = {
65
67
  wallLength: z.number(),
66
68
  clamped: z.boolean(),
67
69
  coordinateSystem: z.literal('wall-local-meters'),
70
+ ...liveSyncOutput,
68
71
  };
69
72
  export const addWindowInput = {
70
73
  wallId: NodeIdSchema,
@@ -86,6 +89,7 @@ export const addWindowOutput = {
86
89
  clamped: z.boolean(),
87
90
  coordinateSystem: z.literal('wall-local-meters'),
88
91
  sillHeight: z.number(),
92
+ ...liveSyncOutput,
89
93
  };
90
94
  export const furnishRoomInput = {
91
95
  levelId: NodeIdSchema.optional(),
@@ -98,6 +102,7 @@ export const furnishRoomOutput = {
98
102
  placed: z.number(),
99
103
  itemIds: z.array(z.string()),
100
104
  skipped: z.array(z.string()),
105
+ ...liveSyncOutput,
101
106
  };
102
107
  function textResult(payload) {
103
108
  return {
@@ -340,6 +345,7 @@ export function registerSearchAssets(server) {
340
345
  description: 'Search the built-in MCP item catalog by keyword. Call before place_item when you need a valid catalogItemId.',
341
346
  inputSchema: searchAssetsInput,
342
347
  outputSchema: searchAssetsOutput,
348
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
343
349
  }, async ({ query, category }) => {
344
350
  const results = searchCatalogItems({ query, category }).map((item) => ({
345
351
  id: item.id,
@@ -358,6 +364,7 @@ export function registerCreateRoom(server, bridge) {
358
364
  description: 'Create a room on a level: zone, slab, ceiling, and one wall per polygon edge. Returns wallIds in polygon edge order.',
359
365
  inputSchema: createRoomInput,
360
366
  outputSchema: createRoomOutput,
367
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
361
368
  }, async ({ levelId, name, polygon, color, wallHeight, wallThickness }) => {
362
369
  assertLevel(bridge, levelId);
363
370
  const points = polygon;
@@ -387,13 +394,14 @@ export function registerCreateRoom(server, bridge) {
387
394
  parentId: levelId,
388
395
  })),
389
396
  ]);
390
- await publishLiveSceneSnapshot(bridge, 'create_room');
397
+ const persistence = await publishLiveSceneSnapshot(bridge, 'create_room');
391
398
  return textResult({
392
399
  zoneId: zone.id,
393
400
  slabId: slab.id,
394
401
  ceilingId: ceiling.id,
395
402
  wallIds: walls.map((wall) => wall.id),
396
403
  areaSqMeters: Math.round(polygonArea(points) * 100) / 100,
404
+ ...persistencePayload(persistence),
397
405
  });
398
406
  });
399
407
  }
@@ -403,6 +411,7 @@ export function registerAddDoor(server, bridge) {
403
411
  description: 'Add a door to an existing wall. t/position is 0..1 along the wall: 0 = start, 0.5 = center, 1 = end.',
404
412
  inputSchema: addDoorInput,
405
413
  outputSchema: addDoorOutput,
414
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
406
415
  }, async ({ wallId, t, position, width = 0.9, height = 2.1, hingesSide, swingDirection }) => {
407
416
  const wall = assertWall(bridge, wallId);
408
417
  const length = wallLength(wall);
@@ -421,7 +430,7 @@ export function registerAddDoor(server, bridge) {
421
430
  ...(swingDirection ? { swingDirection } : {}),
422
431
  });
423
432
  const id = bridge.createNode(door, wallId);
424
- await publishLiveSceneSnapshot(bridge, 'add_door');
433
+ const persistence = await publishLiveSceneSnapshot(bridge, 'add_door');
425
434
  return textResult({
426
435
  doorId: id,
427
436
  localX,
@@ -430,6 +439,7 @@ export function registerAddDoor(server, bridge) {
430
439
  wallLength: length,
431
440
  clamped: Math.abs(localX - wallT * length) > 1e-9,
432
441
  coordinateSystem: 'wall-local-meters',
442
+ ...persistencePayload(persistence),
433
443
  });
434
444
  });
435
445
  }
@@ -439,6 +449,7 @@ export function registerAddWindow(server, bridge) {
439
449
  description: 'Add a window to an existing wall. t/position is 0..1 along the wall; sillHeight is the height from floor to window bottom.',
440
450
  inputSchema: addWindowInput,
441
451
  outputSchema: addWindowOutput,
452
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
442
453
  }, async ({ wallId, t, position, width = 1.5, height = 1.5, sillHeight = 0.9 }) => {
443
454
  const wall = assertWall(bridge, wallId);
444
455
  const length = wallLength(wall);
@@ -455,7 +466,7 @@ export function registerAddWindow(server, bridge) {
455
466
  height,
456
467
  });
457
468
  const id = bridge.createNode(windowNode, wallId);
458
- await publishLiveSceneSnapshot(bridge, 'add_window');
469
+ const persistence = await publishLiveSceneSnapshot(bridge, 'add_window');
459
470
  return textResult({
460
471
  windowId: id,
461
472
  localX,
@@ -465,6 +476,7 @@ export function registerAddWindow(server, bridge) {
465
476
  clamped: Math.abs(localX - wallT * length) > 1e-9,
466
477
  coordinateSystem: 'wall-local-meters',
467
478
  sillHeight,
479
+ ...persistencePayload(persistence),
468
480
  });
469
481
  });
470
482
  }
@@ -474,6 +486,7 @@ export function registerFurnishRoom(server, bridge) {
474
486
  description: 'Place furniture for a room type (levelId+polygon or zoneId). Skips or nudges poses that block door clear zones or overlap existing items (rotation-aware). Parent floor items to the level.',
475
487
  inputSchema: furnishRoomInput,
476
488
  outputSchema: furnishRoomOutput,
489
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
477
490
  }, async ({ levelId, zoneId, roomType, polygon, doorWallIndex }) => {
478
491
  const room = inferRoomGeometry(bridge, levelId, polygon, zoneId);
479
492
  assertLevel(bridge, room.levelId);
@@ -552,18 +565,20 @@ export function registerFurnishRoom(server, bridge) {
552
565
  },
553
566
  }));
554
567
  }
568
+ let persistence = 'published';
555
569
  if (items.length > 0) {
556
570
  bridge.applyPatch(items.map((item) => ({
557
571
  op: 'create',
558
572
  node: item,
559
573
  parentId: room.levelId,
560
574
  })));
561
- await publishLiveSceneSnapshot(bridge, 'furnish_room');
575
+ persistence = await publishLiveSceneSnapshot(bridge, 'furnish_room');
562
576
  }
563
577
  return textResult({
564
578
  placed: items.length,
565
579
  itemIds: items.map((item) => item.id),
566
580
  skipped,
581
+ ...persistencePayload(persistence),
567
582
  });
568
583
  });
569
584
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-project.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/create-project.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,kBAAkB,CAAA;AAIvD,eAAO,MAAM,kBAAkB;;;;CAI9B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;CAsB/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAgD1F"}
1
+ {"version":3,"file":"create-project.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/create-project.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,kBAAkB;;;;CAI9B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;CAsB/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAiD1F"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { ADDITIVE_TOOL_ANNOTATIONS } from '../annotations';
2
3
  import { ErrorCode, throwMcpError } from '../errors';
3
4
  import { currentLevelContext, projectStatusPayload } from './metadata';
4
5
  export const createProjectInput = {
@@ -35,6 +36,7 @@ export function registerCreateProject(server, operations) {
35
36
  description: 'Create a browser-visible Pascal project for the authenticated user. Use this before save_scene when the user asks for a new project.',
36
37
  inputSchema: createProjectInput,
37
38
  outputSchema: createProjectOutput,
39
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
38
40
  }, async ({ name, id, isPrivate }) => {
39
41
  if (!operations.canCreateProject) {
40
42
  throwMcpError(ErrorCode.InvalidRequest, 'create_project_unavailable: this MCP store cannot create hosted projects');
@@ -1 +1 @@
1
- {"version":3,"file":"delete-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/delete-scene.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,kBAAkB,CAAA;AAIvD,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAmCxF"}
1
+ {"version":3,"file":"delete-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/delete-scene.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CAoCxF"}
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { SceneNotFoundError, SceneVersionConflictError } from '../../storage/types';
3
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
3
4
  import { ErrorCode, throwMcpError } from '../errors';
4
5
  export const deleteSceneInput = {
5
6
  id: z.string().min(1).max(64),
@@ -14,6 +15,7 @@ export function registerDeleteScene(server, operations) {
14
15
  description: 'Delete a scene from the SceneStore by id. Optionally pass `expectedVersion` for optimistic concurrency.',
15
16
  inputSchema: deleteSceneInput,
16
17
  outputSchema: deleteSceneOutput,
18
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
17
19
  }, async ({ id, expectedVersion }) => {
18
20
  try {
19
21
  const deleted = await operations.deleteStoredScene(id, {
@@ -1 +1 @@
1
- {"version":3,"file":"get-project-status.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/get-project-status.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,kBAAkB,CAAA;AAIvD,eAAO,MAAM,qBAAqB;;CAEjC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;CAsBlC,CAAA;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA2C7F"}
1
+ {"version":3,"file":"get-project-status.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/get-project-status.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,qBAAqB;;CAEjC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;CAsBlC,CAAA;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA4C7F"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
2
3
  import { ErrorCode, McpError, throwMcpError } from '../errors';
3
4
  import { currentLevelContext, projectStatusPayload } from './metadata';
4
5
  export const getProjectStatusInput = {
@@ -33,6 +34,7 @@ export function registerGetProjectStatus(server, operations) {
33
34
  description: 'Authoritative status/debug call for a Pascal project: editor URL, browser-visible version, latest saved version, published version, node count, and graph hash.',
34
35
  inputSchema: getProjectStatusInput,
35
36
  outputSchema: getProjectStatusOutput,
37
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
36
38
  }, async ({ id }) => {
37
39
  try {
38
40
  const status = await operations.getProjectStatus(id);
@@ -1 +1 @@
1
- {"version":3,"file":"list-scenes.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/list-scenes.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;CAmB5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA2BvF"}
1
+ {"version":3,"file":"list-scenes.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/list-scenes.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,kBAAkB,CAAA;AAMvD,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;CAmB5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA4BvF"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { READ_ONLY_TOOL_ANNOTATIONS } from '../annotations';
2
3
  import { ErrorCode, throwMcpError } from '../errors';
3
4
  const DEFAULT_LIMIT = 100;
4
5
  export const listScenesInput = {
@@ -29,6 +30,7 @@ export function registerListScenes(server, operations) {
29
30
  description: 'List scenes in the SceneStore. Optionally filter by `projectId` and cap results with `limit` (default 100).',
30
31
  inputSchema: listScenesInput,
31
32
  outputSchema: listScenesOutput,
33
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
32
34
  }, async ({ projectId, limit }) => {
33
35
  try {
34
36
  const scenes = await operations.listScenes({
@@ -1 +1 @@
1
- {"version":3,"file":"load-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/load-scene.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,kBAAkB,CAAA;AAIvD,eAAO,MAAM,cAAc;;CAE1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgClF"}
1
+ {"version":3,"file":"load-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/load-scene.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,cAAc;;CAE1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiClF"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
2
3
  import { ErrorCode, throwMcpError } from '../errors';
3
4
  import { currentLevelContext, sceneMetaPayload } from './metadata';
4
5
  export const loadSceneInput = {
@@ -30,6 +31,7 @@ export function registerLoadScene(server, bridge) {
30
31
  description: 'Load a scene from the SceneStore into the bridge. Returns the scene metadata. Throws `scene_not_found` if the id does not exist.',
31
32
  inputSchema: loadSceneInput,
32
33
  outputSchema: loadSceneOutput,
34
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
33
35
  }, async ({ id }) => {
34
36
  const result = await bridge.loadStoredScene(id);
35
37
  if (!result) {
@@ -1 +1 @@
1
- {"version":3,"file":"rename-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/rename-scene.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,kBAAkB,CAAA;AAIvD,eAAO,MAAM,gBAAgB;;;;CAI5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;CAW7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA8CxF"}
1
+ {"version":3,"file":"rename-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/rename-scene.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,kBAAkB,CAAA;AAKvD,eAAO,MAAM,gBAAgB;;;;CAI5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;CAW7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI,CA+CxF"}
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { SceneNotFoundError, SceneVersionConflictError } from '../../storage/types';
3
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
3
4
  import { ErrorCode, throwMcpError } from '../errors';
4
5
  export const renameSceneInput = {
5
6
  id: z.string().min(1).max(64),
@@ -24,6 +25,7 @@ export function registerRenameScene(server, operations) {
24
25
  description: 'Rename a scene in the SceneStore. Returns the updated SceneMeta. Optionally pass `expectedVersion` for optimistic concurrency.',
25
26
  inputSchema: renameSceneInput,
26
27
  outputSchema: renameSceneOutput,
28
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
27
29
  }, async ({ id, newName, expectedVersion }) => {
28
30
  try {
29
31
  const meta = await operations.renameStoredScene(id, newName, {
@@ -1 +1 @@
1
- {"version":3,"file":"save-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/save-scene.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,kBAAkB,CAAA;AAMvD,eAAO,MAAM,cAAc;;;;;;;;;;;;;CA0B1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiGlF"}
1
+ {"version":3,"file":"save-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/save-scene.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,kBAAkB,CAAA;AAOvD,eAAO,MAAM,cAAc;;;;;;;;;;;;;CA0B1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAkGlF"}
@@ -1,6 +1,7 @@
1
1
  import { AnyNode } from '@pascal-app/core/schema';
2
2
  import { z } from 'zod';
3
3
  import { SceneVersionConflictError } from '../../storage/types';
4
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
4
5
  import { ErrorCode, throwMcpError } from '../errors';
5
6
  import { appendLiveSceneEvent } from '../live-sync';
6
7
  import { currentLevelContext, sceneMetaPayload } from './metadata';
@@ -53,6 +54,7 @@ export function registerSaveScene(server, bridge) {
53
54
  description: 'Save the current scene (or a provided graph) to the SceneStore. Defaults to a browser-visible draft save so agents can iterate without creating many project versions. Use saveMode: "checkpoint" for meaningful version history.',
54
55
  inputSchema: saveSceneInput,
55
56
  outputSchema: saveSceneOutput,
57
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
56
58
  }, async ({ id, name, projectId, expectedVersion, saveMode, publish, thumbnail, includeCurrentScene, graph, }) => {
57
59
  let sceneGraph;
58
60
  if (includeCurrentScene) {
@@ -1,6 +1,6 @@
1
1
  import { SceneBridge } from '../../bridge/scene-bridge';
2
2
  import { type SceneOperations } from '../../operations';
3
- import { type ProjectCreateOptions, type ProjectStatus, type SceneListOptions, type SceneMeta, type SceneMutateOptions, type SceneSaveOptions, type SceneStore, type SceneWithGraph } from '../../storage/types';
3
+ import { type ProjectCreateOptions, type ProjectStatus, type SceneEvent, type SceneEventAppendOptions, type SceneEventListOptions, type SceneListOptions, type SceneMeta, type SceneMutateOptions, type SceneSaveOptions, type SceneStore, type SceneWithGraph } from '../../storage/types';
4
4
  export type StoredTextContent = {
5
5
  type: string;
6
6
  text: string;
@@ -23,8 +23,10 @@ export declare class InMemorySceneStore implements SceneStore {
23
23
  readonly backend: "sqlite";
24
24
  private readonly data;
25
25
  private readonly projects;
26
+ private readonly events;
26
27
  private idCounter;
27
28
  private projectCounter;
29
+ private eventCounter;
28
30
  createProject(opts: ProjectCreateOptions): Promise<ProjectStatus>;
29
31
  getProjectStatus(id: string): Promise<ProjectStatus | null>;
30
32
  save(opts: SceneSaveOptions): Promise<SceneMeta>;
@@ -32,6 +34,8 @@ export declare class InMemorySceneStore implements SceneStore {
32
34
  list(opts?: SceneListOptions): Promise<SceneMeta[]>;
33
35
  delete(id: string, opts?: SceneMutateOptions): Promise<boolean>;
34
36
  rename(id: string, newName: string, opts?: SceneMutateOptions): Promise<SceneMeta>;
37
+ appendSceneEvent(opts: SceneEventAppendOptions): Promise<SceneEvent>;
38
+ listSceneEvents(sceneId: string, opts?: SceneEventListOptions): Promise<SceneEvent[]>;
35
39
  private touchProject;
36
40
  private toMeta;
37
41
  private toProjectStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEf,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAG5B,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAClD,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAC3B,GAAG;IACF,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,kBAAkB,CAAA;IACzB,UAAU,EAAE,eAAe,CAAA;CAC5B,CAKA;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAWtB;IACH,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,cAAc,CAAI;IAEpB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAejE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK3D,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IA+DhD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAShD,IAAI,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAanD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAW/D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC;IAmBxF,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,eAAe;CA0BxB"}
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/tools/scene-lifecycle/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEf,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAG5B,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAClD,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAC3B,GAAG;IACF,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,kBAAkB,CAAA;IACzB,UAAU,EAAE,eAAe,CAAA;CAC5B,CAKA;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAWtB;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,YAAY,CAAI;IAElB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAejE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK3D,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IA+DhD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAShD,IAAI,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAanD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAW/D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC;IAmBlF,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC;IAapE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAU3F,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,eAAe;CA0BxB"}
@@ -20,8 +20,10 @@ export class InMemorySceneStore {
20
20
  backend = 'sqlite';
21
21
  data = new Map();
22
22
  projects = new Map();
23
+ events = [];
23
24
  idCounter = 0;
24
25
  projectCounter = 0;
26
+ eventCounter = 0;
25
27
  async createProject(opts) {
26
28
  const id = opts.id ?? `project_${++this.projectCounter}`;
27
29
  const now = new Date().toISOString();
@@ -147,6 +149,28 @@ export class InMemorySceneStore {
147
149
  this.touchProject(id, newName, updated.updatedAt);
148
150
  return this.toMeta(updated);
149
151
  }
152
+ async appendSceneEvent(opts) {
153
+ const event = {
154
+ eventId: ++this.eventCounter,
155
+ sceneId: opts.sceneId,
156
+ version: opts.version,
157
+ kind: opts.kind,
158
+ createdAt: new Date().toISOString(),
159
+ graph: opts.graph,
160
+ };
161
+ this.events.push(event);
162
+ return event;
163
+ }
164
+ async listSceneEvents(sceneId, opts) {
165
+ let events = this.events.filter((event) => event.sceneId === sceneId);
166
+ const afterEventId = opts?.afterEventId;
167
+ if (afterEventId !== undefined) {
168
+ events = events.filter((event) => event.eventId > afterEventId);
169
+ }
170
+ if (opts?.limit !== undefined)
171
+ events = events.slice(0, opts.limit);
172
+ return events;
173
+ }
150
174
  touchProject(id, name, updatedAt) {
151
175
  const existing = this.projects.get(id);
152
176
  if (existing) {
@@ -1 +1 @@
1
- {"version":3,"file":"scene-query.d.ts","sourceRoot":"","sources":["../../src/tools/scene-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAQxE,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAYpD,eAAO,MAAM,gBAAgB;;CAE5B,CAAA;AAID,eAAO,MAAM,gBAAgB;;;;;;;CAO5B,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;CAejC,CAAA;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAA;AA8CD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACvC,MAAM,CAgBR;AAkWD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAyCnF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgBxF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAmBjF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAmBjF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiQpF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAMxF"}
1
+ {"version":3,"file":"scene-query.d.ts","sourceRoot":"","sources":["../../src/tools/scene-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAQxE,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAapD,eAAO,MAAM,gBAAgB;;CAE5B,CAAA;AAID,eAAO,MAAM,gBAAgB;;;;;;;CAO5B,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;CAejC,CAAA;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAA;AA8CD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACvC,MAAM,CAgBR;AAkWD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0CnF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiBxF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAoBjF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAoBjF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAkQpF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAMxF"}
@@ -1,6 +1,7 @@
1
1
  import { DEFAULT_LEVEL_HEIGHT, getStoredLevelHeight, getWallPlaneTop, resolveStairTotalRise, resolveWallEffectiveHeight, } from '@pascal-app/core';
2
2
  import { computeWallSlabSupport } from '@pascal-app/core/spatial-grid';
3
3
  import { z } from 'zod';
4
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations';
4
5
  import { distance2D, pointInPolygon, polygonArea, polygonContainsPolygon, wallLength, } from './geometry';
5
6
  import { layoutIssuesFromScene } from './layout-clearance';
6
7
  import { NodeIdSchema } from './schemas';
@@ -393,6 +394,7 @@ export function registerListLevels(server, bridge) {
393
394
  description: 'List all levels in the current scene with ids, names, floor indices, and child counts.',
394
395
  inputSchema: {},
395
396
  outputSchema: listLevelsOutput,
397
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
396
398
  }, async () => {
397
399
  const activeScene = bridge.getActiveScene();
398
400
  const levels = getLevels(bridge).map((level) => {
@@ -430,6 +432,7 @@ export function registerGetLevelSummary(server, bridge) {
430
432
  description: 'Get a compact model-friendly summary of one level: counts plus walls, zones, slabs, ceilings, and items. Omit levelId to use the first level.',
431
433
  inputSchema: levelScopedInput,
432
434
  outputSchema: getLevelSummaryOutput,
435
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
433
436
  }, async ({ levelId }) => {
434
437
  const resolved = getDefaultLevelId(bridge, levelId);
435
438
  if (!resolved)
@@ -443,6 +446,7 @@ export function registerGetWalls(server, bridge) {
443
446
  description: 'Get walls on a level with start/end coordinates, length, height, thickness, and child doors/windows. Omit levelId to use the first level.',
444
447
  inputSchema: levelScopedInput,
445
448
  outputSchema: getWallsOutput,
449
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
446
450
  }, async ({ levelId }) => {
447
451
  const resolved = getDefaultLevelId(bridge, levelId);
448
452
  if (!resolved)
@@ -459,6 +463,7 @@ export function registerGetZones(server, bridge) {
459
463
  description: 'Get room/zone polygons on a level with names, colors, bounds, and approximate areas. Omit levelId to use the first level.',
460
464
  inputSchema: levelScopedInput,
461
465
  outputSchema: getZonesOutput,
466
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
462
467
  }, async ({ levelId }) => {
463
468
  const resolved = getDefaultLevelId(bridge, levelId);
464
469
  if (!resolved)
@@ -475,6 +480,7 @@ export function registerVerifyScene(server, bridge) {
475
480
  description: 'High-level self-check after complex edits. Returns validation status, per-level room/content counts, empty levels, and practical layout issues.',
476
481
  inputSchema: {},
477
482
  outputSchema: verifySceneOutput,
483
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
478
484
  }, async () => {
479
485
  const validation = bridge.validateScene();
480
486
  const levels = getLevels(bridge).map((level) => {
@@ -8,6 +8,13 @@ export declare const setZoneInput: {
8
8
  properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
9
9
  };
10
10
  export declare const setZoneOutput: {
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
  zoneId: z.ZodString;
12
19
  };
13
20
  export declare function registerSetZone(server: McpServer, bridge: SceneOperations): void;
@@ -1 +1 @@
1
- {"version":3,"file":"set-zone.d.ts","sourceRoot":"","sources":["../../src/tools/set-zone.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,YAAY;;;;;CAKxB,CAAA;AAED,eAAO,MAAM,aAAa;;CAEzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgDhF"}
1
+ {"version":3,"file":"set-zone.d.ts","sourceRoot":"","sources":["../../src/tools/set-zone.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,YAAY;;;;;CAKxB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;;CAGzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDhF"}
@@ -1,7 +1,8 @@
1
1
  import { ZoneNode } from '@pascal-app/core/schema';
2
2
  import { z } from 'zod';
3
+ import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations';
3
4
  import { ErrorCode, throwMcpError } from './errors';
4
- import { publishLiveSceneSnapshot } from './live-sync';
5
+ import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
5
6
  import { NodeIdSchema, Vec2Schema } from './schemas';
6
7
  export const setZoneInput = {
7
8
  levelId: NodeIdSchema,
@@ -11,6 +12,7 @@ export const setZoneInput = {
11
12
  };
12
13
  export const setZoneOutput = {
13
14
  zoneId: z.string(),
15
+ ...liveSyncOutput,
14
16
  };
15
17
  export function registerSetZone(server, bridge) {
16
18
  server.registerTool('set_zone', {
@@ -18,6 +20,7 @@ export function registerSetZone(server, bridge) {
18
20
  description: 'Create a polygonal zone on the given level. label is stored as the zone name and properties are merged into metadata.',
19
21
  inputSchema: setZoneInput,
20
22
  outputSchema: setZoneOutput,
23
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
21
24
  }, async ({ levelId, polygon, label, properties }) => {
22
25
  const parent = bridge.getNode(levelId);
23
26
  if (!parent) {
@@ -38,8 +41,8 @@ export function registerSetZone(server, bridge) {
38
41
  metadata: properties ?? {},
39
42
  });
40
43
  const id = bridge.createNode(zone, levelId);
41
- await publishLiveSceneSnapshot(bridge, 'set_zone');
42
- const payload = { zoneId: id };
44
+ const persistence = await publishLiveSceneSnapshot(bridge, 'set_zone');
45
+ const payload = { zoneId: id, ...persistencePayload(persistence) };
43
46
  return {
44
47
  content: [{ type: 'text', text: JSON.stringify(payload) }],
45
48
  structuredContent: payload,
@@ -1 +1 @@
1
- {"version":3,"file":"create-from-template.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-from-template.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,kBAAkB,CAAA;AAMvD,eAAO,MAAM,uBAAuB;;;IAYlC;;;;;OAKG;;;CAGJ,CAAA;AAED,eAAO,MAAM,wBAAwB;;;;IAInC,yEAAyE;;IAEzE,6DAA6D;;;;;;;;;;;;;;;;;;;;;;;;CAuB9D,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA8F3F"}
1
+ {"version":3,"file":"create-from-template.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-from-template.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,kBAAkB,CAAA;AAOvD,eAAO,MAAM,uBAAuB;;;IAYlC;;;;;OAKG;;;CAGJ,CAAA;AAED,eAAO,MAAM,wBAAwB;;;;IAInC,yEAAyE;;IAEzE,6DAA6D;;;;;;;;;;;;;;;;;;;;;;;;CAuB9D,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA+F3F"}
@@ -2,6 +2,7 @@ import { cloneSceneGraph } from '@pascal-app/core/clone-scene-graph';
2
2
  import { z } from 'zod';
3
3
  import { rehydrateSiteChildren } from '../../lib/rehydrate-site-children';
4
4
  import { isTemplateId, TEMPLATES } from '../../templates';
5
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
5
6
  import { ErrorCode, throwMcpError } from '../errors';
6
7
  import { appendLiveSceneEvent } from '../live-sync';
7
8
  import { currentLevelContext, sceneMetaPayload } from '../scene-lifecycle/metadata';
@@ -68,6 +69,7 @@ export function registerCreateFromTemplate(server, bridge) {
68
69
  description: 'Instantiate a seed Pascal scene template into the bridge. Regenerates all ids before applying. When `save: true` and a SceneStore is wired, also persists the new scene and returns the SceneMeta.',
69
70
  inputSchema: createFromTemplateInput,
70
71
  outputSchema: createFromTemplateOutput,
72
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
71
73
  }, async ({ id, name, save, projectId }) => {
72
74
  if (!isTemplateId(id)) {
73
75
  throwMcpError(ErrorCode.InvalidParams, `unknown_template: ${id}. Call list_templates for the set of valid ids.`);
@@ -1 +1 @@
1
- {"version":3,"file":"create-house-from-brief.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-house-from-brief.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,kBAAkB,CAAA;AAMvD,eAAO,MAAM,yBAAyB;;;;;;;;;CASrC,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;CAmBtC,CAAA;AAmCD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqI7F"}
1
+ {"version":3,"file":"create-house-from-brief.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-house-from-brief.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,kBAAkB,CAAA;AAOvD,eAAO,MAAM,yBAAyB;;;;;;;;;CASrC,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;CAmBtC,CAAA;AAmCD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsI7F"}
@@ -2,6 +2,7 @@ import { cloneSceneGraph } from '@pascal-app/core/clone-scene-graph';
2
2
  import { z } from 'zod';
3
3
  import { rehydrateSiteChildren } from '../../lib/rehydrate-site-children';
4
4
  import { isTemplateId, TEMPLATES } from '../../templates';
5
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations';
5
6
  import { ErrorCode, throwMcpError } from '../errors';
6
7
  import { appendLiveSceneEvent } from '../live-sync';
7
8
  import { currentLevelContext, sceneMetaPayload } from '../scene-lifecycle/metadata';
@@ -66,6 +67,7 @@ export function registerCreateHouseFromBrief(server, bridge) {
66
67
  description: 'High-level hosted workflow for external agents: choose a starter house from a brief, create/save/publish it, and return the editor URL. Use semantic tools afterward for exact customization.',
67
68
  inputSchema: createHouseFromBriefInput,
68
69
  outputSchema: createHouseFromBriefOutput,
70
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
69
71
  }, async ({ brief, projectId, projectName, bedroomCount, rooms, style, landscaping, constraints, }) => {
70
72
  const templateId = chooseTemplate({
71
73
  ...(bedroomCount !== undefined ? { bedroomCount } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"list-templates.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/list-templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,kBAAkB,IAAc,CAAA;AAE7C,eAAO,MAAM,mBAAmB;;;;;;;CAS/B,CAAA;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwB7D"}
1
+ {"version":3,"file":"list-templates.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/list-templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,kBAAkB,IAAc,CAAA;AAE7C,eAAO,MAAM,mBAAmB;;;;;;;CAS/B,CAAA;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyB7D"}
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { TEMPLATES } from '../../templates';
3
+ import { READ_ONLY_TOOL_ANNOTATIONS } from '../annotations';
3
4
  export const listTemplatesInput = {};
4
5
  export const listTemplatesOutput = {
5
6
  templates: z.array(z.object({
@@ -20,6 +21,7 @@ export function registerListTemplates(server) {
20
21
  description: 'List the seed Pascal scene templates available to `create_from_template`. Returns the id, display name, one-line description and node count for each.',
21
22
  inputSchema: listTemplatesInput,
22
23
  outputSchema: listTemplatesOutput,
24
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
23
25
  }, async () => {
24
26
  const templates = Object.values(TEMPLATES).map((entry) => ({
25
27
  id: entry.id,
@@ -5,6 +5,13 @@ export declare const undoInput: {
5
5
  steps: z.ZodOptional<z.ZodNumber>;
6
6
  };
7
7
  export declare const undoOutput: {
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
  undone: z.ZodNumber;
9
16
  };
10
17
  export declare function registerUndo(server: McpServer, bridge: SceneOperations): void;
@@ -1 +1 @@
1
- {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/tools/undo.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,SAAS;;CAErB,CAAA;AAED,eAAO,MAAM,UAAU;;CAEtB,CAAA;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAoB7E"}
1
+ {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/tools/undo.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;AAIpD,eAAO,MAAM,SAAS;;CAErB,CAAA;AAED,eAAO,MAAM,UAAU;;;;;;;;;CAGtB,CAAA;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsB7E"}
@@ -1,10 +1,12 @@
1
1
  import { z } from 'zod';
2
- import { publishLiveSceneSnapshot } from './live-sync';
2
+ import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations';
3
+ import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
3
4
  export const undoInput = {
4
5
  steps: z.number().int().positive().optional(),
5
6
  };
6
7
  export const undoOutput = {
7
8
  undone: z.number(),
9
+ ...liveSyncOutput,
8
10
  };
9
11
  export function registerUndo(server, bridge) {
10
12
  server.registerTool('undo', {
@@ -12,11 +14,11 @@ export function registerUndo(server, bridge) {
12
14
  description: 'Undo the most recent N steps in the scene history (default 1). Returns the number of steps actually undone.',
13
15
  inputSchema: undoInput,
14
16
  outputSchema: undoOutput,
17
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
15
18
  }, async ({ steps }) => {
16
19
  const undone = bridge.undo(steps ?? 1);
17
- if (undone > 0)
18
- await publishLiveSceneSnapshot(bridge, 'undo');
19
- const payload = { undone };
20
+ const persistence = undone > 0 ? await publishLiveSceneSnapshot(bridge, 'undo') : 'published';
21
+ const payload = { undone, ...persistencePayload(persistence) };
20
22
  return {
21
23
  content: [{ type: 'text', text: JSON.stringify(payload) }],
22
24
  structuredContent: payload,
@@ -1 +1 @@
1
- {"version":3,"file":"validate-scene.d.ts","sourceRoot":"","sources":["../../src/tools/validate-scene.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;AAEpD,eAAO,MAAM,kBAAkB,IAAK,CAAA;AAEpC,eAAO,MAAM,mBAAmB;;;;;;;CAS/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAkBtF"}
1
+ {"version":3,"file":"validate-scene.d.ts","sourceRoot":"","sources":["../../src/tools/validate-scene.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,kBAAkB,IAAK,CAAA;AAEpC,eAAO,MAAM,mBAAmB;;;;;;;CAS/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAmBtF"}