@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/index.js CHANGED
@@ -551,6 +551,7 @@ import { writeFile as writeFile3, stat as stat2, unlink } from "fs/promises";
551
551
  import { basename as basename2, dirname as dirname3, resolve as resolve2 } from "path";
552
552
  import {
553
553
  createDefaultRegistry,
554
+ resolveComponents,
554
555
  resolvePreset,
555
556
  serializeGenart as serializeGenart2,
556
557
  serializeWorkspace as serializeWorkspace3
@@ -647,10 +648,40 @@ async function createSketch(state, input) {
647
648
  const adapter = registry4.resolve(rendererType);
648
649
  algorithm = adapter.getAlgorithmTemplate();
649
650
  }
651
+ let resolvedComponents;
652
+ if (input.components && Object.keys(input.components).length > 0) {
653
+ const shorthand = {};
654
+ for (const [name, value] of Object.entries(input.components)) {
655
+ if (typeof value === "string") {
656
+ shorthand[name] = value;
657
+ } else if (value.version) {
658
+ shorthand[name] = value.version;
659
+ } else if (value.code) {
660
+ if (!resolvedComponents) resolvedComponents = {};
661
+ resolvedComponents[name] = {
662
+ ...value.version ? { version: value.version } : {},
663
+ code: value.code,
664
+ ...value.exports ? { exports: value.exports } : {}
665
+ };
666
+ }
667
+ }
668
+ if (Object.keys(shorthand).length > 0) {
669
+ const resolved = resolveComponents(shorthand, rendererType);
670
+ if (!resolvedComponents) resolvedComponents = {};
671
+ for (const rc of resolved) {
672
+ resolvedComponents[rc.name] = {
673
+ version: rc.version,
674
+ code: rc.code,
675
+ exports: [...rc.exports]
676
+ };
677
+ }
678
+ }
679
+ }
650
680
  const seed = input.seed ?? Math.floor(Math.random() * 1e5);
651
681
  const ts = now2();
682
+ const hasComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
652
683
  const sketch = {
653
- genart: "1.1",
684
+ genart: hasComponents ? "1.2" : "1.1",
654
685
  id: input.id,
655
686
  title: input.title,
656
687
  created: ts,
@@ -664,6 +695,7 @@ async function createSketch(state, input) {
664
695
  ...input.philosophy ? { philosophy: input.philosophy } : {},
665
696
  ...input.themes && input.themes.length > 0 ? { themes: input.themes } : {},
666
697
  ...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
698
+ ...hasComponents ? { components: resolvedComponents } : {},
667
699
  ...input.agent ? { agent: input.agent } : {},
668
700
  ...input.model ? { model: input.model } : {}
669
701
  };
@@ -847,10 +879,44 @@ async function updateAlgorithm(state, input) {
847
879
  );
848
880
  }
849
881
  }
882
+ let resolvedComponents;
883
+ if (input.components && Object.keys(input.components).length > 0) {
884
+ const renderer = def.renderer.type;
885
+ const shorthand = {};
886
+ for (const [name, value] of Object.entries(input.components)) {
887
+ if (typeof value === "string") {
888
+ shorthand[name] = value;
889
+ } else if (value.version) {
890
+ shorthand[name] = value.version;
891
+ } else if (value.code) {
892
+ if (!resolvedComponents) resolvedComponents = {};
893
+ resolvedComponents[name] = {
894
+ ...value.version ? { version: value.version } : {},
895
+ code: value.code,
896
+ ...value.exports ? { exports: value.exports } : {}
897
+ };
898
+ }
899
+ }
900
+ if (Object.keys(shorthand).length > 0) {
901
+ const resolved = resolveComponents(shorthand, renderer);
902
+ if (!resolvedComponents) resolvedComponents = {};
903
+ for (const rc of resolved) {
904
+ resolvedComponents[rc.name] = {
905
+ version: rc.version,
906
+ code: rc.code,
907
+ exports: [...rc.exports]
908
+ };
909
+ }
910
+ }
911
+ }
912
+ const updated = ["algorithm"];
913
+ const hasNewComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
914
+ if (hasNewComponents) updated.push("components");
850
915
  const newDef = {
851
916
  ...def,
852
917
  modified: now2(),
853
918
  algorithm: input.algorithm,
919
+ ...hasNewComponents ? { genart: "1.2", components: resolvedComponents } : {},
854
920
  ...input.agent ? { agent: input.agent } : {},
855
921
  ...input.model ? { model: input.model } : {}
856
922
  };
