@genart-dev/mcp-server 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -67,6 +67,10 @@ var EditorState = class extends import_events.EventEmitter {
67
67
  * instead of writing to disk. Set by mcp-host for HTTP-based sessions.
68
68
  */
69
69
  remoteMode = false;
70
+ /** Plugin registry for design mode. Set during server initialization. */
71
+ pluginRegistry = null;
72
+ /** Layer stacks keyed by sketch ID. Created lazily when design tools are used. */
73
+ layerStacks = /* @__PURE__ */ new Map();
70
74
  constructor(options) {
71
75
  super();
72
76
  if (options?.basePath) {
@@ -131,6 +135,7 @@ var EditorState = class extends import_events.EventEmitter {
131
135
  this.workspace = ws;
132
136
  this.sketches.clear();
133
137
  this.selection.clear();
138
+ this.layerStacks.clear();
134
139
  for (const ref of ws.sketches) {
135
140
  const sketchPath = this.resolveSketchPath(ref.file);
136
141
  await this.loadSketch(sketchPath);
@@ -172,6 +177,7 @@ var EditorState = class extends import_events.EventEmitter {
172
177
  removeSketch(id) {
173
178
  this.sketches.delete(id);
174
179
  this.selection.delete(id);
180
+ this.layerStacks.delete(id);
175
181
  this.emitMutation("sketch:removed", { id });
176
182
  }
177
183
  /** Save the active workspace to disk. */
@@ -215,6 +221,81 @@ var EditorState = class extends import_events.EventEmitter {
215
221
  selection: Array.from(this.selection)
216
222
  };
217
223
  }
224
+ /**
225
+ * Get or create a LayerStackAccessor for a sketch.
226
+ * Initializes from the sketch's persisted design layers.
227
+ */
228
+ getLayerStack(sketchId) {
229
+ let stack = this.layerStacks.get(sketchId);
230
+ if (stack) return stack;
231
+ const loaded = this.requireSketch(sketchId);
232
+ const initialLayers = loaded.definition.layers ?? [];
233
+ stack = (0, import_core.createLayerStack)(initialLayers, (changeType) => {
234
+ this.syncLayersToDefinition(sketchId);
235
+ const mutationType = `design:${changeType}`;
236
+ this.emitMutation(mutationType, { sketchId, changeType });
237
+ });
238
+ this.layerStacks.set(sketchId, stack);
239
+ return stack;
240
+ }
241
+ /**
242
+ * Sync the layer stack's current state back to the sketch definition.
243
+ * Called automatically on every layer mutation.
244
+ */
245
+ syncLayersToDefinition(sketchId) {
246
+ const loaded = this.sketches.get(sketchId);
247
+ const stack = this.layerStacks.get(sketchId);
248
+ if (!loaded || !stack) return;
249
+ const layers = stack.getAll();
250
+ loaded.definition = {
251
+ ...loaded.definition,
252
+ layers: layers.length > 0 ? layers : void 0
253
+ };
254
+ }
255
+ /**
256
+ * Create an McpToolContext for a plugin's MCP tool handler.
257
+ * Provides access to the layer stack, sketch state, and change notifications.
258
+ */
259
+ createMcpToolContext(sketchId) {
260
+ const loaded = this.requireSketch(sketchId);
261
+ const layerStack = this.getLayerStack(sketchId);
262
+ const def = loaded.definition;
263
+ const sketchState = {
264
+ seed: def.state.seed,
265
+ params: def.state.params,
266
+ colorPalette: def.state.colorPalette,
267
+ canvasWidth: def.canvas.width,
268
+ canvasHeight: def.canvas.height,
269
+ rendererId: def.renderer.type
270
+ };
271
+ return {
272
+ layers: layerStack,
273
+ sketchState,
274
+ canvasWidth: def.canvas.width,
275
+ canvasHeight: def.canvas.height,
276
+ async resolveAsset(_assetId) {
277
+ return null;
278
+ },
279
+ async captureComposite(_format) {
280
+ throw new Error("captureComposite is not available in headless MCP mode");
281
+ },
282
+ emitChange(_changeType) {
283
+ }
284
+ };
285
+ }
286
+ /**
287
+ * Get the currently selected sketch ID for design operations.
288
+ * Returns the single selected sketch, or throws if none/multiple selected.
289
+ */
290
+ requireSelectedSketchId() {
291
+ if (this.selection.size === 0) {
292
+ throw new Error("No sketch is selected. Use select_sketch or open_sketch first.");
293
+ }
294
+ if (this.selection.size > 1) {
295
+ throw new Error("Multiple sketches are selected. Design operations require a single sketch.");
296
+ }
297
+ return this.selection.values().next().value;
298
+ }
218
299
  /** Emit a mutation event for external listeners (WebSocket broadcast, sidecar IPC). */
219
300
  emitMutation(type, payload) {
220
301
  this.emit("mutation", { type, payload });
@@ -225,6 +306,11 @@ var EditorState = class extends import_events.EventEmitter {
225
306
  // src/server.ts
226
307
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
227
308
  var import_zod2 = require("zod");
309
+ var import_core12 = require("@genart-dev/core");
310
+ var import_plugin_typography = __toESM(require("@genart-dev/plugin-typography"), 1);
311
+ var import_plugin_filters = __toESM(require("@genart-dev/plugin-filters"), 1);
312
+ var import_plugin_shapes = __toESM(require("@genart-dev/plugin-shapes"), 1);
313
+ var import_plugin_layout_guides = __toESM(require("@genart-dev/plugin-layout-guides"), 1);
228
314
 
229
315
  // src/tools/workspace.ts
230
316
  var import_promises3 = require("fs/promises");
@@ -2877,6 +2963,330 @@ async function exportZip(sketch, input) {
2877
2963
  };
2878
2964
  }
2879
2965
 
2966
+ // src/tools/design.ts
2967
+ function requireSketchId(state, args) {
2968
+ return args.sketchId ?? state.requireSelectedSketchId();
2969
+ }
2970
+ var BLEND_MODES = [
2971
+ "normal",
2972
+ "multiply",
2973
+ "screen",
2974
+ "overlay",
2975
+ "darken",
2976
+ "lighten",
2977
+ "color-dodge",
2978
+ "color-burn",
2979
+ "hard-light",
2980
+ "soft-light",
2981
+ "difference",
2982
+ "exclusion",
2983
+ "hue",
2984
+ "saturation",
2985
+ "color",
2986
+ "luminosity"
2987
+ ];
2988
+ function generateLayerId() {
2989
+ return `layer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
2990
+ }
2991
+ async function designAddLayer(state, args) {
2992
+ const sketchId = requireSketchId(state, args);
2993
+ const loaded = state.requireSketch(sketchId);
2994
+ const stack = state.getLayerStack(sketchId);
2995
+ const registry4 = state.pluginRegistry;
2996
+ const layerTypeDef = registry4?.resolveLayerType(args.type);
2997
+ if (!layerTypeDef) {
2998
+ throw new Error(
2999
+ `Unknown layer type: '${args.type}'. Use design_list_layers types from registered plugins.`
3000
+ );
3001
+ }
3002
+ const defaults = layerTypeDef.createDefault();
3003
+ const id = generateLayerId();
3004
+ const { width, height } = loaded.definition.canvas;
3005
+ const layer = {
3006
+ id,
3007
+ type: args.type,
3008
+ name: args.name ?? layerTypeDef.displayName,
3009
+ visible: true,
3010
+ locked: false,
3011
+ opacity: args.opacity ?? 1,
3012
+ blendMode: args.blendMode ?? "normal",
3013
+ transform: {
3014
+ x: 0,
3015
+ y: 0,
3016
+ width,
3017
+ height,
3018
+ rotation: 0,
3019
+ scaleX: 1,
3020
+ scaleY: 1,
3021
+ anchorX: 0.5,
3022
+ anchorY: 0.5,
3023
+ ...args.transform
3024
+ },
3025
+ properties: { ...defaults, ...args.properties }
3026
+ };
3027
+ stack.add(layer, args.index);
3028
+ await state.saveSketch(sketchId);
3029
+ return {
3030
+ layerId: id,
3031
+ type: args.type,
3032
+ name: layer.name,
3033
+ index: args.index ?? stack.count - 1,
3034
+ sketchId
3035
+ };
3036
+ }
3037
+ async function designRemoveLayer(state, args) {
3038
+ const sketchId = requireSketchId(state, args);
3039
+ const stack = state.getLayerStack(sketchId);
3040
+ const removed = stack.remove(args.layerId);
3041
+ if (!removed) {
3042
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3043
+ }
3044
+ await state.saveSketch(sketchId);
3045
+ return { removed: true, layerId: args.layerId, sketchId };
3046
+ }
3047
+ async function designListLayers(state, args) {
3048
+ const sketchId = requireSketchId(state, args);
3049
+ const stack = state.getLayerStack(sketchId);
3050
+ const layers = stack.getAll();
3051
+ return {
3052
+ sketchId,
3053
+ count: layers.length,
3054
+ layers: layers.map((l, i) => ({
3055
+ index: i,
3056
+ id: l.id,
3057
+ type: l.type,
3058
+ name: l.name,
3059
+ visible: l.visible,
3060
+ locked: l.locked,
3061
+ opacity: l.opacity,
3062
+ blendMode: l.blendMode
3063
+ }))
3064
+ };
3065
+ }
3066
+ async function designGetLayer(state, args) {
3067
+ const sketchId = requireSketchId(state, args);
3068
+ const stack = state.getLayerStack(sketchId);
3069
+ const layer = stack.get(args.layerId);
3070
+ if (!layer) {
3071
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3072
+ }
3073
+ return {
3074
+ sketchId,
3075
+ layer: {
3076
+ id: layer.id,
3077
+ type: layer.type,
3078
+ name: layer.name,
3079
+ visible: layer.visible,
3080
+ locked: layer.locked,
3081
+ opacity: layer.opacity,
3082
+ blendMode: layer.blendMode,
3083
+ transform: layer.transform,
3084
+ properties: layer.properties
3085
+ }
3086
+ };
3087
+ }
3088
+ async function designUpdateLayer(state, args) {
3089
+ const sketchId = requireSketchId(state, args);
3090
+ const stack = state.getLayerStack(sketchId);
3091
+ const layer = stack.get(args.layerId);
3092
+ if (!layer) {
3093
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3094
+ }
3095
+ const updates = {};
3096
+ if (args.properties) {
3097
+ Object.assign(updates, args.properties);
3098
+ }
3099
+ if (Object.keys(updates).length > 0) {
3100
+ stack.updateProperties(args.layerId, updates);
3101
+ }
3102
+ if (args.name !== void 0) {
3103
+ const current = stack.get(args.layerId);
3104
+ stack.updateProperties(args.layerId, { ...current.properties });
3105
+ const mutableLayer = stack.get(args.layerId);
3106
+ mutableLayer.name = args.name;
3107
+ }
3108
+ await state.saveSketch(sketchId);
3109
+ return { updated: true, layerId: args.layerId, sketchId };
3110
+ }
3111
+ async function designSetTransform(state, args) {
3112
+ const sketchId = requireSketchId(state, args);
3113
+ const stack = state.getLayerStack(sketchId);
3114
+ const layer = stack.get(args.layerId);
3115
+ if (!layer) {
3116
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3117
+ }
3118
+ const partial = {};
3119
+ if (args.x !== void 0) partial.x = args.x;
3120
+ if (args.y !== void 0) partial.y = args.y;
3121
+ if (args.width !== void 0) partial.width = args.width;
3122
+ if (args.height !== void 0) partial.height = args.height;
3123
+ if (args.rotation !== void 0) partial.rotation = args.rotation;
3124
+ if (args.scaleX !== void 0) partial.scaleX = args.scaleX;
3125
+ if (args.scaleY !== void 0) partial.scaleY = args.scaleY;
3126
+ if (args.anchorX !== void 0) partial.anchorX = args.anchorX;
3127
+ if (args.anchorY !== void 0) partial.anchorY = args.anchorY;
3128
+ stack.updateTransform(args.layerId, partial);
3129
+ await state.saveSketch(sketchId);
3130
+ return {
3131
+ updated: true,
3132
+ layerId: args.layerId,
3133
+ transform: stack.get(args.layerId).transform,
3134
+ sketchId
3135
+ };
3136
+ }
3137
+ async function designSetBlend(state, args) {
3138
+ const sketchId = requireSketchId(state, args);
3139
+ const stack = state.getLayerStack(sketchId);
3140
+ const layer = stack.get(args.layerId);
3141
+ if (!layer) {
3142
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3143
+ }
3144
+ if (args.blendMode && !BLEND_MODES.includes(args.blendMode)) {
3145
+ throw new Error(
3146
+ `Invalid blend mode '${args.blendMode}'. Must be one of: ${BLEND_MODES.join(", ")}`
3147
+ );
3148
+ }
3149
+ stack.updateBlend(
3150
+ args.layerId,
3151
+ args.blendMode,
3152
+ args.opacity
3153
+ );
3154
+ await state.saveSketch(sketchId);
3155
+ const updated = stack.get(args.layerId);
3156
+ return {
3157
+ updated: true,
3158
+ layerId: args.layerId,
3159
+ blendMode: updated.blendMode,
3160
+ opacity: updated.opacity,
3161
+ sketchId
3162
+ };
3163
+ }
3164
+ async function designReorderLayers(state, args) {
3165
+ const sketchId = requireSketchId(state, args);
3166
+ const stack = state.getLayerStack(sketchId);
3167
+ const layer = stack.get(args.layerId);
3168
+ if (!layer) {
3169
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3170
+ }
3171
+ stack.reorder(args.layerId, args.newIndex);
3172
+ await state.saveSketch(sketchId);
3173
+ return {
3174
+ reordered: true,
3175
+ layerId: args.layerId,
3176
+ newIndex: args.newIndex,
3177
+ sketchId
3178
+ };
3179
+ }
3180
+ async function designDuplicateLayer(state, args) {
3181
+ const sketchId = requireSketchId(state, args);
3182
+ const stack = state.getLayerStack(sketchId);
3183
+ const layer = stack.get(args.layerId);
3184
+ if (!layer) {
3185
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3186
+ }
3187
+ const newId = stack.duplicate(args.layerId);
3188
+ await state.saveSketch(sketchId);
3189
+ return {
3190
+ duplicated: true,
3191
+ sourceLayerId: args.layerId,
3192
+ newLayerId: newId,
3193
+ sketchId
3194
+ };
3195
+ }
3196
+ async function designToggleVisibility(state, args) {
3197
+ const sketchId = requireSketchId(state, args);
3198
+ const stack = state.getLayerStack(sketchId);
3199
+ const layer = stack.get(args.layerId);
3200
+ if (!layer) {
3201
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3202
+ }
3203
+ const newVisible = args.visible ?? !layer.visible;
3204
+ const mutableLayer = layer;
3205
+ mutableLayer.visible = newVisible;
3206
+ stack.updateProperties(args.layerId, { ...layer.properties });
3207
+ await state.saveSketch(sketchId);
3208
+ return {
3209
+ layerId: args.layerId,
3210
+ visible: newVisible,
3211
+ sketchId
3212
+ };
3213
+ }
3214
+ async function designLockLayer(state, args) {
3215
+ const sketchId = requireSketchId(state, args);
3216
+ const stack = state.getLayerStack(sketchId);
3217
+ const layer = stack.get(args.layerId);
3218
+ if (!layer) {
3219
+ throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
3220
+ }
3221
+ const newLocked = args.locked ?? !layer.locked;
3222
+ const mutableLayer = layer;
3223
+ mutableLayer.locked = newLocked;
3224
+ stack.updateProperties(args.layerId, { ...layer.properties });
3225
+ await state.saveSketch(sketchId);
3226
+ return {
3227
+ layerId: args.layerId,
3228
+ locked: newLocked,
3229
+ sketchId
3230
+ };
3231
+ }
3232
+ async function designCaptureComposite(state, args) {
3233
+ const sketchId = requireSketchId(state, args);
3234
+ const stack = state.getLayerStack(sketchId);
3235
+ const layers = stack.getAll();
3236
+ return {
3237
+ sketchId,
3238
+ layerCount: layers.length,
3239
+ visibleCount: layers.filter((l) => l.visible).length,
3240
+ message: "Composite capture requires a rendering surface. Use capture_screenshot to get a rasterized preview of the sketch, then use design_list_layers to see the design layer stack."
3241
+ };
3242
+ }
3243
+
3244
+ // src/tools/design-plugins.ts
3245
+ function registerPluginMcpTools(server, registry4, state) {
3246
+ for (const tool of registry4.getMcpTools()) {
3247
+ const inputSchema = tool.definition.inputSchema;
3248
+ server.tool(
3249
+ tool.name,
3250
+ tool.definition.description,
3251
+ // Pass raw JSON schema — MCP SDK accepts this alongside Zod
3252
+ inputSchema,
3253
+ async (args) => {
3254
+ try {
3255
+ const sketchId = args.sketchId ?? state.requireSelectedSketchId();
3256
+ const context = state.createMcpToolContext(sketchId);
3257
+ const result = await tool.definition.handler(args, context);
3258
+ await state.saveSketch(sketchId);
3259
+ return {
3260
+ content: result.content.map((c) => {
3261
+ if (c.type === "text") {
3262
+ return { type: "text", text: c.text };
3263
+ }
3264
+ return {
3265
+ type: "image",
3266
+ data: c.data,
3267
+ mimeType: c.mimeType
3268
+ };
3269
+ }),
3270
+ isError: result.isError
3271
+ };
3272
+ } catch (e) {
3273
+ return {
3274
+ content: [
3275
+ {
3276
+ type: "text",
3277
+ text: JSON.stringify({
3278
+ error: e instanceof Error ? e.message : String(e)
3279
+ })
3280
+ }
3281
+ ],
3282
+ isError: true
3283
+ };
3284
+ }
3285
+ }
3286
+ );
3287
+ }
3288
+ }
3289
+
2880
3290
  // src/resources/index.ts
2881
3291
  var import_core11 = require("@genart-dev/core");
2882
3292
  function registerResources(server, state) {
@@ -3360,11 +3770,23 @@ function toolError(message) {
3360
3770
  isError: true
3361
3771
  };
3362
3772
  }
3773
+ async function initializePluginRegistry() {
3774
+ const registry4 = (0, import_core12.createPluginRegistry)({
3775
+ surface: "mcp",
3776
+ supportsInteractiveTools: false,
3777
+ supportsRendering: false
3778
+ });
3779
+ await registry4.register(import_plugin_typography.default);
3780
+ await registry4.register(import_plugin_filters.default);
3781
+ await registry4.register(import_plugin_shapes.default);
3782
+ await registry4.register(import_plugin_layout_guides.default);
3783
+ return registry4;
3784
+ }
3363
3785
  function createServer(state) {
3364
3786
  const server = new import_mcp.McpServer(
3365
3787
  {
3366
3788
  name: "@genart/mcp-server",
3367
- version: "0.0.1"
3789
+ version: "0.3.0"
3368
3790
  },
3369
3791
  {
3370
3792
  capabilities: {
@@ -3374,6 +3796,11 @@ function createServer(state) {
3374
3796
  }
3375
3797
  }
3376
3798
  );
3799
+ const registryReady = initializePluginRegistry().then((registry4) => {
3800
+ state.pluginRegistry = registry4;
3801
+ registerPluginMcpTools(server, registry4, state);
3802
+ });
3803
+ server._pluginsReady = registryReady;
3377
3804
  registerWorkspaceTools(server, state);
3378
3805
  registerSketchTools(server, state);
3379
3806
  registerComponentTools(server, state);
@@ -3384,6 +3811,7 @@ function createServer(state) {
3384
3811
  registerMergeTools(server, state);
3385
3812
  registerSnapshotTools(server, state);
3386
3813
  registerKnowledgeTools(server, state);
3814
+ registerDesignTools(server, state);
3387
3815
  registerCaptureTools(server, state);
3388
3816
  registerExportTools(server, state);
3389
3817
  registerResources(server, state);
@@ -4172,6 +4600,247 @@ function registerExportTools(server, state) {
4172
4600
  }
4173
4601
  );
4174
4602
  }
4603
+ function registerDesignTools(server, state) {
4604
+ server.tool(
4605
+ "design_add_layer",
4606
+ "Add a new design layer of a given type to the active sketch. Layer types come from registered plugins (e.g. 'typography:text', 'filter:grain', 'shapes:rect', 'guides:thirds').",
4607
+ {
4608
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4609
+ type: import_zod2.z.string().describe("Layer type ID (e.g. 'typography:text', 'filter:grain', 'shapes:rect')"),
4610
+ name: import_zod2.z.string().optional().describe("Layer display name (default: type's display name)"),
4611
+ properties: import_zod2.z.record(import_zod2.z.unknown()).optional().describe("Initial layer properties (merged with type defaults)"),
4612
+ transform: import_zod2.z.object({
4613
+ x: import_zod2.z.number().optional(),
4614
+ y: import_zod2.z.number().optional(),
4615
+ width: import_zod2.z.number().optional(),
4616
+ height: import_zod2.z.number().optional(),
4617
+ rotation: import_zod2.z.number().optional(),
4618
+ scaleX: import_zod2.z.number().optional(),
4619
+ scaleY: import_zod2.z.number().optional(),
4620
+ anchorX: import_zod2.z.number().optional(),
4621
+ anchorY: import_zod2.z.number().optional()
4622
+ }).optional().describe("Layer transform (default: full canvas)"),
4623
+ opacity: import_zod2.z.number().optional().describe("Layer opacity 0\u20131 (default: 1)"),
4624
+ blendMode: import_zod2.z.string().optional().describe("Blend mode (default: 'normal')"),
4625
+ index: import_zod2.z.number().optional().describe("Insert position in layer stack (default: top)")
4626
+ },
4627
+ async (args) => {
4628
+ try {
4629
+ const result = await designAddLayer(state, args);
4630
+ return jsonResult(result);
4631
+ } catch (e) {
4632
+ return toolError(e instanceof Error ? e.message : String(e));
4633
+ }
4634
+ }
4635
+ );
4636
+ server.tool(
4637
+ "design_remove_layer",
4638
+ "Remove a design layer from the active sketch",
4639
+ {
4640
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4641
+ layerId: import_zod2.z.string().describe("ID of the layer to remove")
4642
+ },
4643
+ async (args) => {
4644
+ try {
4645
+ const result = await designRemoveLayer(state, args);
4646
+ return jsonResult(result);
4647
+ } catch (e) {
4648
+ return toolError(e instanceof Error ? e.message : String(e));
4649
+ }
4650
+ }
4651
+ );
4652
+ server.tool(
4653
+ "design_list_layers",
4654
+ "List all design layers in the active sketch with their types, visibility, and key properties",
4655
+ {
4656
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)")
4657
+ },
4658
+ async (args) => {
4659
+ try {
4660
+ const result = await designListLayers(state, args);
4661
+ return jsonResult(result);
4662
+ } catch (e) {
4663
+ return toolError(e instanceof Error ? e.message : String(e));
4664
+ }
4665
+ }
4666
+ );
4667
+ server.tool(
4668
+ "design_get_layer",
4669
+ "Get full details of a single design layer including all properties and transform",
4670
+ {
4671
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4672
+ layerId: import_zod2.z.string().describe("ID of the layer to inspect")
4673
+ },
4674
+ async (args) => {
4675
+ try {
4676
+ const result = await designGetLayer(state, args);
4677
+ return jsonResult(result);
4678
+ } catch (e) {
4679
+ return toolError(e instanceof Error ? e.message : String(e));
4680
+ }
4681
+ }
4682
+ );
4683
+ server.tool(
4684
+ "design_update_layer",
4685
+ "Update properties on a design layer (e.g. text content, filter intensity, shape fill color)",
4686
+ {
4687
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4688
+ layerId: import_zod2.z.string().describe("ID of the layer to update"),
4689
+ name: import_zod2.z.string().optional().describe("New display name"),
4690
+ properties: import_zod2.z.record(import_zod2.z.unknown()).optional().describe("Property key-value pairs to set")
4691
+ },
4692
+ async (args) => {
4693
+ try {
4694
+ const result = await designUpdateLayer(state, args);
4695
+ return jsonResult(result);
4696
+ } catch (e) {
4697
+ return toolError(e instanceof Error ? e.message : String(e));
4698
+ }
4699
+ }
4700
+ );
4701
+ server.tool(
4702
+ "design_set_transform",
4703
+ "Set the position, size, rotation, and scale of a design layer",
4704
+ {
4705
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4706
+ layerId: import_zod2.z.string().describe("ID of the layer to transform"),
4707
+ x: import_zod2.z.number().optional().describe("X position"),
4708
+ y: import_zod2.z.number().optional().describe("Y position"),
4709
+ width: import_zod2.z.number().optional().describe("Width"),
4710
+ height: import_zod2.z.number().optional().describe("Height"),
4711
+ rotation: import_zod2.z.number().optional().describe("Rotation in degrees"),
4712
+ scaleX: import_zod2.z.number().optional().describe("Horizontal scale"),
4713
+ scaleY: import_zod2.z.number().optional().describe("Vertical scale"),
4714
+ anchorX: import_zod2.z.number().optional().describe("Anchor X (0\u20131)"),
4715
+ anchorY: import_zod2.z.number().optional().describe("Anchor Y (0\u20131)")
4716
+ },
4717
+ async (args) => {
4718
+ try {
4719
+ const result = await designSetTransform(state, args);
4720
+ return jsonResult(result);
4721
+ } catch (e) {
4722
+ return toolError(e instanceof Error ? e.message : String(e));
4723
+ }
4724
+ }
4725
+ );
4726
+ server.tool(
4727
+ "design_set_blend",
4728
+ "Set blend mode and/or opacity on a design layer",
4729
+ {
4730
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4731
+ layerId: import_zod2.z.string().describe("ID of the layer"),
4732
+ blendMode: import_zod2.z.enum([
4733
+ "normal",
4734
+ "multiply",
4735
+ "screen",
4736
+ "overlay",
4737
+ "darken",
4738
+ "lighten",
4739
+ "color-dodge",
4740
+ "color-burn",
4741
+ "hard-light",
4742
+ "soft-light",
4743
+ "difference",
4744
+ "exclusion",
4745
+ "hue",
4746
+ "saturation",
4747
+ "color",
4748
+ "luminosity"
4749
+ ]).optional().describe("CSS blend mode"),
4750
+ opacity: import_zod2.z.number().optional().describe("Layer opacity 0\u20131")
4751
+ },
4752
+ async (args) => {
4753
+ try {
4754
+ const result = await designSetBlend(state, args);
4755
+ return jsonResult(result);
4756
+ } catch (e) {
4757
+ return toolError(e instanceof Error ? e.message : String(e));
4758
+ }
4759
+ }
4760
+ );
4761
+ server.tool(
4762
+ "design_reorder_layers",
4763
+ "Move a design layer to a new position in the z-order stack",
4764
+ {
4765
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4766
+ layerId: import_zod2.z.string().describe("ID of the layer to move"),
4767
+ newIndex: import_zod2.z.number().describe("New position (0 = bottom, n-1 = top)")
4768
+ },
4769
+ async (args) => {
4770
+ try {
4771
+ const result = await designReorderLayers(state, args);
4772
+ return jsonResult(result);
4773
+ } catch (e) {
4774
+ return toolError(e instanceof Error ? e.message : String(e));
4775
+ }
4776
+ }
4777
+ );
4778
+ server.tool(
4779
+ "design_duplicate_layer",
4780
+ "Clone a design layer with a new ID, inserted directly above the source",
4781
+ {
4782
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4783
+ layerId: import_zod2.z.string().describe("ID of the layer to duplicate")
4784
+ },
4785
+ async (args) => {
4786
+ try {
4787
+ const result = await designDuplicateLayer(state, args);
4788
+ return jsonResult(result);
4789
+ } catch (e) {
4790
+ return toolError(e instanceof Error ? e.message : String(e));
4791
+ }
4792
+ }
4793
+ );
4794
+ server.tool(
4795
+ "design_toggle_visibility",
4796
+ "Show or hide a design layer",
4797
+ {
4798
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4799
+ layerId: import_zod2.z.string().describe("ID of the layer"),
4800
+ visible: import_zod2.z.boolean().optional().describe("Set visibility (default: toggle)")
4801
+ },
4802
+ async (args) => {
4803
+ try {
4804
+ const result = await designToggleVisibility(state, args);
4805
+ return jsonResult(result);
4806
+ } catch (e) {
4807
+ return toolError(e instanceof Error ? e.message : String(e));
4808
+ }
4809
+ }
4810
+ );
4811
+ server.tool(
4812
+ "design_lock_layer",
4813
+ "Lock or unlock a design layer to prevent accidental edits",
4814
+ {
4815
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
4816
+ layerId: import_zod2.z.string().describe("ID of the layer"),
4817
+ locked: import_zod2.z.boolean().optional().describe("Set lock state (default: toggle)")
4818
+ },
4819
+ async (args) => {
4820
+ try {
4821
+ const result = await designLockLayer(state, args);
4822
+ return jsonResult(result);
4823
+ } catch (e) {
4824
+ return toolError(e instanceof Error ? e.message : String(e));
4825
+ }
4826
+ }
4827
+ );
4828
+ server.tool(
4829
+ "design_capture_composite",
4830
+ "Get info about the design layer composite for a sketch. For full visual capture use capture_screenshot.",
4831
+ {
4832
+ sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)")
4833
+ },
4834
+ async (args) => {
4835
+ try {
4836
+ const result = await designCaptureComposite(state, args);
4837
+ return jsonResult(result);
4838
+ } catch (e) {
4839
+ return toolError(e instanceof Error ? e.message : String(e));
4840
+ }
4841
+ }
4842
+ );
4843
+ }
4175
4844
  function registerKnowledgeTools(server, _state) {
4176
4845
  server.tool(
4177
4846
  "list_skills",