@pascal-app/mcp 1.0.3 → 1.0.4

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 (193) hide show
  1. package/README.md +47 -26
  2. package/dist/bin/pascal-mcp.js +8 -2
  3. package/dist/bridge/scene-bridge.d.ts +39 -6
  4. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  5. package/dist/bridge/scene-bridge.js +225 -45
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/operations/scene-operations.d.ts +23 -3
  10. package/dist/operations/scene-operations.d.ts.map +1 -1
  11. package/dist/operations/scene-operations.js +31 -1
  12. package/dist/prompts/from-brief.js +1 -1
  13. package/dist/prompts/iterate-on-feedback.js +1 -1
  14. package/dist/prompts/scene-guidance.js +3 -3
  15. package/dist/resources/agent-guide.d.ts.map +1 -1
  16. package/dist/resources/agent-guide.js +22 -1
  17. package/dist/resources/catalog-items.d.ts +4 -5
  18. package/dist/resources/catalog-items.d.ts.map +1 -1
  19. package/dist/resources/catalog-items.js +7 -9
  20. package/dist/resources/design-schema.d.ts +9 -0
  21. package/dist/resources/design-schema.d.ts.map +1 -0
  22. package/dist/resources/design-schema.js +21 -0
  23. package/dist/resources/index.d.ts +3 -1
  24. package/dist/resources/index.d.ts.map +1 -1
  25. package/dist/resources/index.js +5 -2
  26. package/dist/server.d.ts +19 -0
  27. package/dist/server.d.ts.map +1 -1
  28. package/dist/server.js +14 -6
  29. package/dist/storage/disk-artifact-store.d.ts +9 -0
  30. package/dist/storage/disk-artifact-store.d.ts.map +1 -0
  31. package/dist/storage/disk-artifact-store.js +46 -0
  32. package/dist/storage/index.d.ts +1 -0
  33. package/dist/storage/index.d.ts.map +1 -1
  34. package/dist/storage/index.js +1 -0
  35. package/dist/storage/sqlite-scene-store.d.ts +2 -2
  36. package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
  37. package/dist/storage/sqlite-scene-store.js +4 -3
  38. package/dist/storage/types.d.ts +32 -3
  39. package/dist/storage/types.d.ts.map +1 -1
  40. package/dist/storage/types.js +12 -0
  41. package/dist/tools/add-column.d.ts +5 -0
  42. package/dist/tools/add-column.d.ts.map +1 -0
  43. package/dist/tools/add-column.js +58 -0
  44. package/dist/tools/add-object.d.ts +60 -0
  45. package/dist/tools/add-object.d.ts.map +1 -0
  46. package/dist/tools/add-object.js +156 -0
  47. package/dist/tools/apply-patch.d.ts +3 -0
  48. package/dist/tools/apply-patch.d.ts.map +1 -1
  49. package/dist/tools/apply-patch.js +28 -2
  50. package/dist/tools/asset-catalog.d.ts +9 -5
  51. package/dist/tools/asset-catalog.d.ts.map +1 -1
  52. package/dist/tools/asset-catalog.js +10 -12
  53. package/dist/tools/check-collisions.d.ts +1 -1
  54. package/dist/tools/check-collisions.d.ts.map +1 -1
  55. package/dist/tools/check-collisions.js +1 -2
  56. package/dist/tools/clear-scene.d.ts +9 -0
  57. package/dist/tools/clear-scene.d.ts.map +1 -0
  58. package/dist/tools/clear-scene.js +59 -0
  59. package/dist/tools/collections.d.ts +4 -0
  60. package/dist/tools/collections.d.ts.map +1 -0
  61. package/dist/tools/collections.js +52 -0
  62. package/dist/tools/construction-tools.d.ts +8 -41
  63. package/dist/tools/construction-tools.d.ts.map +1 -1
  64. package/dist/tools/construction-tools.js +120 -230
  65. package/dist/tools/create-unit.d.ts +2 -0
  66. package/dist/tools/create-unit.d.ts.map +1 -1
  67. package/dist/tools/describe-node.d.ts.map +1 -1
  68. package/dist/tools/describe-node.js +8 -6
  69. package/dist/tools/errors.d.ts +11 -0
  70. package/dist/tools/errors.d.ts.map +1 -1
  71. package/dist/tools/errors.js +10 -0
  72. package/dist/tools/find-nodes.d.ts +3 -19
  73. package/dist/tools/find-nodes.d.ts.map +1 -1
  74. package/dist/tools/find-nodes.js +150 -53
  75. package/dist/tools/geometry-script-worker.d.ts +2 -0
  76. package/dist/tools/geometry-script-worker.d.ts.map +1 -0
  77. package/dist/tools/geometry-script-worker.js +21 -0
  78. package/dist/tools/get-scene.d.ts +1 -0
  79. package/dist/tools/get-scene.d.ts.map +1 -1
  80. package/dist/tools/get-scene.js +3 -1
  81. package/dist/tools/honest-patch-guard.d.ts +2 -0
  82. package/dist/tools/honest-patch-guard.d.ts.map +1 -0
  83. package/dist/tools/honest-patch-guard.js +32 -0
  84. package/dist/tools/hosted-services.d.ts +4 -0
  85. package/dist/tools/hosted-services.d.ts.map +1 -0
  86. package/dist/tools/hosted-services.js +30 -0
  87. package/dist/tools/index.d.ts +10 -1
  88. package/dist/tools/index.d.ts.map +1 -1
  89. package/dist/tools/index.js +17 -26
  90. package/dist/tools/live-sync.d.ts +24 -5
  91. package/dist/tools/live-sync.d.ts.map +1 -1
  92. package/dist/tools/live-sync.js +47 -26
  93. package/dist/tools/local-geometry-scripts.d.ts +8 -0
  94. package/dist/tools/local-geometry-scripts.d.ts.map +1 -0
  95. package/dist/tools/local-geometry-scripts.js +67 -0
  96. package/dist/tools/measure.d.ts +6 -0
  97. package/dist/tools/measure.d.ts.map +1 -1
  98. package/dist/tools/measure.js +33 -65
  99. package/dist/tools/measurement.d.ts +1 -36
  100. package/dist/tools/measurement.d.ts.map +1 -1
  101. package/dist/tools/measurement.js +2 -94
  102. package/dist/tools/node-world-point.d.ts +37 -0
  103. package/dist/tools/node-world-point.d.ts.map +1 -0
  104. package/dist/tools/node-world-point.js +457 -0
  105. package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
  106. package/dist/tools/normalize-schema-dialect.js +20 -0
  107. package/dist/tools/patch-guards.d.ts +58 -0
  108. package/dist/tools/patch-guards.d.ts.map +1 -0
  109. package/dist/tools/patch-guards.js +253 -0
  110. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  111. package/dist/tools/photo-to-scene/photo-to-scene.js +3 -2
  112. package/dist/tools/place-design.d.ts +34 -0
  113. package/dist/tools/place-design.d.ts.map +1 -0
  114. package/dist/tools/place-design.js +91 -0
  115. package/dist/tools/redo.d.ts +2 -0
  116. package/dist/tools/redo.d.ts.map +1 -1
  117. package/dist/tools/redo.js +2 -2
  118. package/dist/tools/room-tools.d.ts +50 -113
  119. package/dist/tools/room-tools.d.ts.map +1 -1
  120. package/dist/tools/room-tools.js +129 -565
  121. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
  122. package/dist/tools/scene-lifecycle/create-project.js +26 -1
  123. package/dist/tools/scene-lifecycle/delete-scene.d.ts +2 -0
  124. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
  125. package/dist/tools/scene-lifecycle/delete-scene.js +4 -3
  126. package/dist/tools/scene-lifecycle/save-scene.d.ts +1 -0
  127. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
  128. package/dist/tools/scene-lifecycle/save-scene.js +46 -6
  129. package/dist/tools/scene-lifecycle/test-utils.d.ts +2 -2
  130. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
  131. package/dist/tools/scene-lifecycle/test-utils.js +2 -1
  132. package/dist/tools/set-unit-members.d.ts +2 -0
  133. package/dist/tools/set-unit-members.d.ts.map +1 -1
  134. package/dist/tools/set-zone.d.ts +2 -0
  135. package/dist/tools/set-zone.d.ts.map +1 -1
  136. package/dist/tools/set-zone.js +2 -2
  137. package/dist/tools/shared-tools.d.ts +30 -0
  138. package/dist/tools/shared-tools.d.ts.map +1 -0
  139. package/dist/tools/shared-tools.js +361 -0
  140. package/dist/tools/structure-tools.d.ts +57 -0
  141. package/dist/tools/structure-tools.d.ts.map +1 -0
  142. package/dist/tools/structure-tools.js +57 -0
  143. package/dist/tools/undo.d.ts +2 -0
  144. package/dist/tools/undo.d.ts.map +1 -1
  145. package/dist/tools/undo.js +2 -2
  146. package/dist/tools/validate-design.d.ts +136 -0
  147. package/dist/tools/validate-design.d.ts.map +1 -0
  148. package/dist/tools/validate-design.js +28 -0
  149. package/dist/tools/variants/mutations.js +1 -1
  150. package/dist/tools/view-scene.d.ts +38 -0
  151. package/dist/tools/view-scene.d.ts.map +1 -0
  152. package/dist/tools/view-scene.js +63 -0
  153. package/dist/version.d.ts +5 -0
  154. package/dist/version.d.ts.map +1 -1
  155. package/dist/version.js +17 -3
  156. package/examples/embed-in-agent.ts +1 -1
  157. package/examples/generate-apartment.md +9 -11
  158. package/examples/photo-to-scene.md +1 -1
  159. package/examples/renovate-from-photos.md +3 -4
  160. package/package.json +10 -4
  161. package/dist/tools/create-level.d.ts +0 -21
  162. package/dist/tools/create-level.d.ts.map +0 -1
  163. package/dist/tools/create-level.js +0 -63
  164. package/dist/tools/create-wall.d.ts +0 -22
  165. package/dist/tools/create-wall.d.ts.map +0 -1
  166. package/dist/tools/create-wall.js +0 -57
  167. package/dist/tools/cut-opening.d.ts +0 -25
  168. package/dist/tools/cut-opening.d.ts.map +0 -1
  169. package/dist/tools/cut-opening.js +0 -61
  170. package/dist/tools/delete-node.d.ts +0 -19
  171. package/dist/tools/delete-node.d.ts.map +0 -1
  172. package/dist/tools/delete-node.js +0 -40
  173. package/dist/tools/door-clearance.d.ts +0 -135
  174. package/dist/tools/door-clearance.d.ts.map +0 -1
  175. package/dist/tools/door-clearance.js +0 -334
  176. package/dist/tools/duplicate-level.d.ts +0 -19
  177. package/dist/tools/duplicate-level.d.ts.map +0 -1
  178. package/dist/tools/duplicate-level.js +0 -60
  179. package/dist/tools/geometry.d.ts +0 -24
  180. package/dist/tools/geometry.d.ts.map +0 -1
  181. package/dist/tools/geometry.js +0 -94
  182. package/dist/tools/get-node.d.ts +0 -11
  183. package/dist/tools/get-node.d.ts.map +0 -1
  184. package/dist/tools/get-node.js +0 -29
  185. package/dist/tools/layout-clearance.d.ts +0 -105
  186. package/dist/tools/layout-clearance.d.ts.map +0 -1
  187. package/dist/tools/layout-clearance.js +0 -190
  188. package/dist/tools/place-item.d.ts +0 -22
  189. package/dist/tools/place-item.d.ts.map +0 -1
  190. package/dist/tools/place-item.js +0 -89
  191. package/dist/tools/scene-query.d.ts +0 -62
  192. package/dist/tools/scene-query.d.ts.map +0 -1
  193. package/dist/tools/scene-query.js +0 -670