@@ -864,7 +930,7 @@ async function updateAlgorithm(state, input) {
864
930
  }
865
931
  state.emitMutation("sketch:updated", {
866
932
  id: input.sketchId,
867
- updated: ["algorithm"]
933
+ updated
868
934
  });
869
935
  return {
870
936
  success: true,
@@ -872,6 +938,7 @@ async function updateAlgorithm(state, input) {
872
938
  renderer: def.renderer.type,
873
939
  algorithmLength: input.algorithm.length,
874
940
  validationPassed,
941
+ ...hasNewComponents ? { componentsUpdated: true } : {},
875
942
  fileContent: json
876
943
  };
877
944
  }
@@ -2173,8 +2240,219 @@ ${guidelines}`,
2173
2240
  };
2174
2241
  }
2175
2242
 
2176
- // src/tools/capture.ts
2243
+ // src/tools/components.ts
2177
2244
  import { writeFile as writeFile5 } from "fs/promises";
2245
+ import {
2246
+ COMPONENT_REGISTRY,
2247
+ resolveComponents as resolveComponents2,
2248
+ serializeGenart as serializeGenart4
2249
+ } from "@genart-dev/core";
2250
+ var VALID_RENDERERS2 = [
2251
+ "p5",
2252
+ "three",
2253
+ "glsl",
2254
+ "canvas2d",
2255
+ "svg"
2256
+ ];
2257
+ var RENDERER_TARGET = {
2258
+ p5: "js",
2259
+ three: "js",
2260
+ canvas2d: "js",
2261
+ svg: "js",
2262
+ glsl: "glsl"
2263
+ };
2264
+ async function listComponents(_state, input) {
2265
+ let entries = Object.values(COMPONENT_REGISTRY);
2266
+ if (input.renderer) {
2267
+ const renderer = input.renderer;
2268
+ const target = RENDERER_TARGET[renderer];
2269
+ if (!target) {
2270
+ throw new Error(
2271
+ `Unknown renderer type: '${input.renderer}'. Valid types: ${VALID_RENDERERS2.join(", ")}`
2272
+ );
2273
+ }
2274
+ entries = entries.filter(
2275
+ (e) => e.target === target && (e.renderers.length === 0 || e.renderers.includes(renderer))
2276
+ );
2277
+ }
2278
+ if (input.category) {
2279
+ const cat = input.category;
2280
+ entries = entries.filter((e) => e.category === cat);
2281
+ }
2282
+ entries.sort((a, b) => {
2283
+ const catCmp = a.category.localeCompare(b.category);
2284
+ if (catCmp !== 0) return catCmp;
2285
+ return a.name.localeCompare(b.name);
2286
+ });
2287
+ const components = entries.map((e) => ({
2288
+ name: e.name,
2289
+ version: e.version,
2290
+ category: e.category,
2291
+ target: e.target,
2292
+ exports: [...e.exports],
2293
+ dependencies: [...e.dependencies],
2294
+ description: e.description
2295
+ }));
2296
+ return {
2297
+ count: components.length,
2298
+ components
2299
+ };
2300
+ }
2301
+ async function addComponent(state, input) {
2302
+ const loaded = state.requireSketch(input.sketchId);
2303
+ const def = loaded.definition;
2304
+ const renderer = def.renderer.type;
2305
+ const entry = COMPONENT_REGISTRY[input.component];
2306
+ if (!entry) {
2307
+ throw new Error(`Unknown component: "${input.component}"`);
2308
+ }
2309
+ const target = RENDERER_TARGET[renderer];
2310
+ if (entry.target !== target) {
2311
+ throw new Error(
2312
+ `Component "${input.component}" has target "${entry.target}" but renderer "${renderer}" requires target "${target}"`
2313
+ );
2314
+ }
2315
+ if (entry.renderers.length > 0 && !entry.renderers.includes(renderer)) {
2316
+ throw new Error(
2317
+ `Component "${input.component}" is not compatible with renderer "${renderer}". Compatible: ${entry.renderers.join(", ")}`
2318
+ );
2319
+ }
2320
+ const existingComponents = {};
2321
+ if (def.components) {
2322
+ for (const [name, value] of Object.entries(def.components)) {
2323
+ if (typeof value === "string") {
2324
+ existingComponents[name] = value;
2325
+ } else if (value.version) {
2326
+ existingComponents[name] = value.version;
2327
+ }
2328
+ }
2329
+ }
2330
+ if (existingComponents[input.component]) {
2331
+ throw new Error(
2332
+ `Component "${input.component}" is already present in sketch "${input.sketchId}"`
2333
+ );
2334
+ }
2335
+ existingComponents[input.component] = input.version ?? "^1.0.0";
2336
+ const resolved = resolveComponents2(existingComponents, renderer);
2337
+ const resolvedRecord = {};
2338
+ for (const rc of resolved) {
2339
+ resolvedRecord[rc.name] = {
2340
+ version: rc.version,
2341
+ code: rc.code,
2342
+ exports: [...rc.exports]
2343
+ };
2344
+ }
2345
+ const previousNames = new Set(
2346
+ def.components ? Object.keys(def.components) : []
2347
+ );
2348
+ const added = resolved.map((rc) => rc.name).filter((name) => !previousNames.has(name));
2349
+ const newDef = {
2350
+ ...def,
2351
+ genart: "1.2",
2352
+ modified: (/* @__PURE__ */ new Date()).toISOString(),
2353
+ components: resolvedRecord
2354
+ };
2355
+ state.sketches.set(input.sketchId, {
2356
+ definition: newDef,
2357
+ path: loaded.path
2358
+ });
2359
+ const json = serializeGenart4(newDef);
2360
+ if (!state.remoteMode) {
2361
+ await writeFile5(loaded.path, json, "utf-8");
2362
+ }
2363
+ state.emitMutation("sketch:updated", {
2364
+ id: input.sketchId,
2365
+ updated: ["components"]
2366
+ });
2367
+ return {
2368
+ success: true,
2369
+ sketchId: input.sketchId,
2370
+ components: resolvedRecord,
2371
+ added,
2372
+ fileContent: json
2373
+ };
2374
+ }
2375
+ async function removeComponent(state, input) {
2376
+ const loaded = state.requireSketch(input.sketchId);
2377
+ const def = loaded.definition;
2378
+ if (!def.components || !def.components[input.component]) {
2379
+ throw new Error(
2380
+ `Component "${input.component}" is not present in sketch "${input.sketchId}"`
2381
+ );
2382
+ }
2383
+ const remaining = { ...def.components };
2384
+ delete remaining[input.component];
2385
+ for (const [name, value] of Object.entries(remaining)) {
2386
+ const entry = COMPONENT_REGISTRY[name];
2387
+ if (entry && entry.dependencies.includes(input.component)) {
2388
+ throw new Error(
2389
+ `Cannot remove "${input.component}": component "${name}" depends on it`
2390
+ );
2391
+ }
2392
+ }
2393
+ let warning;
2394
+ const removedValue = def.components[input.component];
2395
+ const exports = typeof removedValue === "string" ? COMPONENT_REGISTRY[input.component]?.exports ?? [] : removedValue.exports ?? [];
2396
+ const usedExports = exports.filter((exp) => def.algorithm.includes(exp));
2397
+ if (usedExports.length > 0) {
2398
+ warning = `Algorithm may reference these exports from "${input.component}": ${usedExports.join(", ")}. Review your algorithm after removal.`;
2399
+ }
2400
+ const removed = [input.component];
2401
+ const neededDeps = /* @__PURE__ */ new Set();
2402
+ for (const name of Object.keys(remaining)) {
2403
+ const entry = COMPONENT_REGISTRY[name];
2404
+ if (entry) {
2405
+ collectTransitiveDeps(name, neededDeps);
2406
+ }
2407
+ }
2408
+ for (const name of Object.keys(remaining)) {
2409
+ if (!neededDeps.has(name) && !isDirectComponent(name, remaining)) {
2410
+ delete remaining[name];
2411
+ removed.push(name);
2412
+ }
2413
+ }
2414
+ const hasRemaining = Object.keys(remaining).length > 0;
2415
+ const newDef = {
2416
+ ...def,
2417
+ modified: (/* @__PURE__ */ new Date()).toISOString(),
2418
+ ...hasRemaining ? { components: remaining } : { components: void 0 }
2419
+ };
2420
+ state.sketches.set(input.sketchId, {
2421
+ definition: newDef,
2422
+ path: loaded.path
2423
+ });
2424
+ const json = serializeGenart4(newDef);
2425
+ if (!state.remoteMode) {
2426
+ await writeFile5(loaded.path, json, "utf-8");
2427
+ }
2428
+ state.emitMutation("sketch:updated", {
2429
+ id: input.sketchId,
2430
+ updated: ["components"]
2431
+ });
2432
+ return {
2433
+ success: true,
2434
+ sketchId: input.sketchId,
2435
+ removed,
2436
+ ...warning ? { warning } : {},
2437
+ fileContent: json
2438
+ };
2439
+ }
2440
+ function collectTransitiveDeps(name, deps) {
2441
+ const entry = COMPONENT_REGISTRY[name];
2442
+ if (!entry) return;
2443
+ deps.add(name);
2444
+ for (const dep of entry.dependencies) {
2445
+ if (!deps.has(dep)) {
2446
+ collectTransitiveDeps(dep, deps);
2447
+ }
2448
+ }
2449
+ }
2450
+ function isDirectComponent(name, components) {
2451
+ return name in components;
2452
+ }
2453
+
2454
+ // src/tools/capture.ts
2455
+ import { writeFile as writeFile6 } from "fs/promises";
2178
2456
  import {
2179
2457
  createDefaultRegistry as createDefaultRegistry2
2180
2458
  } from "@genart-dev/core";
@@ -2352,7 +2630,7 @@ async function buildScreenshotMetadata(state, multi, info) {
2352
2630
  previewPath: info.previewPath
2353
2631
  };
2354
2632
  if (!state.remoteMode) {
2355
- await writeFile5(info.previewPath, multi.previewPng);
2633
+ await writeFile6(info.previewPath, multi.previewPng);
2356
2634
  metadata.savedPreviewTo = info.previewPath;
2357
2635
  }
2358
2636
  return metadata;
@@ -2413,12 +2691,12 @@ async function captureBatch(state, input) {
2413
2691
 
2414
2692
  // src/tools/export.ts
2415
2693
  import { createWriteStream } from "fs";
2416
- import { stat as stat4, writeFile as writeFile6 } from "fs/promises";
2694
+ import { stat as stat4, writeFile as writeFile7 } from "fs/promises";
2417
2695
  import { dirname as dirname6 } from "path";
2418
2696
  import archiver from "archiver";
2419
2697
  import {
2420
2698
  createDefaultRegistry as createDefaultRegistry3,
2421
- serializeGenart as serializeGenart4
2699
+ serializeGenart as serializeGenart5
2422
2700
  } from "@genart-dev/core";
2423
2701
  var registry3 = createDefaultRegistry3();
2424
2702
  async function validateOutputPath(outputPath) {
@@ -2488,7 +2766,7 @@ async function exportHtml(sketch, outputPath) {
2488
2766
  const adapter = registry3.resolve(sketch.renderer.type);
2489
2767
  const html = adapter.generateStandaloneHTML(sketch);
2490
2768
  const content = Buffer.from(html, "utf-8");
2491
- await writeFile6(outputPath, content);
2769
+ await writeFile7(outputPath, content);
2492
2770
  return {
2493
2771
  success: true,
2494
2772
  sketchId: sketch.id,
@@ -2504,7 +2782,7 @@ async function exportPng(sketch, input) {
2504
2782
  const width = input.width ?? sketch.canvas.width;
2505
2783
  const height = input.height ?? sketch.canvas.height;
2506
2784
  const result = await captureHtml({ html, width, height });
2507
- await writeFile6(input.outputPath, result.bytes);
2785
+ await writeFile7(input.outputPath, result.bytes);
2508
2786
  return {
2509
2787
  success: true,
2510
2788
  sketchId: sketch.id,
@@ -2519,7 +2797,7 @@ async function exportSvg(sketch, input) {
2519
2797
  const height = input.height ?? sketch.canvas.height;
2520
2798
  if (sketch.renderer.type === "svg") {
2521
2799
  const content2 = Buffer.from(sketch.algorithm, "utf-8");
2522
- await writeFile6(input.outputPath, content2);
2800
+ await writeFile7(input.outputPath, content2);
2523
2801
  return {
2524
2802
  success: true,
2525
2803
  sketchId: sketch.id,
@@ -2541,7 +2819,7 @@ async function exportSvg(sketch, input) {
2541
2819
  href="data:image/png;base64,${b64}"/>
2542
2820
  </svg>`;
