@genart-dev/mcp-server 0.1.1 → 0.2.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/lib.js CHANGED
@@ -345,6 +345,7 @@ import { writeFile as writeFile2, stat as stat2, unlink } from "fs/promises";
345
345
  import { basename as basename2, dirname as dirname2, resolve } from "path";
346
346
  import {
347
347
  createDefaultRegistry,
348
+ resolveComponents,
348
349
  resolvePreset,
349
350
  serializeGenart,
350
351
  serializeWorkspace as serializeWorkspace2
@@ -441,10 +442,40 @@ async function createSketch(state, input) {
441
442
  const adapter = registry4.resolve(rendererType);
442
443
  algorithm = adapter.getAlgorithmTemplate();
443
444
  }
445
+ let resolvedComponents;
446
+ if (input.components && Object.keys(input.components).length > 0) {
447
+ const shorthand = {};
448
+ for (const [name, value] of Object.entries(input.components)) {
449
+ if (typeof value === "string") {
450
+ shorthand[name] = value;
451
+ } else if (value.version) {
452
+ shorthand[name] = value.version;
453
+ } else if (value.code) {
454
+ if (!resolvedComponents) resolvedComponents = {};
455
+ resolvedComponents[name] = {
456
+ ...value.version ? { version: value.version } : {},
457
+ code: value.code,
458
+ ...value.exports ? { exports: value.exports } : {}
459
+ };
460
+ }
461
+ }
462
+ if (Object.keys(shorthand).length > 0) {
463
+ const resolved = resolveComponents(shorthand, rendererType);
464
+ if (!resolvedComponents) resolvedComponents = {};
465
+ for (const rc of resolved) {
466
+ resolvedComponents[rc.name] = {
467
+ version: rc.version,
468
+ code: rc.code,
469
+ exports: [...rc.exports]
470
+ };
471
+ }
472
+ }
473
+ }
444
474
  const seed = input.seed ?? Math.floor(Math.random() * 1e5);
445
475
  const ts = now2();
476
+ const hasComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
446
477
  const sketch = {
447
- genart: "1.1",
478
+ genart: hasComponents ? "1.2" : "1.1",
448
479
  id: input.id,
449
480
  title: input.title,
450
481
  created: ts,
@@ -458,6 +489,7 @@ async function createSketch(state, input) {
458
489
  ...input.philosophy ? { philosophy: input.philosophy } : {},
459
490
  ...input.themes && input.themes.length > 0 ? { themes: input.themes } : {},
460
491
  ...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
492
+ ...hasComponents ? { components: resolvedComponents } : {},
461
493
  ...input.agent ? { agent: input.agent } : {},
462
494
  ...input.model ? { model: input.model } : {}
463
495
  };
@@ -641,10 +673,44 @@ async function updateAlgorithm(state, input) {
641
673
  );
642
674
  }
643
675
  }
676
+ let resolvedComponents;
677
+ if (input.components && Object.keys(input.components).length > 0) {
678
+ const renderer = def.renderer.type;
679
+ const shorthand = {};
680
+ for (const [name, value] of Object.entries(input.components)) {
681
+ if (typeof value === "string") {
682
+ shorthand[name] = value;
683
+ } else if (value.version) {
684
+ shorthand[name] = value.version;
685
+ } else if (value.code) {
686
+ if (!resolvedComponents) resolvedComponents = {};
687
+ resolvedComponents[name] = {
688
+ ...value.version ? { version: value.version } : {},
689
+ code: value.code,
690
+ ...value.exports ? { exports: value.exports } : {}
691
+ };
692
+ }
693
+ }
694
+ if (Object.keys(shorthand).length > 0) {
695
+ const resolved = resolveComponents(shorthand, renderer);
696
+ if (!resolvedComponents) resolvedComponents = {};
697
+ for (const rc of resolved) {
698
+ resolvedComponents[rc.name] = {
699
+ version: rc.version,
700
+ code: rc.code,
701
+ exports: [...rc.exports]
702
+ };
703
+ }
704
+ }
705
+ }
706
+ const updated = ["algorithm"];
707
+ const hasNewComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
708
+ if (hasNewComponents) updated.push("components");
644
709
  const newDef = {
645
710
  ...def,
646
711
  modified: now2(),
647
712
  algorithm: input.algorithm,
713
+ ...hasNewComponents ? { genart: "1.2", components: resolvedComponents } : {},
648
714
  ...input.agent ? { agent: input.agent } : {},
649
715
  ...input.model ? { model: input.model } : {}
650
716
  };