@@ -0,0 +1,361 @@
1
+ import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, writeCollections, } from '@pascal-app/core/agent-operations';
2
+ import { addLevelTool, addWallTool, createRoomTool, createStairTool, deleteNodeTool, duplicateLevelTool, findByTypeTool, fitStairTool, furnishRoomTool, getLevelSummaryTool, getNodeTool, getWallsTool, getZonesTool, listLevelsTool, measureStairTool, paintTool, placeItemsTool, ROOM_TOOL_CONTRACTS, searchAssetsTool, searchMaterialsTool, verifySceneTool, } from '@pascal-app/core/agent-tools';
3
+ import { MaterialSchema, SceneMaterial, } from '@pascal-app/core/schema';
4
+ import { z } from 'zod';
5
+ import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS, READ_ONLY_TOOL_ANNOTATIONS, } from './annotations.js';
6
+ import { builtInCatalog } from './asset-catalog.js';
7
+ import { registerCollectionTools } from './collections.js';
8
+ import { refusalResult } from './errors.js';
9
+ import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
10
+ import { ROOM_TOOL_ANNOTATIONS, structureOutput } from './structure-tools.js';
11
+ const jsonObject = z.record(z.string(), z.unknown());
12
+ /** What the scene holds after a mutating call (core achievedChanges). */
13
+ const achievedOutput = {
14
+ achieved: z
15
+ .object({
16
+ created: z.record(z.string(), z.number()),
17
+ updated: z.number(),
18
+ deleted: z.record(z.string(), z.number()),
19
+ unchanged: z.literal(true).optional(),
20
+ })
21
+ .optional(),
22
+ };
23
+ const levelRoleOutput = {
24
+ levelId: z.string(),
25
+ levelName: z.string().optional(),
26
+ floorIndex: z.number(),
27
+ role: z.string(),
28
+ metadataRole: z.string().nullable(),
29
+ isOccupiedStory: z.boolean(),
30
+ isSupportLevel: z.boolean(),
31
+ referenceLevelId: z.string().nullable(),
32
+ };
33
+ const SHARED_TOOLS = [
34
+ {
35
+ contract: measureStairTool,
36
+ operation: AGENT_OPERATIONS.measure_stair,
37
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
38
+ outputSchema: { measurements: z.json(), layouts: z.json() },
39
+ },
40
+ {
41
+ contract: fitStairTool,
42
+ operation: AGENT_OPERATIONS.fit_stair,
43
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
44
+ outputSchema: { stairId: z.string().min(1), measurements: z.json(), ...liveSyncOutput },
45
+ },
46
+ {
47
+ contract: findByTypeTool,
48
+ operation: AGENT_OPERATIONS.find_by_type,
49
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
50
+ },
51
+ {
52
+ contract: listLevelsTool,
53
+ operation: AGENT_OPERATIONS.list_levels,
54
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
55
+ outputSchema: {
56
+ activeSceneId: z.string().nullable(),
57
+ activeLevelId: z.string().nullable(),
58
+ levelCount: z.number(),
59
+ occupiedStoryCount: z.number(),
60
+ supportLevelCount: z.number(),
61
+ roofLevelIds: z.array(z.string()),
62
+ levels: z.array(jsonObject),
63
+ },
64
+ envelope: (bridge) => ({ activeSceneId: bridge.getActiveScene()?.id ?? null }),
65
+ },
66
+ {
67
+ contract: getNodeTool,
68
+ operation: AGENT_OPERATIONS.get_node,
69
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
70
+ outputSchema: { node: jsonObject, materials: z.record(z.string(), SceneMaterial).optional() },
71
+ },
72
+ {
73
+ contract: getLevelSummaryTool,
74
+ operation: AGENT_OPERATIONS.get_level_summary,
75
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
76
+ outputSchema: {
77
+ ...levelRoleOutput,
78
+ counts: jsonObject,
79
+ walls: z.array(jsonObject),
80
+ zones: z.array(jsonObject),
81
+ slabs: z.array(jsonObject),
82
+ ceilings: z.array(jsonObject),
83
+ items: z.array(jsonObject),
84
+ openings: z.array(jsonObject),
85
+ stairs: z.array(jsonObject),
86
+ roofs: z.array(jsonObject),
87
+ other: z.array(jsonObject),
88
+ },
89
+ },
90
+ {
91
+ contract: getWallsTool,
92
+ operation: AGENT_OPERATIONS.get_walls,
93
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
94
+ outputSchema: { levelId: z.string(), walls: z.array(jsonObject) },
95
+ },
96
+ {
97
+ contract: getZonesTool,
98
+ operation: AGENT_OPERATIONS.get_zones,
99
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
100
+ outputSchema: { levelId: z.string(), zones: z.array(jsonObject) },
101
+ },
102
+ {
103
+ contract: duplicateLevelTool,
104
+ operation: AGENT_OPERATIONS.duplicate_level,
105
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
106
+ outputSchema: {
107
+ newLevelId: z.string(),
108
+ name: z.string().optional(),
109
+ floorIndex: z.number(),
110
+ shiftedLevelIds: z.array(z.string()),
111
+ copied: z.record(z.string(), z.number()),
112
+ skipped: z.record(z.string(), z.number()),
113
+ newNodeIds: z.array(z.string()),
114
+ // A floor copy is hundreds of ids: the result lists 40 and counts the rest.
115
+ newNodeIdsOmitted: z.number().optional(),
116
+ ...achievedOutput,
117
+ ...liveSyncOutput,
118
+ },
119
+ },
120
+ {
121
+ contract: verifySceneTool,
122
+ operation: AGENT_OPERATIONS.verify_scene,
123
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
124
+ outputSchema: {
125
+ ok: z.boolean(),
126
+ valid: z.boolean(),
127
+ levelCount: z.number(),
128
+ occupiedStoryCount: z.number(),
129
+ supportLevelCount: z.number(),
130
+ roofLevelIds: z.array(z.string()),
131
+ activeSceneId: z.string().nullable(),
132
+ activeLevelId: z.string().nullable(),
133
+ levels: z.array(jsonObject),
134
+ emptyLevelIds: z.array(z.string()),
135
+ issues: z.array(z.object({
136
+ type: z.string(),
137
+ message: z.string(),
138
+ severity: z.literal('info').optional(),
139
+ wallId: z.string().optional(),
140
+ end: z.enum(['start', 'end']).optional(),
141
+ reason: z.enum(['gap', 'crosses', 'parallel', 'rejected']).optional(),
142
+ gap: z.number().optional(),
143
+ nearestWallId: z.string().optional(),
144
+ })),
145
+ hasIssues: z.boolean(),
146
+ authoredObjects: z
147
+ .array(z.object({
148
+ id: z.string(),
149
+ name: z.string(),
150
+ category: z.string(),
151
+ reason: z.string().nullable(),
152
+ }))
153
+ .optional(),
154
+ },
155
+ envelope: (bridge) => ({ activeSceneId: bridge.getActiveScene()?.id ?? null }),
156
+ },
157
+ {
158
+ contract: addWallTool,
159
+ operation: AGENT_OPERATIONS.add_wall,
160
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
161
+ },
162
+ {
163
+ contract: addLevelTool,
164
+ operation: AGENT_OPERATIONS.add_level,
165
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
166
+ },
167
+ {
168
+ contract: createStairTool,
169
+ operation: AGENT_OPERATIONS.create_stair,
170
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
171
+ },
172
+ {
173
+ contract: placeItemsTool,
174
+ operation: AGENT_OPERATIONS.place_items,
175
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
176
+ catalog: true,
177
+ },
178
+ {
179
+ contract: deleteNodeTool,
180
+ operation: AGENT_OPERATIONS.delete_node,
181
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
182
+ outputSchema: { deletedIds: z.array(z.string()), ...achievedOutput, ...liveSyncOutput },
183
+ },
184
+ {
185
+ contract: createRoomTool,
186
+ operation: AGENT_OPERATIONS.create_room,
187
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
188
+ outputSchema: {
189
+ ok: z.literal(true),
190
+ zoneId: z.string(),
191
+ // The floor plate and the ceiling the host derived; null where it derives none (a terrace).
192
+ slabId: z.string().nullable(),
193
+ ceilingId: z.string().nullable(),
194
+ // One per polygon edge; null where no wall runs along it.
195
+ wallIds: z.array(z.string().nullable()),
196
+ reusedWalls: z.number(),
197
+ areaSqMeters: z.number(),
198
+ doorIds: z.array(z.string()),
199
+ windowIds: z.array(z.string()),
200
+ skippedOpenings: z
201
+ .array(z.object({
202
+ kind: z.enum(['door', 'window']),
203
+ index: z.number(),
204
+ code: z.string(),
205
+ message: z.string(),
206
+ }))
207
+ .optional(),
208
+ message: z.string(),
209
+ ...achievedOutput,
210
+ ...liveSyncOutput,
211
+ },
212
+ },
213
+ {
214
+ contract: furnishRoomTool,
215
+ operation: AGENT_OPERATIONS.furnish_room,
216
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
217
+ catalog: true,
218
+ outputSchema: {
219
+ ok: z.literal(true),
220
+ placed: z.number(),
221
+ itemIds: z.array(z.string()),
222
+ skipped: z.array(z.string()),
223
+ doorWallIndex: z.number(),
224
+ doorsDetected: z.number(),
225
+ message: z.string(),
226
+ ...achievedOutput,
227
+ ...liveSyncOutput,
228
+ },
229
+ },
230
+ {
231
+ contract: searchAssetsTool,
232
+ operation: AGENT_OPERATIONS.search_assets,
233
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
234
+ catalog: true,
235
+ },
236
+ {
237
+ contract: paintTool,
238
+ operation: AGENT_OPERATIONS.paint,
239
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
240
+ outputSchema: {
241
+ ok: z.literal(true),
242
+ painted: z.array(z.object({ id: z.string(), roles: z.array(z.string()) })),
243
+ finish: z.string(),
244
+ materialId: z.string().optional(),
245
+ material: MaterialSchema.optional(),
246
+ createdMaterial: SceneMaterial.optional(),
247
+ note: z.string().optional(),
248
+ ...achievedOutput,
249
+ ...liveSyncOutput,
250
+ },
251
+ },
252
+ {
253
+ contract: searchMaterialsTool,
254
+ operation: AGENT_OPERATIONS.search_materials,
255
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
256
+ },
257
+ ...ROOM_TOOL_CONTRACTS.map((contract) => {
258
+ const name = contract.name;
259
+ return {
260
+ contract,
261
+ operation: AGENT_OPERATIONS[name],
262
+ annotations: ROOM_TOOL_ANNOTATIONS[name],
263
+ outputSchema: { ...structureOutput, ...achievedOutput },
264
+ };
265
+ }),
266
+ ];
267
+ export function toPatches(changes) {
268
+ return [
269
+ ...(changes.materials ?? []).map((material) => ({ op: 'upsert_material', material })),
270
+ ...(changes.create ?? []).map(({ node, parentId }) => ({
271
+ op: 'create',
272
+ node,
273
+ parentId: parentId,
274
+ })),
275
+ ...(changes.update ?? []).map(({ id, data }) => ({
276
+ op: 'update',
277
+ id: id,
278
+ data,
279
+ })),
280
+ ...(changes.delete ?? []).map((id) => ({
281
+ op: 'delete',
282
+ id: id,
283
+ cascade: true,
284
+ })),
285
+ ];
286
+ }
287
+ function writesScene(changes) {
288
+ return Boolean(changes &&
289
+ (toPatches(changes).length ||
290
+ (changes.collections && Object.keys(changes.collections).length)));
291
+ }
292
+ export function registerSharedTools(server, bridge, catalog = builtInCatalog) {
293
+ for (const tool of SHARED_TOOLS)
294
+ registerOperationTool(server, bridge, tool, catalog);
295
+ registerCollectionTools(server, bridge);
296
+ }
297
+ /**
298
+ * Registers one operation tool, as the shared tools are: a host adds a plugin's `'operation'`
299
+ * tools with it, from the plugin's `./agent-tools` contract and `./agent-operations` operation.
300
+ */
301
+ export function registerOperationTool(server, bridge, tool, catalog = builtInCatalog) {
302
+ server.registerTool(tool.contract.name, {
303
+ title: tool.contract.title,
304
+ description: tool.contract.description,
305
+ inputSchema: tool.contract.input,
306
+ // Loose: a client that listed the tools rejects any field the schema leaves out, and the
307
+ // operations in core grow fields (verify_scene's guesses) that this list would miss.
308
+ ...(tool.outputSchema ? { outputSchema: z.looseObject(tool.outputSchema) } : {}),
309
+ annotations: tool.annotations,
310
+ }, async (input) => {
311
+ let outcome;
312
+ // A copy of the map: a host may write its own in place (the hosted bridge does), and a
313
+ // "before" that grows with the call reads every creation as unchanged.
314
+ const before = { ...bridge.getNodes() };
315
+ const context = {
316
+ activeLevelId: null,
317
+ ...(tool.catalog && { catalog: await catalog() }),
318
+ ...(bridge.supportsMaterialUpserts && bridge.getMaterials
319
+ ? { materials: bridge.getMaterials() }
320
+ : {}),
321
+ };
322
+ try {
323
+ outcome = tool.operation(before, input, context);
324
+ }
325
+ catch (error) {
326
+ return refusalResult(error);
327
+ }
328
+ let result = outcome.result;
329
+ let persistence = {};
330
+ // An outcome edits the scene through its node changes, its collection writes, or the
331
+ // follow-up it plans once the host has reconciled; any of them is one history step.
332
+ if (writesScene(outcome.changes) || outcome.afterReconcile) {
333
+ result = bridge.runAsSingleHistoryStep(() => applyAgentOutcome(outcome, {
334
+ getNodes: () => bridge.getNodes(),
335
+ applyChanges: (changes) => {
336
+ const next = toPatches(changes);
337
+ if (next.length)
338
+ bridge.applyPatch(next);
339
+ if (changes.collections && Object.keys(changes.collections).length)
340
+ bridge.setCollections(writeCollections(bridge.getCollections(), changes.collections));
341
+ },
342
+ reconcile: () => {
343
+ bridge.deriveStructure();
344
+ },
345
+ }));
346
+ persistence = persistencePayload(await publishLiveSceneSnapshot(bridge, tool.contract.name));
347
+ }
348
+ // What the scene holds after the call, not only what the call says it built.
349
+ const achieved = outcome.changes ? achievedChanges(before, outcome.changes) : null;
350
+ const payload = {
351
+ ...result,
352
+ ...(achieved ? { achieved } : {}),
353
+ ...(tool.envelope?.(bridge) ?? {}),
354
+ ...persistence,
355
+ };
356
+ return {
357
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
358
+ structuredContent: payload,
359
+ };
360
+ });
361
+ }
@@ -0,0 +1,57 @@
1
+ import type { ROOM_OPERATIONS } from '@pascal-app/core/agent-operations';
2
+ import { z } from 'zod';
3
+ import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
4
+ export type RoomToolName = keyof typeof ROOM_OPERATIONS;
5
+ export declare const ROOM_TOOL_ANNOTATIONS: Record<RoomToolName, typeof ADDITIVE_TOOL_ANNOTATIONS | typeof DESTRUCTIVE_TOOL_ANNOTATIONS>;
6
+ export declare const structureOutput: {
7
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
8
+ unsaved: z.ZodOptional<z.ZodString>;
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>>;
16
+ zoneId: z.ZodOptional<z.ZodString>;
17
+ openingId: z.ZodOptional<z.ZodString>;
18
+ openingIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
19
+ hints: z.ZodOptional<z.ZodArray<z.ZodObject<{
20
+ code: z.ZodLiteral<"manual-ceiling">;
21
+ openingId: z.ZodString;
22
+ surfaceIds: z.ZodArray<z.ZodString>;
23
+ message: z.ZodString;
24
+ }, z.core.$strip>>>;
25
+ idMap: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
26
+ changes: z.ZodNumber;
27
+ separatorId: z.ZodOptional<z.ZodString>;
28
+ separatorIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
29
+ zoneIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
+ payload: z.ZodOptional<z.ZodObject<{
31
+ zoneId: z.ZodString;
32
+ name: z.ZodString;
33
+ mode: z.ZodEnum<{
34
+ merge: "merge";
35
+ delete: "delete";
36
+ blocked: "blocked";
37
+ }>;
38
+ mergedIntoZoneId: z.ZodOptional<z.ZodString>;
39
+ contents: z.ZodEnum<{
40
+ delete: "delete";
41
+ keep: "keep";
42
+ }>;
43
+ wallIds: z.ZodArray<z.ZodString>;
44
+ keptSharedWallIds: z.ZodArray<z.ZodString>;
45
+ separatorIds: z.ZodArray<z.ZodString>;
46
+ keptSharedSeparatorIds: z.ZodArray<z.ZodString>;
47
+ openingIds: z.ZodArray<z.ZodString>;
48
+ itemIds: z.ZodArray<z.ZodString>;
49
+ opensZoneIds: z.ZodArray<z.ZodString>;
50
+ }, z.core.$strip>>;
51
+ conflicts: z.ZodOptional<z.ZodArray<z.ZodObject<{
52
+ code: z.ZodString;
53
+ nodeIds: z.ZodArray<z.ZodString>;
54
+ message: z.ZodString;
55
+ }, z.core.$strip>>>;
56
+ };
57
+ //# sourceMappingURL=structure-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure-tools.d.ts","sourceRoot":"","sources":["../../src/tools/structure-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAA;AAMvF,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,CAAA;AAEvD,eAAO,MAAM,qBAAqB,EAAE,MAAM,CACxC,YAAY,EACZ,OAAO,yBAAyB,GAAG,OAAO,4BAA4B,CAgBvE,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuC3B,CAAA"}
@@ -0,0 +1,57 @@
1
+ import { z } from 'zod';
2
+ import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
3
+ import { liveSyncOutput } from './live-sync.js';
4
+ export const ROOM_TOOL_ANNOTATIONS = {
5
+ cut_floor_opening: ADDITIVE_TOOL_ANNOTATIONS,
6
+ remove_floor_opening: DESTRUCTIVE_TOOL_ANNOTATIONS,
7
+ set_floor_foundation: ADDITIVE_TOOL_ANNOTATIONS,
8
+ set_room_floor_construction: ADDITIVE_TOOL_ANNOTATIONS,
9
+ rebase_floor_reference: DESTRUCTIVE_TOOL_ANNOTATIONS,
10
+ create_mezzanine: ADDITIVE_TOOL_ANNOTATIONS,
11
+ move_zone: DESTRUCTIVE_TOOL_ANNOTATIONS,
12
+ duplicate_zone: DESTRUCTIVE_TOOL_ANNOTATIONS,
13
+ rotate_zone: DESTRUCTIVE_TOOL_ANNOTATIONS,
14
+ lock_outside_faces: DESTRUCTIVE_TOOL_ANNOTATIONS,
15
+ set_zone_intent: ADDITIVE_TOOL_ANNOTATIONS,
16
+ divide_zone: DESTRUCTIVE_TOOL_ANNOTATIONS,
17
+ merge_zones: DESTRUCTIVE_TOOL_ANNOTATIONS,
18
+ delete_zone: DESTRUCTIVE_TOOL_ANNOTATIONS,
19
+ };
20
+ export const structureOutput = {
21
+ zoneId: z.string().optional(),
22
+ openingId: z.string().optional(),
23
+ openingIds: z.array(z.string()).optional(),
24
+ hints: z
25
+ .array(z.object({
26
+ code: z.literal('manual-ceiling'),
27
+ openingId: z.string(),
28
+ surfaceIds: z.array(z.string()),
29
+ message: z.string(),
30
+ }))
31
+ .optional(),
32
+ idMap: z.record(z.string(), z.array(z.string())).optional(),
33
+ changes: z.number(),
34
+ separatorId: z.string().optional(),
35
+ separatorIds: z.array(z.string()).optional(),
36
+ zoneIds: z.array(z.string()).optional(),
37
+ payload: z
38
+ .object({
39
+ zoneId: z.string(),
40
+ name: z.string(),
41
+ mode: z.enum(['delete', 'merge', 'blocked']),
42
+ mergedIntoZoneId: z.string().optional(),
43
+ contents: z.enum(['delete', 'keep']),
44
+ wallIds: z.array(z.string()),
45
+ keptSharedWallIds: z.array(z.string()),
46
+ separatorIds: z.array(z.string()),
47
+ keptSharedSeparatorIds: z.array(z.string()),
48
+ openingIds: z.array(z.string()),
49
+ itemIds: z.array(z.string()),
50
+ opensZoneIds: z.array(z.string()),
51
+ })
52
+ .optional(),
53
+ conflicts: z
54
+ .array(z.object({ code: z.string(), nodeIds: z.array(z.string()), message: z.string() }))
55
+ .optional(),
56
+ ...liveSyncOutput,
57
+ };
@@ -5,6 +5,8 @@ export declare const undoInput: {
5
5
  steps: z.ZodOptional<z.ZodNumber>;
6
6
  };