2543
2821
  const content = Buffer.from(svg, "utf-8");
2544
- await writeFile6(input.outputPath, content);
2822
+ await writeFile7(input.outputPath, content);
2545
2823
  return {
2546
2824
  success: true,
2547
2825
  sketchId: sketch.id,
@@ -2554,7 +2832,7 @@ async function exportSvg(sketch, input) {
2554
2832
  }
2555
2833
  async function exportAlgorithm(sketch, outputPath) {
2556
2834
  const content = Buffer.from(sketch.algorithm, "utf-8");
2557
- await writeFile6(outputPath, content);
2835
+ await writeFile7(outputPath, content);
2558
2836
  return {
2559
2837
  success: true,
2560
2838
  sketchId: sketch.id,
@@ -2569,7 +2847,7 @@ async function exportZip(sketch, input) {
2569
2847
  const width = input.width ?? sketch.canvas.width;
2570
2848
  const height = input.height ?? sketch.canvas.height;
2571
2849
  const html = adapter.generateStandaloneHTML(sketch);
2572
- const genartJson = serializeGenart4(sketch);
2850
+ const genartJson = serializeGenart5(sketch);
2573
2851
  const algorithm = sketch.algorithm;
2574
2852
  const algExt = algorithmExtension(sketch.renderer.type);
2575
2853
  const captureResult = await captureHtml({ html, width, height });
@@ -3106,6 +3384,7 @@ function createServer(state) {
3106
3384
  );
3107
3385
  registerWorkspaceTools(server, state);
3108
3386
  registerSketchTools(server, state);
3387
+ registerComponentTools(server, state);
3109
3388
  registerSelectionTools(server, state);
3110
3389
  registerParameterTools(server, state);
3111
3390
  registerArrangementTools(server, state);
@@ -3209,7 +3488,7 @@ function registerWorkspaceTools(server, state) {
3209
3488
  function registerSketchTools(server, state) {
3210
3489
  server.tool(
3211
3490
  "create_sketch",
3212
- "Create a new .genart sketch file from metadata, parameters, and algorithm",
3491
+ '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.',
3213
3492
  {
3214
3493
  id: z2.string().describe("URL-safe kebab-case identifier"),
3215
3494
  title: z2.string().describe("Human-readable title"),
@@ -3247,6 +3526,16 @@ function registerSketchTools(server, state) {
3247
3526
  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)."),
3248
3527
  seed: z2.number().optional().describe("Initial random seed (default: random)"),
3249
3528
  skills: z2.array(z2.string()).optional().describe("Design skill references"),
3529
+ components: z2.record(
3530
+ z2.union([
3531
+ z2.string(),
3532
+ z2.object({
3533
+ version: z2.string().optional(),
3534
+ code: z2.string().optional(),
3535
+ exports: z2.array(z2.string()).optional()
3536
+ })
3537
+ ])
3538
+ ).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.'),
3250
3539
  addToWorkspace: z2.string().optional().describe("Path to workspace to add sketch to after creation"),
3251
3540
  agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
3252
3541
  model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
@@ -3326,11 +3615,21 @@ function registerSketchTools(server, state) {
3326
3615
  );
3327
3616
  server.tool(
3328
3617
  "update_algorithm",
3329
- "Replace the algorithm source code of a sketch",
3618
+ "Replace the algorithm source code of a sketch. If adding/changing components, pass them in the components field alongside the algorithm.",
3330
3619
  {
3331
3620
  sketchId: z2.string().describe("ID of the sketch to update"),
3332
3621
  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)."),
3333
3622
  validate: z2.boolean().optional().describe("Run renderer-specific validation before saving (default: true)"),
3623
+ components: z2.record(
3624
+ z2.union([
3625
+ z2.string(),
3626
+ z2.object({
3627
+ version: z2.string().optional(),
3628
+ code: z2.string().optional(),
3629
+ exports: z2.array(z2.string()).optional()
3630
+ })
3631
+ ])
3632
+ ).optional().describe("Component dependencies to resolve alongside the algorithm update. Use list_components to see available."),
3334
3633
  agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
3335
3634
  model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
3336
3635
  },
@@ -3426,6 +3725,76 @@ function registerSketchTools(server, state) {
3426
3725
  }
3427
3726
  );
3428
3727
  }
3728
+ function registerComponentTools(server, state) {
3729
+ server.tool(
3730
+ "list_components",
3731
+ "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.",
3732
+ {
3733
+ renderer: z2.enum(["p5", "three", "glsl", "canvas2d", "svg"]).optional().describe("Filter by renderer compatibility"),
3734
+ category: z2.enum([
3735
+ "randomness",
3736
+ "noise",
3737
+ "math",
3738
+ "easing",
3739
+ "color",
3740
+ "vector",
3741
+ "geometry",
3742
+ "grid",
3743
+ "particle",
3744
+ "physics",
3745
+ "distribution",
3746
+ "pattern",
3747
+ "sdf",
3748
+ "transform",
3749
+ "animation",
3750
+ "string",
3751
+ "data-structure",
3752
+ "imaging"
3753
+ ]).optional().describe("Filter by component category")
3754
+ },
3755
+ async (args) => {
3756
+ try {
3757
+ const result = await listComponents(state, args);
3758
+ return jsonResult(result);
3759
+ } catch (e) {
3760
+ return toolError(e instanceof Error ? e.message : String(e));
3761
+ }
3762
+ }
3763
+ );
3764
+ server.tool(
3765
+ "add_component",
3766
+ "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.",
3767
+ {
3768
+ sketchId: z2.string().describe("ID of the sketch to add the component to"),
3769
+ component: z2.string().describe("Component name (e.g. 'prng', 'noise-2d', 'glsl-noise')"),
3770
+ version: z2.string().optional().describe("Version range (default: '^1.0.0')")
3771
+ },
3772
+ async (args) => {
3773
+ try {
3774
+ const result = await addComponent(state, args);
3775
+ return jsonResult(result);
3776
+ } catch (e) {
3777
+ return toolError(e instanceof Error ? e.message : String(e));
3778
+ }
3779
+ }
3780
+ );
3781
+ server.tool(
3782
+ "remove_component",
3783
+ "Remove a component dependency from a sketch. Checks for dependent components and warns if the algorithm references the component's exports.",
3784
+ {
3785
+ sketchId: z2.string().describe("ID of the sketch to remove the component from"),
3786
+ component: z2.string().describe("Component name to remove")
3787
+ },
3788
+ async (args) => {
3789
+ try {
3790
+ const result = await removeComponent(state, args);
3791
+ return jsonResult(result);
3792
+ } catch (e) {
3793
+ return toolError(e instanceof Error ? e.message : String(e));
3794
+ }
3795
+ }
3796
+ );
3797
+ }
3429
3798
  function registerSelectionTools(server, state) {
3430
3799
  server.tool(
3431
3800
  "get_selection",