@@ -658,7 +724,7 @@ async function updateAlgorithm(state, input) {
658
724
  }
659
725
  state.emitMutation("sketch:updated", {
660
726
  id: input.sketchId,
661
- updated: ["algorithm"]
727
+ updated
662
728
  });
663
729
  return {
664
730
  success: true,
@@ -666,6 +732,7 @@ async function updateAlgorithm(state, input) {
666
732
  renderer: def.renderer.type,
667
733
  algorithmLength: input.algorithm.length,
668
734
  validationPassed,
735
+ ...hasNewComponents ? { componentsUpdated: true } : {},
669
736
  fileContent: json
670
737
  };
671
738
  }
@@ -1967,8 +2034,219 @@ ${guidelines}`,
1967
2034
  };
1968
2035
  }
1969
2036
 
1970
- // src/tools/capture.ts
2037
+ // src/tools/components.ts
1971
2038
  import { writeFile as writeFile4 } from "fs/promises";
2039
+ import {
2040
+ COMPONENT_REGISTRY,
2041
+ resolveComponents as resolveComponents2,
2042
+ serializeGenart as serializeGenart3
2043
+ } from "@genart-dev/core";
2044
+ var VALID_RENDERERS2 = [
2045
+ "p5",
2046
+ "three",
2047
+ "glsl",
2048
+ "canvas2d",
2049
+ "svg"
2050
+ ];
2051
+ var RENDERER_TARGET = {
2052
+ p5: "js",
2053
+ three: "js",
2054
+ canvas2d: "js",
2055
+ svg: "js",
2056
+ glsl: "glsl"
2057
+ };
2058
+ async function listComponents(_state, input) {
2059
+ let entries = Object.values(COMPONENT_REGISTRY);
2060
+ if (input.renderer) {
2061
+ const renderer = input.renderer;
2062
+ const target = RENDERER_TARGET[renderer];
2063
+ if (!target) {
2064
+ throw new Error(
2065
+ `Unknown renderer type: '${input.renderer}'. Valid types: ${VALID_RENDERERS2.join(", ")}`
2066
+ );
2067
+ }
2068
+ entries = entries.filter(
2069
+ (e) => e.target === target && (e.renderers.length === 0 || e.renderers.includes(renderer))
2070
+ );
2071
+ }
2072
+ if (input.category) {
2073
+ const cat = input.category;
2074
+ entries = entries.filter((e) => e.category === cat);
2075
+ }
2076
+ entries.sort((a, b) => {
2077
+ const catCmp = a.category.localeCompare(b.category);
2078
+ if (catCmp !== 0) return catCmp;
2079
+ return a.name.localeCompare(b.name);
2080
+ });
2081
+ const components = entries.map((e) => ({
2082
+ name: e.name,
2083
+ version: e.version,
2084
+ category: e.category,
2085
+ target: e.target,
2086
+ exports: [...e.exports],
2087
+ dependencies: [...e.dependencies],
2088
+ description: e.description
2089
+ }));
2090
+ return {
2091
+ count: components.length,
2092
+ components
2093
+ };
2094
+ }
2095
+ async function addComponent(state, input) {
2096
+ const loaded = state.requireSketch(input.sketchId);
2097
+ const def = loaded.definition;
2098
+ const renderer = def.renderer.type;
2099
+ const entry = COMPONENT_REGISTRY[input.component];
2100
+ if (!entry) {
2101
+ throw new Error(`Unknown component: "${input.component}"`);
2102
+ }
2103
+ const target = RENDERER_TARGET[renderer];
2104
+ if (entry.target !== target) {
2105
+ throw new Error(
2106
+ `Component "${input.component}" has target "${entry.target}" but renderer "${renderer}" requires target "${target}"`
2107
+ );
2108
+ }
2109
+ if (entry.renderers.length > 0 && !entry.renderers.includes(renderer)) {
2110
+ throw new Error(
2111
+ `Component "${input.component}" is not compatible with renderer "${renderer}". Compatible: ${entry.renderers.join(", ")}`
2112
+ );
2113
+ }
2114
+ const existingComponents = {};
2115
+ if (def.components) {
2116
+ for (const [name, value] of Object.entries(def.components)) {
2117
+ if (typeof value === "string") {
2118
+ existingComponents[name] = value;
2119
+ } else if (value.version) {
2120
+ existingComponents[name] = value.version;
2121
+ }
2122
+ }
2123
+ }
2124
+ if (existingComponents[input.component]) {
2125
+ throw new Error(
2126
+ `Component "${input.component}" is already present in sketch "${input.sketchId}"`
2127
+ );
2128
+ }
2129
+ existingComponents[input.component] = input.version ?? "^1.0.0";
2130
+ const resolved = resolveComponents2(existingComponents, renderer);
2131
+ const resolvedRecord = {};
2132
+ for (const rc of resolved) {
2133
+ resolvedRecord[rc.name] = {
2134
+ version: rc.version,
2135
+ code: rc.code,
2136
+ exports: [...rc.exports]
2137
+ };
2138
+ }
2139
+ const previousNames = new Set(
2140
+ def.components ? Object.keys(def.components) : []
2141
+ );
2142
+ const added = resolved.map((rc) => rc.name).filter((name) => !previousNames.has(name));
2143
+ const newDef = {
2144
+ ...def,
2145
+ genart: "1.2",
2146
+ modified: (/* @__PURE__ */ new Date()).toISOString(),
2147
+ components: resolvedRecord
2148
+ };
2149
+ state.sketches.set(input.sketchId, {
2150
+ definition: newDef,
2151
+ path: loaded.path
2152
+ });
2153
+ const json = serializeGenart3(newDef);
2154
+ if (!state.remoteMode) {
2155
+ await writeFile4(loaded.path, json, "utf-8");
2156
+ }
2157
+ state.emitMutation("sketch:updated", {
2158
+ id: input.sketchId,
2159
+ updated: ["components"]
2160
+ });
2161
+ return {
2162
+ success: true,
2163
+ sketchId: input.sketchId,
2164
+ components: resolvedRecord,
2165
+ added,
2166
+ fileContent: json
2167
+ };
2168
+ }
2169
+ async function removeComponent(state, input) {
2170
+ const loaded = state.requireSketch(input.sketchId);
2171
+ const def = loaded.definition;
2172
+ if (!def.components || !def.components[input.component]) {
2173
+ throw new Error(
2174
+ `Component "${input.component}" is not present in sketch "${input.sketchId}"`
2175
+ );
2176
+ }
2177
+ const remaining = { ...def.components };
2178
+ delete remaining[input.component];
2179
+ for (const [name, value] of Object.entries(remaining)) {
2180
+ const entry = COMPONENT_REGISTRY[name];
2181
+ if (entry && entry.dependencies.includes(input.component)) {
2182
+ throw new Error(
2183
+ `Cannot remove "${input.component}": component "${name}" depends on it`
2184
+ );
2185
+ }
2186
+ }
2187
+ let warning;
2188
+ const removedValue = def.components[input.component];
2189
+ const exports = typeof removedValue === "string" ? COMPONENT_REGISTRY[input.component]?.exports ?? [] : removedValue.exports ?? [];
2190
+ const usedExports = exports.filter((exp) => def.algorithm.includes(exp));
2191
+ if (usedExports.length > 0) {
2192
+ warning = `Algorithm may reference these exports from "${input.component}": ${usedExports.join(", ")}. Review your algorithm after removal.`;
2193
+ }
2194
+ const removed = [input.component];
2195
+ const neededDeps = /* @__PURE__ */ new Set();
2196
+ for (const name of Object.keys(remaining)) {
2197
+ const entry = COMPONENT_REGISTRY[name];
2198
+ if (entry) {
2199
+ collectTransitiveDeps(name, neededDeps);
2200
+ }
2201
+ }
2202
+ for (const name of Object.keys(remaining)) {
2203
+ if (!neededDeps.has(name) && !isDirectComponent(name, remaining)) {
2204
+ delete remaining[name];
2205
+ removed.push(name);
2206
+ }
2207
+ }
2208
+ const hasRemaining = Object.keys(remaining).length > 0;
2209
+ const newDef = {
2210
+ ...def,
2211
+ modified: (/* @__PURE__ */ new Date()).toISOString(),
2212
+ ...hasRemaining ? { components: remaining } : { components: void 0 }
2213
+ };
2214
+ state.sketches.set(input.sketchId, {
2215
+ definition: newDef,
2216
+ path: loaded.path
2217
+ });
2218
+ const json = serializeGenart3(newDef);
2219
+ if (!state.remoteMode) {
2220
+ await writeFile4(loaded.path, json, "utf-8");
2221
+ }
2222
+ state.emitMutation("sketch:updated", {
2223
+ id: input.sketchId,
2224
+ updated: ["components"]
2225
+ });
2226
+ return {
2227
+ success: true,
2228
+ sketchId: input.sketchId,
2229
+ removed,
2230
+ ...warning ? { warning } : {},
2231
+ fileContent: json
2232
+ };
2233
+ }
2234
+ function collectTransitiveDeps(name, deps) {
2235
+ const entry = COMPONENT_REGISTRY[name];
2236
+ if (!entry) return;
2237
+ deps.add(name);
2238
+ for (const dep of entry.dependencies) {
2239
+ if (!deps.has(dep)) {
2240
+ collectTransitiveDeps(dep, deps);
2241
+ }
2242
+ }
2243
+ }
2244
+ function isDirectComponent(name, components) {
2245
+ return name in components;
2246
+ }
2247
+
2248
+ // src/tools/capture.ts
2249
+ import { writeFile as writeFile5 } from "fs/promises";
1972
2250
  import {
1973
2251
  createDefaultRegistry as createDefaultRegistry2
1974
2252
  } from "@genart-dev/core";
@@ -2146,7 +2424,7 @@ async function buildScreenshotMetadata(state, multi, info) {
2146
2424
  previewPath: info.previewPath
2147
2425
  };
2148
2426
  if (!state.remoteMode) {
2149
- await writeFile4(info.previewPath, multi.previewPng);
2427
+ await writeFile5(info.previewPath, multi.previewPng);
2150
2428
  metadata.savedPreviewTo = info.previewPath;
2151
2429
  }
2152
2430
  return metadata;
@@ -2207,12 +2485,12 @@ async function captureBatch(state, input) {
2207
2485
 
2208
2486
  // src/tools/export.ts
2209
2487
  import { createWriteStream } from "fs";
2210
- import { stat as stat4, writeFile as writeFile5 } from "fs/promises";
2488
+ import { stat as stat4, writeFile as writeFile6 } from "fs/promises";
2211
2489
  import { dirname as dirname5 } from "path";
2212
2490
  import archiver from "archiver";
2213
2491
  import {
2214
2492
  createDefaultRegistry as createDefaultRegistry3,
2215
- serializeGenart as serializeGenart3
2493
+ serializeGenart as serializeGenart4
2216
2494
  } from "@genart-dev/core";
2217
2495
  var registry3 = createDefaultRegistry3();
2218
2496
  async function validateOutputPath(outputPath) {
@@ -2282,7 +2560,7 @@ async function exportHtml(sketch, outputPath) {
2282
2560
  const adapter = registry3.resolve(sketch.renderer.type);
2283
2561
  const html = adapter.generateStandaloneHTML(sketch);
2284
2562
  const content = Buffer.from(html, "utf-8");
2285
- await writeFile5(outputPath, content);
2563
+ await writeFile6(outputPath, content);
2286
2564
  return {
2287
2565
  success: true,
2288
2566
  sketchId: sketch.id,
@@ -2298,7 +2576,7 @@ async function exportPng(sketch, input) {
2298
2576
  const width = input.width ?? sketch.canvas.width;
2299
2577
  const height = input.height ?? sketch.canvas.height;
2300
2578
  const result = await captureHtml({ html, width, height });
2301
- await writeFile5(input.outputPath, result.bytes);
2579
+ await writeFile6(input.outputPath, result.bytes);
2302
2580
  return {
2303
2581
  success: true,
2304
2582
  sketchId: sketch.id,
@@ -2313,7 +2591,7 @@ async function exportSvg(sketch, input) {
2313
2591
  const height = input.height ?? sketch.canvas.height;
2314
2592
  if (sketch.renderer.type === "svg") {
2315
2593
  const content2 = Buffer.from(sketch.algorithm, "utf-8");
2316
- await writeFile5(input.outputPath, content2);
2594
+ await writeFile6(input.outputPath, content2);
2317
2595
  return {
2318
2596
  success: true,
2319
2597
  sketchId: sketch.id,
@@ -2335,7 +2613,7 @@ async function exportSvg(sketch, input) {
2335
2613
  href="data:image/png;base64,${b64}"/>
2336
2614
  </svg>`;