7
7
  export declare const undoOutput: {
8
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
9
+ unsaved: z.ZodOptional<z.ZodString>;
8
10
  persistence: z.ZodOptional<z.ZodObject<{
9
11
  status: z.ZodEnum<{
10
12
  unbound: "unbound";
@@ -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;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
+ {"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;AASpD,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,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
3
- import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
3
+ import { currentLiveSync, liveSyncOutput, persistencePayload, publishLiveSceneSnapshot, } from './live-sync.js';
4
4
  export const undoInput = {
5
5
  steps: z.number().int().positive().optional(),
6
6
  };
@@ -17,7 +17,7 @@ export function registerUndo(server, bridge) {
17
17
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
18
18
  }, async ({ steps }) => {
19
19
  const undone = bridge.undo(steps ?? 1);
20
- const persistence = undone > 0 ? await publishLiveSceneSnapshot(bridge, 'undo') : 'published';
20
+ const persistence = undone > 0 ? await publishLiveSceneSnapshot(bridge, 'undo') : currentLiveSync(bridge);
21
21
  const payload = { undone, ...persistencePayload(persistence) };
22
22
  return {
23
23
  content: [{ type: 'text', text: JSON.stringify(payload) }],
@@ -0,0 +1,136 @@
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 validateDesignInput: {
5
+ design: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
6
+ parameters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
7
+ };
8
+ export declare const validateDesignOutput: {
9
+ valid: z.ZodBoolean;
10
+ diagnostics: z.ZodArray<z.ZodObject<{
11
+ severity: z.ZodEnum<{
12
+ error: "error";
13
+ warning: "warning";
14
+ }>;
15
+ code: z.ZodEnum<{
16
+ schema: "schema";
17
+ parameters: "parameters";
18
+ invalid_json: "invalid_json";
19
+ rule: "rule";
20
+ sweep: "sweep";
21
+ floating_component: "floating_component";
22
+ unbalanced: "unbalanced";
23
+ behind_wall: "behind_wall";
24
+ draw_budget: "draw_budget";
25
+ design_version_not_enabled: "design_version_not_enabled";
26
+ design_too_large: "design_too_large";
27
+ }>;
28
+ path: z.ZodOptional<z.ZodString>;
29
+ message: z.ZodString;
30
+ hint: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strip>>;
32
+ design: z.ZodNullable<z.ZodObject<{
33
+ name: z.ZodString;
34
+ mounting: z.ZodEnum<{
35
+ ceiling: "ceiling";
36
+ "wall-side": "wall-side";
37
+ floor: "floor";
38
+ }>;
39
+ parameters: z.ZodArray<z.ZodObject<{
40
+ id: z.ZodString;
41
+ default: z.ZodNumber;
42
+ min: z.ZodNumber;
43
+ max: z.ZodNumber;
44
+ step: z.ZodNumber;
45
+ unit: z.ZodString;
46
+ }, z.core.$strip>>;
47
+ slots: z.ZodArray<z.ZodString>;
48
+ }, z.core.$strip>>;
49
+ sweep: z.ZodNullable<z.ZodObject<{
50
+ cases: z.ZodNumber;
51
+ failed: z.ZodNumber;
52
+ failures: z.ZodArray<z.ZodObject<{
53
+ values: z.ZodRecord<z.ZodString, z.ZodNumber>;
54
+ error: z.ZodString;
55
+ }, z.core.$strip>>;
56
+ }, z.core.$strip>>;
57
+ measurements: z.ZodNullable<z.ZodObject<{
58
+ parameters: z.ZodRecord<z.ZodString, z.ZodNumber>;
59
+ bounds: z.ZodObject<{
60
+ min: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
61
+ max: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
62
+ dimensions: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
63
+ }, z.core.$strip>;
64
+ shapes: z.ZodNumber;
65
+ triangles: z.ZodObject<{
66
+ actual: z.ZodNumber;
67
+ budget: z.ZodNumber;
68
+ limit: z.ZodNumber;
69
+ }, z.core.$strip>;
70
+ drawBudget: z.ZodNumber;
71
+ drawGroups: z.ZodArray<z.ZodObject<{
72
+ slot: z.ZodString;
73
+ motionGroup: z.ZodNullable<z.ZodString>;
74
+ shapes: z.ZodNumber;
75
+ triangles: z.ZodNumber;
76
+ }, z.core.$strip>>;
77
+ parts: z.ZodArray<z.ZodObject<{
78
+ id: z.ZodString;
79
+ instances: z.ZodNumber;
80
+ shapes: z.ZodNumber;
81
+ triangles: z.ZodNumber;
82
+ slots: z.ZodArray<z.ZodString>;
83
+ motion: z.ZodNullable<z.ZodEnum<{
84
+ hinge: "hinge";
85
+ slide: "slide";
86
+ spin: "spin";
87
+ }>>;
88
+ lights: z.ZodNumber;
89
+ bounds: z.ZodNullable<z.ZodObject<{
90
+ min: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
91
+ max: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
92
+ dimensions: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
93
+ }, z.core.$strip>>;
94
+ }, z.core.$strip>>;
95
+ datum: z.ZodObject<{
96
+ kind: z.ZodEnum<{
97
+ wall: "wall";
98
+ ceiling: "ceiling";
99
+ floor: "floor";
100
+ }>;
101
+ axis: z.ZodEnum<{
102
+ z: "z";
103
+ y: "y";
104
+ }>;
105
+ value: z.ZodNumber;
106
+ gap: z.ZodNumber;
107
+ contact: z.ZodNullable<z.ZodObject<{
108
+ parts: z.ZodArray<z.ZodString>;
109
+ shapes: z.ZodNumber;
110
+ min: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
111
+ max: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
112
+ }, z.core.$strip>>;
113
+ balanced: z.ZodNullable<z.ZodBoolean>;
114
+ }, z.core.$strip>;
115
+ components: z.ZodObject<{
116
+ count: z.ZodNumber;
117
+ list: z.ZodArray<z.ZodObject<{
118
+ parts: z.ZodArray<z.ZodString>;
119
+ shapes: z.ZodNumber;
120
+ bounds: z.ZodObject<{
121
+ min: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
122
+ max: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
123
+ dimensions: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
124
+ }, z.core.$strip>;
125
+ touchesDatum: z.ZodBoolean;
126
+ datumGap: z.ZodNumber;
127
+ nearestGap: z.ZodNullable<z.ZodNumber>;
128
+ }, z.core.$strip>>;
129
+ }, z.core.$strip>;
130
+ surfaces: z.ZodArray<z.ZodString>;
131
+ motions: z.ZodNumber;
132
+ lights: z.ZodNumber;
133
+ }, z.core.$strip>>;
134
+ };
135
+ export declare function registerValidateDesign(server: McpServer, _bridge: SceneOperations): void;
136
+ //# sourceMappingURL=validate-design.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-design.d.ts","sourceRoot":"","sources":["../../src/tools/validate-design.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;AAGpD,eAAO,MAAM,mBAAmB;;;CAU/B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAA+B,CAAA;AAEhE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAmBxF"}
@@ -0,0 +1,28 @@
1
+ import { DesignValidationSchema, validateDesign } from '@pascal-app/core/procedural-items';
2
+ import { z } from 'zod';
3
+ import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
4
+ export const validateDesignInput = {
5
+ design: z
6
+ .union([z.string(), z.record(z.string(), z.unknown())])
7
+ .describe('The design (procedural recipe, version 1 or 2) as an object, or as a JSON string for clients that cannot send recursive objects. Schema: pascal://schema/design.'),
8
+ parameters: z
9
+ .record(z.string(), z.number())
10
+ .optional()
11
+ .describe('Parameter values to measure at, by parameter id. Defaults otherwise.'),
12
+ };
13
+ export const validateDesignOutput = DesignValidationSchema.shape;
14
+ export function registerValidateDesign(server, _bridge) {
15
+ server.registerTool('validate_design', {
16
+ title: 'Validate design',
17
+ description: 'Check a design (procedural item recipe) without changing the scene. Returns `valid`, coded `diagnostics` with paths (errors block placement, warnings flag floating or unbalanced parts), the parameter `sweep`, and `measurements` at the given parameters: bounds, per-part bounds and instances, true triangle counts, draw groups per slot, datum contact and connected components. It is the authority: some rules are not expressible in pascal://schema/design. Repair and re-validate until `valid` is true. Version 2 designs validate fully but carry a design_version_not_enabled warning: placement accepts version 1 only until the next release.',
18
+ inputSchema: validateDesignInput,
19
+ outputSchema: validateDesignOutput,
20
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
21
+ }, async ({ design, parameters }) => {
22
+ const result = validateDesign(design, { parameters });
23
+ return {
24
+ content: [{ type: 'text', text: JSON.stringify(result) }],
25
+ structuredContent: result,
26
+ };
27
+ });
28
+ }