2337
2615
  const content = Buffer.from(svg, "utf-8");
2338
- await writeFile5(input.outputPath, content);
2616
+ await writeFile6(input.outputPath, content);
2339
2617
  return {
2340
2618
  success: true,
2341
2619
  sketchId: sketch.id,
@@ -2348,7 +2626,7 @@ async function exportSvg(sketch, input) {
2348
2626
  }
2349
2627
  async function exportAlgorithm(sketch, outputPath) {
2350
2628
  const content = Buffer.from(sketch.algorithm, "utf-8");
2351
- await writeFile5(outputPath, content);
2629
+ await writeFile6(outputPath, content);
2352
2630
  return {
2353
2631
  success: true,
2354
2632
  sketchId: sketch.id,
@@ -2363,7 +2641,7 @@ async function exportZip(sketch, input) {
2363
2641
  const width = input.width ?? sketch.canvas.width;
2364
2642
  const height = input.height ?? sketch.canvas.height;
2365
2643
  const html = adapter.generateStandaloneHTML(sketch);
2366
- const genartJson = serializeGenart3(sketch);
2644
+ const genartJson = serializeGenart4(sketch);
2367
2645
  const algorithm = sketch.algorithm;
2368
2646
  const algExt = algorithmExtension(sketch.renderer.type);
2369
2647
  const captureResult = await captureHtml({ html, width, height });
@@ -2900,6 +3178,7 @@ function createServer(state) {
2900
3178
  );
2901
3179
  registerWorkspaceTools(server, state);
2902
3180
  registerSketchTools(server, state);
3181
+ registerComponentTools(server, state);
2903
3182
  registerSelectionTools(server, state);
2904
3183
  registerParameterTools(server, state);
2905
3184
  registerArrangementTools(server, state);
@@ -3003,7 +3282,7 @@ function registerWorkspaceTools(server, state) {
3003
3282
  function registerSketchTools(server, state) {
3004
3283
  server.tool(
3005
3284
  "create_sketch",
3006
- "Create a new .genart sketch file from metadata, parameters, and algorithm",
3285
+ 'Create a new .genart sketch file from metadata, parameters, and algorithm. IMPORTANT: Do not embed common utilities (PRNG, noise, easing, color math, vector ops) inline in the algorithm. Instead, declare them as components: { "prng": "^1.0.0", "noise-2d": "^1.0.0" }. Then use the exported functions directly in your algorithm (e.g., mulberry32, fbm2D). Use list_components to see all available components for the current renderer.',
3007
3286
  {
3008
3287
  id: z2.string().describe("URL-safe kebab-case identifier"),
3009
3288
  title: z2.string().describe("Human-readable title"),
@@ -3041,6 +3320,16 @@ function registerSketchTools(server, state) {
3041
3320
  algorithm: z2.string().optional().describe("Algorithm source code (default: renderer template). For p5: must be `function sketch(p, state) { ... }` in instance mode. State provides: state.WIDTH, state.HEIGHT, state.SEED (number), state.PARAMS (keyed by param key), state.COLORS (keyed by color key, hex strings). Use p5 instance methods (p.createCanvas, p.background, etc)."),
3042
3321
  seed: z2.number().optional().describe("Initial random seed (default: random)"),
3043
3322
  skills: z2.array(z2.string()).optional().describe("Design skill references"),
3323
+ components: z2.record(
3324
+ z2.union([
3325
+ z2.string(),
3326
+ z2.object({
3327
+ version: z2.string().optional(),
3328
+ code: z2.string().optional(),
3329
+ exports: z2.array(z2.string()).optional()
3330
+ })
3331
+ ])
3332
+ ).optional().describe('Component dependencies. Use list_components to see available. Keys are component names, values are semver ranges (e.g. "^1.0.0") or objects with version/code/exports.'),
3044
3333
  addToWorkspace: z2.string().optional().describe("Path to workspace to add sketch to after creation"),
3045
3334
  agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
3046
3335
  model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
@@ -3120,11 +3409,21 @@ function registerSketchTools(server, state) {
3120
3409
  );
3121
3410
  server.tool(
3122
3411
  "update_algorithm",
3123
- "Replace the algorithm source code of a sketch",
3412
+ "Replace the algorithm source code of a sketch. If adding/changing components, pass them in the components field alongside the algorithm.",
3124
3413
  {
3125
3414
  sketchId: z2.string().describe("ID of the sketch to update"),
3126
3415
  algorithm: z2.string().describe("New algorithm source code. For p5: must be `function sketch(p, state) { ... }` in instance mode. State provides: state.WIDTH, state.HEIGHT, state.SEED, state.PARAMS (keyed by param key), state.COLORS (keyed by color key)."),
3127
3416
  validate: z2.boolean().optional().describe("Run renderer-specific validation before saving (default: true)"),
3417
+ components: z2.record(
3418
+ z2.union([
3419
+ z2.string(),
3420
+ z2.object({
3421
+ version: z2.string().optional(),
3422
+ code: z2.string().optional(),
3423
+ exports: z2.array(z2.string()).optional()
3424
+ })
3425
+ ])
3426
+ ).optional().describe("Component dependencies to resolve alongside the algorithm update. Use list_components to see available."),
3128
3427
  agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
3129
3428
  model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
3130
3429
  },
@@ -3220,6 +3519,76 @@ function registerSketchTools(server, state) {
3220
3519
  }
3221
3520
  );
3222
3521
  }
3522
+ function registerComponentTools(server, state) {
3523
+ server.tool(
3524
+ "list_components",
3525
+ "List available reusable components from the registry, filtered by renderer and/or category. Components provide common utilities (PRNG, noise, easing, color math, etc.) that can be declared as dependencies instead of inlining code in the algorithm.",
3526
+ {
3527
+ renderer: z2.enum(["p5", "three", "glsl", "canvas2d", "svg"]).optional().describe("Filter by renderer compatibility"),
3528
+ category: z2.enum([
3529
+ "randomness",
3530
+ "noise",
3531
+ "math",
3532
+ "easing",
3533
+ "color",
3534
+ "vector",
3535
+ "geometry",
3536
+ "grid",
3537
+ "particle",
3538
+ "physics",
3539
+ "distribution",
3540
+ "pattern",
3541
+ "sdf",
3542
+ "transform",
3543
+ "animation",
3544
+ "string",
3545
+ "data-structure",
3546
+ "imaging"
3547
+ ]).optional().describe("Filter by component category")
3548
+ },
3549
+ async (args) => {
3550
+ try {
3551
+ const result = await listComponents(state, args);
3552
+ return jsonResult(result);
3553
+ } catch (e) {
3554
+ return toolError(e instanceof Error ? e.message : String(e));
3555
+ }
3556
+ }
3557
+ );
3558
+ server.tool(
3559
+ "add_component",
3560
+ "Add a component dependency to an existing sketch. Resolves the component and any transitive dependencies from the registry, validates renderer compatibility, and writes the resolved form to the sketch file.",
3561
+ {
3562
+ sketchId: z2.string().describe("ID of the sketch to add the component to"),
3563
+ component: z2.string().describe("Component name (e.g. 'prng', 'noise-2d', 'glsl-noise')"),
3564
+ version: z2.string().optional().describe("Version range (default: '^1.0.0')")
3565
+ },
3566
+ async (args) => {
3567
+ try {
3568
+ const result = await addComponent(state, args);
3569
+ return jsonResult(result);
3570
+ } catch (e) {
3571
+ return toolError(e instanceof Error ? e.message : String(e));
3572
+ }
3573
+ }
3574
+ );
3575
+ server.tool(
3576
+ "remove_component",
3577
+ "Remove a component dependency from a sketch. Checks for dependent components and warns if the algorithm references the component's exports.",
3578
+ {
3579
+ sketchId: z2.string().describe("ID of the sketch to remove the component from"),
3580
+ component: z2.string().describe("Component name to remove")
3581
+ },
3582
+ async (args) => {
3583
+ try {
3584
+ const result = await removeComponent(state, args);
3585
+ return jsonResult(result);
3586
+ } catch (e) {
3587
+ return toolError(e instanceof Error ? e.message : String(e));
3588
+ }
3589
+ }
3590
+ );
3591
+ }
3223
3592
  function registerSelectionTools(server, state) {
3224
3593
  server.tool(
3225
3594
  "get_selection",
@@ -3676,10 +4045,10 @@ function notifyMutation(type, payload) {
3676
4045
  import {
3677
4046
  parseGenart as parseGenart4,
3678
4047
  parseWorkspace as parseWorkspace2,
3679
- serializeGenart as serializeGenart4,
4048
+ serializeGenart as serializeGenart5,
3680
4049
  serializeWorkspace as serializeWorkspace3
3681
4050
  } from "@genart-dev/core";
3682
- import { writeFile as writeFile6 } from "fs/promises";
4051
+ import { writeFile as writeFile7 } from "fs/promises";
3683
4052
  var EditorState = class extends EventEmitter {
3684
4053
  /** Absolute path to the active .genart-workspace file, or null. */
3685
4054
  workspacePath = null;
@@ -3815,16 +4184,16 @@ var EditorState = class extends EventEmitter {
3815
4184
  }
3816
4185
  const json = serializeWorkspace3(this.workspace);
3817
4186
  if (!this.remoteMode) {
3818
- await writeFile6(this.workspacePath, json, "utf-8");
4187
+ await writeFile7(this.workspacePath, json, "utf-8");
3819
4188
  }
3820
4189
  this.emitMutation("workspace:saved", { path: this.workspacePath });
3821
4190
  }
3822
4191
  /** Save a sketch to disk. */
3823
4192
  async saveSketch(id) {
3824
4193
  const loaded = this.requireSketch(id);
3825
- const json = serializeGenart4(loaded.definition);
4194
+ const json = serializeGenart5(loaded.definition);
3826
4195
  if (!this.remoteMode) {
3827
- await writeFile6(loaded.path, json, "utf-8");
4196
+ await writeFile7(loaded.path, json, "utf-8");
3828
4197
  }
3829
4198
  this.emitMutation("sketch:saved", { id, path: loaded.path });
3830
4199
  }