@vcad/mcp 0.9.4-main.29 → 0.9.4-main.31

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/README.md CHANGED
@@ -6,4 +6,4 @@ vcad's MCP server as a self-contained bundle (server + BRep kernel WASM).
6
6
  { "mcpServers": { "vcad": { "command": "npx", "args": ["-y", "@vcad/mcp"] } } }
7
7
  ```
8
8
 
9
- Built from d80a65e336d1c8b8ebf75dd1bebc553b41f7ca93 at 2026-07-26T00:45:54.095Z. Source: https://github.com/ecto/vcad
9
+ Built from ab04c2e57eeea3736ac3421fabbc01e89959f953 at 2026-07-26T01:25:19.323Z. Source: https://github.com/ecto/vcad
package/index.mjs CHANGED
@@ -10873,11 +10873,11 @@ var init_analyze = __esm({
10873
10873
  type: "module"
10874
10874
  });
10875
10875
  this.worker = worker;
10876
- this.ready = new Promise((resolve3, reject) => {
10876
+ this.ready = new Promise((resolve4, reject) => {
10877
10877
  const onMessage = (e) => {
10878
10878
  const { type, id, message, analysis } = e.data;
10879
10879
  if (type === "ready") {
10880
- resolve3();
10880
+ resolve4();
10881
10881
  return;
10882
10882
  }
10883
10883
  if (type === "error" && id === null) {
@@ -10901,9 +10901,9 @@ var init_analyze = __esm({
10901
10901
  return this.ready;
10902
10902
  }
10903
10903
  request(msg, transfer = []) {
10904
- return this.ensureWorker().then(() => new Promise((resolve3, reject) => {
10904
+ return this.ensureWorker().then(() => new Promise((resolve4, reject) => {
10905
10905
  const id = this.nextId++;
10906
- this.pending.set(id, { resolve: resolve3, reject });
10906
+ this.pending.set(id, { resolve: resolve4, reject });
10907
10907
  this.worker.postMessage({ ...msg, id }, transfer);
10908
10908
  }));
10909
10909
  }
@@ -11463,11 +11463,11 @@ var init_dist = __esm({
11463
11463
  return;
11464
11464
  try {
11465
11465
  const worker = new Worker(new URL("./eval-worker.js", import.meta.url), { type: "module" });
11466
- this.workerReady = new Promise((resolve3, reject) => {
11466
+ this.workerReady = new Promise((resolve4, reject) => {
11467
11467
  const onMessage = (e) => {
11468
11468
  if (e.data.type === "ready") {
11469
11469
  worker.removeEventListener("message", onMessage);
11470
- resolve3();
11470
+ resolve4();
11471
11471
  } else if (e.data.type === "error" && e.data.id === null) {
11472
11472
  worker.removeEventListener("message", onMessage);
11473
11473
  console.warn("[ENGINE] Worker WASM init failed:", e.data.message);
@@ -11513,6 +11513,7 @@ var init_dist = __esm({
11513
11513
  createDetailView: wasmModule7.createDetailView,
11514
11514
  evaluateDocument: wasmModule7.evaluateDocument,
11515
11515
  evalVcadSource: wasmModule7.evalVcadSource,
11516
+ evalVcadSourceWithModules: wasmModule7.evalVcadSourceWithModules,
11516
11517
  documentParameterGradient: wasmModule7.documentParameterGradient,
11517
11518
  getPartsManifest: wasmModule7.getPartsManifest,
11518
11519
  buildPart: wasmModule7.buildPart,
@@ -11656,7 +11657,7 @@ var init_dist = __esm({
11656
11657
  const worker = this.worker;
11657
11658
  const id = Math.random().toString(36).slice(2);
11658
11659
  const skipClash = options.skipClashDetection ?? false;
11659
- return new Promise((resolve3, reject) => {
11660
+ return new Promise((resolve4, reject) => {
11660
11661
  const onMessage = (e) => {
11661
11662
  if (e.data.id !== id)
11662
11663
  return;
@@ -11666,7 +11667,7 @@ var init_dist = __esm({
11666
11667
  if (scene.timing && _Engine.DEV) {
11667
11668
  _Engine.logTiming(scene.timing, e.data.workerTotalMs);
11668
11669
  }
11669
- resolve3(scene);
11670
+ resolve4(scene);
11670
11671
  } else if (e.data.type === "error") {
11671
11672
  reject(new Error(e.data.message));
11672
11673
  }
@@ -12185,6 +12186,21 @@ var init_dist = __esm({
12185
12186
  const json = this.kernel.evalVcadSource(source);
12186
12187
  return JSON.parse(json);
12187
12188
  }
12189
+ /**
12190
+ * Evaluate loon source whose `[use ...]` resolves against an in-memory
12191
+ * `name -> source` map — the browser's stand-in for a filesystem.
12192
+ *
12193
+ * With an empty map this is exactly {@link evalVcadSource}. Returns null
12194
+ * if the kernel doesn't support module-aware loon evaluation.
12195
+ */
12196
+ evalVcadSourceWithModules(source, modules) {
12197
+ if (!Object.keys(modules).length)
12198
+ return this.evalVcadSource(source);
12199
+ if (!this.kernel.evalVcadSourceWithModules)
12200
+ return null;
12201
+ const json = this.kernel.evalVcadSourceWithModules(source, JSON.stringify(modules));
12202
+ return JSON.parse(json);
12203
+ }
12188
12204
  /** Evaluate a preview extrusion without adding to document */
12189
12205
  evaluateExtrudePreview(origin, xDir, yDir, segments, direction) {
12190
12206
  if (segments.length === 0)
@@ -17849,12 +17865,12 @@ var init_stdio2 = __esm({
17849
17865
  this.onclose?.();
17850
17866
  }
17851
17867
  send(message) {
17852
- return new Promise((resolve3) => {
17868
+ return new Promise((resolve4) => {
17853
17869
  const json = serializeMessage(message);
17854
17870
  if (this._stdout.write(json)) {
17855
- resolve3();
17871
+ resolve4();
17856
17872
  } else {
17857
- this._stdout.once("drain", resolve3);
17873
+ this._stdout.once("drain", resolve4);
17858
17874
  }
17859
17875
  });
17860
17876
  }
@@ -18898,7 +18914,7 @@ var init_protocol = __esm({
18898
18914
  return;
18899
18915
  }
18900
18916
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
18901
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
18917
+ await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
18902
18918
  options?.signal?.throwIfAborted();
18903
18919
  }
18904
18920
  } catch (error2) {
@@ -18915,7 +18931,7 @@ var init_protocol = __esm({
18915
18931
  */
18916
18932
  request(request, resultSchema, options) {
18917
18933
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
18918
- return new Promise((resolve3, reject) => {
18934
+ return new Promise((resolve4, reject) => {
18919
18935
  const earlyReject = (error2) => {
18920
18936
  reject(error2);
18921
18937
  };
@@ -18993,7 +19009,7 @@ var init_protocol = __esm({
18993
19009
  if (!parseResult.success) {
18994
19010
  reject(parseResult.error);
18995
19011
  } else {
18996
- resolve3(parseResult.data);
19012
+ resolve4(parseResult.data);
18997
19013
  }
18998
19014
  } catch (error2) {
18999
19015
  reject(error2);
@@ -19254,12 +19270,12 @@ var init_protocol = __esm({
19254
19270
  }
19255
19271
  } catch {
19256
19272
  }
19257
- return new Promise((resolve3, reject) => {
19273
+ return new Promise((resolve4, reject) => {
19258
19274
  if (signal.aborted) {
19259
19275
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
19260
19276
  return;
19261
19277
  }
19262
- const timeoutId = setTimeout(resolve3, interval);
19278
+ const timeoutId = setTimeout(resolve4, interval);
19263
19279
  signal.addEventListener("abort", () => {
19264
19280
  clearTimeout(timeoutId);
19265
19281
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -22286,7 +22302,7 @@ var require_compile = __commonJS({
22286
22302
  const schOrFunc = root.refs[ref];
22287
22303
  if (schOrFunc)
22288
22304
  return schOrFunc;
22289
- let _sch = resolve3.call(this, root, ref);
22305
+ let _sch = resolve4.call(this, root, ref);
22290
22306
  if (_sch === void 0) {
22291
22307
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
22292
22308
  const { schemaId } = this.opts;
@@ -22313,7 +22329,7 @@ var require_compile = __commonJS({
22313
22329
  function sameSchemaEnv(s1, s2) {
22314
22330
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
22315
22331
  }
22316
- function resolve3(root, ref) {
22332
+ function resolve4(root, ref) {
22317
22333
  let sch;
22318
22334
  while (typeof (sch = this.refs[ref]) == "string")
22319
22335
  ref = sch;
@@ -22888,7 +22904,7 @@ var require_fast_uri = __commonJS({
22888
22904
  }
22889
22905
  return uri;
22890
22906
  }
22891
- function resolve3(baseURI, relativeURI, options) {
22907
+ function resolve4(baseURI, relativeURI, options) {
22892
22908
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
22893
22909
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
22894
22910
  schemelessOptions.skipEscape = true;
@@ -23115,7 +23131,7 @@ var require_fast_uri = __commonJS({
23115
23131
  var fastUri = {
23116
23132
  SCHEMES,
23117
23133
  normalize,
23118
- resolve: resolve3,
23134
+ resolve: resolve4,
23119
23135
  resolveComponent,
23120
23136
  equal,
23121
23137
  serialize,
@@ -29268,21 +29284,21 @@ var require_react_development = __commonJS({
29268
29284
  );
29269
29285
  actScopeDepth = prevActScopeDepth;
29270
29286
  }
29271
- function recursivelyFlushAsyncActWork(returnValue, resolve3, reject) {
29287
+ function recursivelyFlushAsyncActWork(returnValue, resolve4, reject) {
29272
29288
  var queue = ReactSharedInternals.actQueue;
29273
29289
  if (null !== queue)
29274
29290
  if (0 !== queue.length)
29275
29291
  try {
29276
29292
  flushActQueue(queue);
29277
29293
  enqueueTask(function() {
29278
- return recursivelyFlushAsyncActWork(returnValue, resolve3, reject);
29294
+ return recursivelyFlushAsyncActWork(returnValue, resolve4, reject);
29279
29295
  });
29280
29296
  return;
29281
29297
  } catch (error2) {
29282
29298
  ReactSharedInternals.thrownErrors.push(error2);
29283
29299
  }
29284
29300
  else ReactSharedInternals.actQueue = null;
29285
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve3(returnValue);
29301
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve4(returnValue);
29286
29302
  }
29287
29303
  function flushActQueue(queue) {
29288
29304
  if (!isFlushing) {
@@ -29469,7 +29485,7 @@ var require_react_development = __commonJS({
29469
29485
  ));
29470
29486
  });
29471
29487
  return {
29472
- then: function(resolve3, reject) {
29488
+ then: function(resolve4, reject) {
29473
29489
  didAwaitActCall = true;
29474
29490
  thenable.then(
29475
29491
  function(returnValue) {
@@ -29479,7 +29495,7 @@ var require_react_development = __commonJS({
29479
29495
  flushActQueue(queue), enqueueTask(function() {
29480
29496
  return recursivelyFlushAsyncActWork(
29481
29497
  returnValue,
29482
- resolve3,
29498
+ resolve4,
29483
29499
  reject
29484
29500
  );
29485
29501
  });
@@ -29493,7 +29509,7 @@ var require_react_development = __commonJS({
29493
29509
  ReactSharedInternals.thrownErrors.length = 0;
29494
29510
  reject(_thrownError);
29495
29511
  }
29496
- } else resolve3(returnValue);
29512
+ } else resolve4(returnValue);
29497
29513
  },
29498
29514
  function(error2) {
29499
29515
  popActScope(prevActQueue, prevActScopeDepth);
@@ -29515,15 +29531,15 @@ var require_react_development = __commonJS({
29515
29531
  if (0 < ReactSharedInternals.thrownErrors.length)
29516
29532
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
29517
29533
  return {
29518
- then: function(resolve3, reject) {
29534
+ then: function(resolve4, reject) {
29519
29535
  didAwaitActCall = true;
29520
29536
  0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
29521
29537
  return recursivelyFlushAsyncActWork(
29522
29538
  returnValue$jscomp$0,
29523
- resolve3,
29539
+ resolve4,
29524
29540
  reject
29525
29541
  );
29526
- })) : resolve3(returnValue$jscomp$0);
29542
+ })) : resolve4(returnValue$jscomp$0);
29527
29543
  }
29528
29544
  };
29529
29545
  };
@@ -41084,6 +41100,22 @@ var init_CHANGELOG = __esm({
41084
41100
  "checkpoint_document"
41085
41101
  ]
41086
41102
  },
41103
+ {
41104
+ id: "2026-07-25-loon-modules-everywhere",
41105
+ version: "0.9.4",
41106
+ date: "2026-07-25",
41107
+ category: "feat",
41108
+ title: "Multi-file loon projects, everywhere loon runs",
41109
+ summary: "[use ...] now resolves in the browser and over MCP, not just on disk: pass modules by value or point create_cad_loon at a base_dir, and pub, aliasing and selective imports all work.",
41110
+ features: [
41111
+ "loon",
41112
+ "mcp",
41113
+ "modules"
41114
+ ],
41115
+ mcpTools: [
41116
+ "create_cad_loon"
41117
+ ]
41118
+ },
41087
41119
  {
41088
41120
  id: "2026-07-25-kicad-export-design-rules",
41089
41121
  version: "0.9.4",
@@ -49566,6 +49598,8 @@ var init_loon_macros = __esm({
49566
49598
  });
49567
49599
 
49568
49600
  // src/tools/loon.ts
49601
+ import { readFileSync as readFileSync3 } from "node:fs";
49602
+ import { isAbsolute as isAbsolute2, join as join2, resolve as resolve2, sep as sep2 } from "node:path";
49569
49603
  function composeLoonProgram(input) {
49570
49604
  const { source, use_loons, loons } = input;
49571
49605
  const names = [
@@ -49577,14 +49611,56 @@ function composeLoonProgram(input) {
49577
49611
 
49578
49612
  ${source}`;
49579
49613
  }
49614
+ function importedNames(source) {
49615
+ const names = [];
49616
+ const re = /\[\s*use\s+([A-Za-z_][\w.-]*)/g;
49617
+ for (let m = re.exec(source); m; m = re.exec(source)) names.push(m[1]);
49618
+ return names;
49619
+ }
49620
+ function readModule(base, name) {
49621
+ if (isAbsolute2(name) || name.split(".").includes("..")) return null;
49622
+ const root = resolve2(base);
49623
+ for (const ext of [".loon", ".oo"]) {
49624
+ const file = resolve2(join2(root, ...name.split(".")) + ext);
49625
+ if (file !== root && !file.startsWith(root + sep2)) continue;
49626
+ try {
49627
+ return readFileSync3(file, "utf8");
49628
+ } catch {
49629
+ }
49630
+ }
49631
+ return null;
49632
+ }
49633
+ function composeLoonModules(input) {
49634
+ const { source, loons, modules, base_dir } = input;
49635
+ const map = {};
49636
+ for (const m of loons ?? []) map[m.name] = m.source;
49637
+ Object.assign(map, modules ?? {});
49638
+ if (base_dir) {
49639
+ const pending2 = [
49640
+ ...importedNames(source),
49641
+ ...Object.values(map).flatMap(importedNames)
49642
+ ];
49643
+ const seen = /* @__PURE__ */ new Set();
49644
+ while (pending2.length) {
49645
+ const name = pending2.pop();
49646
+ if (seen.has(name) || map[name]) continue;
49647
+ seen.add(name);
49648
+ const src = readModule(base_dir, name);
49649
+ if (src === null) continue;
49650
+ map[name] = src;
49651
+ pending2.push(...importedNames(src));
49652
+ }
49653
+ }
49654
+ return map;
49655
+ }
49580
49656
  function createCadLoon(input, engine) {
49581
49657
  const { format = "vcode" } = input;
49582
49658
  const source = composeLoonProgram(input);
49583
- const doc = engine.evalVcadSource(source);
49659
+ const modules = composeLoonModules(input);
49660
+ const doc = engine.evalVcadSourceWithModules(source, modules);
49584
49661
  if (!doc) {
49585
- return {
49586
- content: [{ type: "text", text: "Error: Loon evaluation not supported by this engine build" }]
49587
- };
49662
+ const text2 = Object.keys(modules).length ? "Error: this kernel build cannot resolve loon modules ([use ...]) \u2014 update the kernel, or inline the modules into `source`" : "Error: Loon evaluation not supported by this engine build";
49663
+ return { content: [{ type: "text", text: text2 }] };
49588
49664
  }
49589
49665
  const text = format === "json" ? JSON.stringify(doc, null, 2) : toVCode2(doc);
49590
49666
  return {
@@ -49629,6 +49705,15 @@ var init_loon = __esm({
49629
49705
  }
49630
49706
  }
49631
49707
  },
49708
+ modules: {
49709
+ type: "object",
49710
+ description: 'Multi-file projects, by value: a { "<module name>": "<loon source>" } map that `[use <name>]` in `source` resolves against. `pub` controls what a module exports; `[use m :as alias]` and `[use m [a b]]` work as in the language. Entries passed via `loons` are importable by name too.',
49711
+ additionalProperties: { type: "string" }
49712
+ },
49713
+ base_dir: {
49714
+ type: "string",
49715
+ description: "Server-side directory that `[use <name>]` resolves against \u2014 the server reads <base_dir>/<name>.loon (dots are path separators) and hands the sources to the kernel, following nested imports. Reads are confined to this directory. Explicit `modules` entries win."
49716
+ },
49632
49717
  format: {
49633
49718
  type: "string",
49634
49719
  enum: ["vcode", "json"],
@@ -49641,7 +49726,7 @@ var init_loon = __esm({
49641
49726
  {
49642
49727
  name: "create_cad_loon",
49643
49728
  pack: null,
49644
- description: 'The preferred authoring tool for whole parts and multi-feature models \u2014 one call, full vocabulary. Create a CAD document from loon source code. Loon is a Lisp-like language for parametric CAD \u2014 the FULL modeling vocabulary (patterns, sketches, extrude/revolve/sweep/loft, assemblies) is available here even where no dedicated MCP tool exists. For incremental single-node edits to an open session, use create/update/delete instead.\n\nPrimitives: [cube x y z], [cylinder r h], [sphere r], [cone r-bottom r-top h]\nBooleans (subject-last): [difference tool subject], [union other subject], [intersection other subject]\nTransforms (subject-last): [translate x y z s], [rotate rx ry rz s], [scale sx sy sz s]\nFeatures: [fillet r s], [chamfer d s], [shell t s]\nPatterns (subject-last): [linear-pattern dx dy dz count spacing s], [circular-pattern ox oy oz ax ay az count angle s] \u2014 e.g. a bolt circle is [circular-pattern 0 0 0 0 0 1 6 360 bolt-hole]\nSketches: [sketch ox oy oz xx xy xz yx yy yz #[segments]] with [line x1 y1 x2 y2] and [arc x1 y1 x2 y2 cx cy ccw]\nSketch ops (sketch-last): [extrude dx dy dz sk], [revolve aox aoy aoz adx ady adz angle sk], [sweep-line sx sy sz ex ey ez sk], [sweep-helix radius pitch height turns sk], [loft #[sk1 sk2 \u2026]]\nAssemblies: [assembly #[parts] #[instances] #[joints] ground-id] with [part name solid "material"], [instance name part-name x y z], [revolute-joint \u2026], [prismatic-joint \u2026], [fixed-joint \u2026], [ball-joint \u2026]\nPipe: [pipe [cube 50 30 5] [difference [cylinder 3 10]] [fillet 1.0]]\nLet bindings: [let body [cube 50 30 5]]\nScene: [root solid "material-name"]',
49729
+ description: 'The preferred authoring tool for whole parts and multi-feature models \u2014 one call, full vocabulary. Create a CAD document from loon source code. Loon is a Lisp-like language for parametric CAD \u2014 the FULL modeling vocabulary (patterns, sketches, extrude/revolve/sweep/loft, assemblies) is available here even where no dedicated MCP tool exists. For incremental single-node edits to an open session, use create/update/delete instead.\n\nPrimitives: [cube x y z], [cylinder r h], [sphere r], [cone r-bottom r-top h]\nBooleans (subject-last): [difference tool subject], [union other subject], [intersection other subject]\nTransforms (subject-last): [translate x y z s], [rotate rx ry rz s], [scale sx sy sz s]\nFeatures: [fillet r s], [chamfer d s], [shell t s]\nPatterns (subject-last): [linear-pattern dx dy dz count spacing s], [circular-pattern ox oy oz ax ay az count angle s] \u2014 e.g. a bolt circle is [circular-pattern 0 0 0 0 0 1 6 360 bolt-hole]\nSketches: [sketch ox oy oz xx xy xz yx yy yz #[segments]] with [line x1 y1 x2 y2] and [arc x1 y1 x2 y2 cx cy ccw]\nSketch ops (sketch-last): [extrude dx dy dz sk], [revolve aox aoy aoz adx ady adz angle sk], [sweep-line sx sy sz ex ey ez sk], [sweep-helix radius pitch height turns sk], [loft #[sk1 sk2 \u2026]]\nAssemblies: [assembly #[parts] #[instances] #[joints] ground-id] with [part name solid "material"], [instance name part-name x y z], [revolute-joint \u2026], [prismatic-joint \u2026], [fixed-joint \u2026], [ball-joint \u2026]\nPipe: [pipe [cube 50 30 5] [difference [cylinder 3 10]] [fillet 1.0]]\nLet bindings: [let body [cube 50 30 5]]\nScene: [root solid "material-name"]\nModules: [use bracket] then [bracket.plate] \u2014 multi-file projects work here, with sources passed in `modules` (or read from `base_dir`); [use bracket :as b] aliases, [use bracket [plate]] imports selectively, and `pub` in a module picks what it exports',
49645
49730
  inputSchema: createCadLoonSchema,
49646
49731
  handler: async (args, ctx) => {
49647
49732
  const useLoons = Array.isArray(args.use_loons) ? args.use_loons : void 0;
@@ -49653,7 +49738,10 @@ var init_loon = __esm({
49653
49738
  const targetId = typeof args.document_id === "string" ? args.document_id : null;
49654
49739
  if (targetId) getSession(targetId);
49655
49740
  try {
49656
- const doc = ctx.engine.evalVcadSource(composeLoonProgram(args));
49741
+ const doc = ctx.engine.evalVcadSourceWithModules(
49742
+ composeLoonProgram(args),
49743
+ composeLoonModules(args)
49744
+ );
49657
49745
  if (doc) {
49658
49746
  if (targetId) documents.set(targetId, doc);
49659
49747
  const integrity = computeIntegrity(doc, ctx.engine);
@@ -52928,7 +53016,7 @@ function normalizeOp(op, index) {
52928
53016
  return { name, args };
52929
53017
  }
52930
53018
  function resolveSymbolicRefs(args, createdIds, index) {
52931
- const resolve3 = (v) => {
53019
+ const resolve4 = (v) => {
52932
53020
  if (typeof v !== "string") return v;
52933
53021
  const m = SYMBOLIC_REF.exec(v);
52934
53022
  if (!m) return v;
@@ -52948,15 +53036,15 @@ function resolveSymbolicRefs(args, createdIds, index) {
52948
53036
  };
52949
53037
  const out = { ...args };
52950
53038
  for (const key of ["part_id", "node_id"]) {
52951
- if (key in out) out[key] = resolve3(out[key]);
53039
+ if (key in out) out[key] = resolve4(out[key]);
52952
53040
  }
52953
53041
  if (out.params && typeof out.params === "object" && !Array.isArray(out.params)) {
52954
53042
  const params = { ...out.params };
52955
53043
  for (const key of ["child", "left", "right", "sketch"]) {
52956
- if (key in params) params[key] = resolve3(params[key]);
53044
+ if (key in params) params[key] = resolve4(params[key]);
52957
53045
  }
52958
53046
  if (Array.isArray(params.sketches)) {
52959
- params.sketches = params.sketches.map(resolve3);
53047
+ params.sketches = params.sketches.map(resolve4);
52960
53048
  }
52961
53049
  out.params = params;
52962
53050
  }
@@ -57549,12 +57637,12 @@ var init_enclosure = __esm({
57549
57637
  // src/wasm/ecad-diff.ts
57550
57638
  import { createRequire } from "node:module";
57551
57639
  import { fileURLToPath } from "node:url";
57552
- import { dirname, resolve as resolve2 } from "node:path";
57640
+ import { dirname, resolve as resolve3 } from "node:path";
57553
57641
  function load() {
57554
57642
  if (cached2 !== void 0) return cached2;
57555
57643
  try {
57556
57644
  const here = dirname(fileURLToPath(import.meta.url));
57557
- const pkg = resolve2(here, "../../../../crates/vcad-ecad-diff-wasm/pkg/vcad_ecad_diff_wasm.js");
57645
+ const pkg = resolve3(here, "../../../../crates/vcad-ecad-diff-wasm/pkg/vcad_ecad_diff_wasm.js");
57558
57646
  const req = createRequire(import.meta.url);
57559
57647
  cached2 = req(pkg);
57560
57648
  } catch {
@@ -60096,9 +60184,9 @@ function isPinTypeOrPowerViolation(message) {
60096
60184
  function synthesizeNettedSheet(sheet, derived) {
60097
60185
  const connectedByRef = /* @__PURE__ */ new Map();
60098
60186
  for (const key of derived.netByPin.keys()) {
60099
- const sep2 = key.indexOf(PIN_SEP);
60100
- const ref = key.slice(0, sep2);
60101
- const pin = key.slice(sep2 + 1);
60187
+ const sep3 = key.indexOf(PIN_SEP);
60188
+ const ref = key.slice(0, sep3);
60189
+ const pin = key.slice(sep3 + 1);
60102
60190
  let set = connectedByRef.get(ref);
60103
60191
  if (!set) connectedByRef.set(ref, set = /* @__PURE__ */ new Set());
60104
60192
  set.add(pin);
@@ -69611,12 +69699,12 @@ import {
69611
69699
  existsSync as existsSync2,
69612
69700
  mkdtempSync,
69613
69701
  readdirSync as readdirSync2,
69614
- readFileSync as readFileSync3,
69702
+ readFileSync as readFileSync4,
69615
69703
  rmSync,
69616
69704
  writeFileSync as writeFileSync4
69617
69705
  } from "fs";
69618
69706
  import { tmpdir } from "os";
69619
- import { dirname as dirname2, join as join2 } from "path";
69707
+ import { dirname as dirname2, join as join3 } from "path";
69620
69708
  import { fileURLToPath as fileURLToPath2 } from "url";
69621
69709
  import { promisify } from "util";
69622
69710
  function textResult(payload, isError = false) {
@@ -69625,7 +69713,7 @@ function textResult(payload, isError = false) {
69625
69713
  function findUpward(start, probe) {
69626
69714
  let dir = start;
69627
69715
  for (let i = 0; i < 12; i++) {
69628
- if (existsSync2(join2(dir, probe))) return dir;
69716
+ if (existsSync2(join3(dir, probe))) return dir;
69629
69717
  const parent = dirname2(dir);
69630
69718
  if (parent === dir) break;
69631
69719
  dir = parent;
@@ -69634,18 +69722,18 @@ function findUpward(start, probe) {
69634
69722
  }
69635
69723
  function findMechevalRoot() {
69636
69724
  const override = process.env.MECHEVAL_DIR;
69637
- if (override && existsSync2(join2(override, "tasks"))) {
69725
+ if (override && existsSync2(join3(override, "tasks"))) {
69638
69726
  return override;
69639
69727
  }
69640
69728
  const here = dirname2(fileURLToPath2(import.meta.url));
69641
- const repoRoot = findUpward(here, join2("mecheval", "tasks")) ?? findUpward(process.cwd(), join2("mecheval", "tasks"));
69642
- return repoRoot ? join2(repoRoot, "mecheval") : null;
69729
+ const repoRoot = findUpward(here, join3("mecheval", "tasks")) ?? findUpward(process.cwd(), join3("mecheval", "tasks"));
69730
+ return repoRoot ? join3(repoRoot, "mecheval") : null;
69643
69731
  }
69644
69732
  function findGraderBin(mechevalRoot) {
69645
69733
  const override = process.env.MECHEVAL_GRADE_BIN;
69646
69734
  if (override && existsSync2(override)) return override;
69647
69735
  for (const profile of ["release", "debug"]) {
69648
- const candidate = join2(
69736
+ const candidate = join3(
69649
69737
  dirname2(mechevalRoot),
69650
69738
  "target",
69651
69739
  profile,
@@ -69665,13 +69753,13 @@ function listEvalTasks(args) {
69665
69753
  true
69666
69754
  );
69667
69755
  }
69668
- const tasksDir = join2(mechevalRoot, "tasks");
69756
+ const tasksDir = join3(mechevalRoot, "tasks");
69669
69757
  const suiteFilter = args.suite ? String(args.suite).toUpperCase() : null;
69670
69758
  const tasks = [];
69671
69759
  for (const file of readdirSync2(tasksDir).sort()) {
69672
69760
  if (!file.endsWith(".json")) continue;
69673
69761
  try {
69674
- const raw = JSON.parse(readFileSync3(join2(tasksDir, file), "utf8"));
69762
+ const raw = JSON.parse(readFileSync4(join3(tasksDir, file), "utf8"));
69675
69763
  if (suiteFilter && String(raw.suite).toUpperCase() !== suiteFilter) {
69676
69764
  continue;
69677
69765
  }
@@ -69705,7 +69793,7 @@ async function verifyPart(args) {
69705
69793
  if (!/^[A-Za-z0-9._-]+$/.test(taskId)) {
69706
69794
  return textResult({ error: `invalid task_id: ${taskId}` }, true);
69707
69795
  }
69708
- const taskPath = join2(mechevalRoot, "tasks", `${taskId}.json`);
69796
+ const taskPath = join3(mechevalRoot, "tasks", `${taskId}.json`);
69709
69797
  if (!existsSync2(taskPath)) {
69710
69798
  return textResult(
69711
69799
  {
@@ -69725,8 +69813,8 @@ async function verifyPart(args) {
69725
69813
  true
69726
69814
  );
69727
69815
  }
69728
- const scratch = mkdtempSync(join2(tmpdir(), "vcad-verify-"));
69729
- const vcadPath = join2(scratch, "candidate.vcad");
69816
+ const scratch = mkdtempSync(join3(tmpdir(), "vcad-verify-"));
69817
+ const vcadPath = join3(scratch, "candidate.vcad");
69730
69818
  try {
69731
69819
  writeFileSync4(vcadPath, JSON.stringify(doc));
69732
69820
  let stdout;
@@ -73813,7 +73901,7 @@ var init_acoustics = __esm({
73813
73901
  });
73814
73902
 
73815
73903
  // src/tools/import.ts
73816
- import { readFileSync as readFileSync4, existsSync as existsSync3, statSync } from "node:fs";
73904
+ import { readFileSync as readFileSync5, existsSync as existsSync3, statSync } from "node:fs";
73817
73905
  import { basename } from "node:path";
73818
73906
  function importStep(input, engine) {
73819
73907
  const { filename, content_base64, name, material } = input;
@@ -73846,7 +73934,7 @@ function importStep(input, engine) {
73846
73934
  if (stat.size > MAX_STEP_BYTES) {
73847
73935
  throw new Error(`STEP file exceeds ${MAX_STEP_BYTES} byte limit`);
73848
73936
  }
73849
- fileBuffer = readFileSync4(filepath);
73937
+ fileBuffer = readFileSync5(filepath);
73850
73938
  } else {
73851
73939
  throw new Error("Provide either `filename` or `content_base64`");
73852
73940
  }
@@ -73996,7 +74084,7 @@ var init_import = __esm({
73996
74084
  });
73997
74085
 
73998
74086
  // src/tools/import-pcb.ts
73999
- import { readFileSync as readFileSync5, existsSync as existsSync4, statSync as statSync2 } from "node:fs";
74087
+ import { readFileSync as readFileSync6, existsSync as existsSync4, statSync as statSync2 } from "node:fs";
74000
74088
  function importError(text) {
74001
74089
  return {
74002
74090
  content: [{ type: "text", text: JSON.stringify({ error: text }) }],
@@ -74029,7 +74117,7 @@ function readSource(input, label) {
74029
74117
  if (stat.size > MAX_PCB_BYTES) {
74030
74118
  throw new Error(`${label} file exceeds the ${MAX_PCB_BYTES} byte limit`);
74031
74119
  }
74032
- return readFileSync5(filepath, "utf8");
74120
+ return readFileSync6(filepath, "utf8");
74033
74121
  }
74034
74122
  throw new Error("Provide either `filename` or `content_base64`");
74035
74123
  }
@@ -77583,13 +77671,13 @@ ${issues.map((i) => i.problem).join("\n")}`
77583
77671
  }
77584
77672
  async function hasFfmpeg() {
77585
77673
  if (ffmpegAvailable !== null) return ffmpegAvailable;
77586
- ffmpegAvailable = await new Promise((resolve3) => {
77674
+ ffmpegAvailable = await new Promise((resolve4) => {
77587
77675
  try {
77588
77676
  const p = spawn("ffmpeg", ["-version"], { stdio: "ignore" });
77589
- p.on("error", () => resolve3(false));
77590
- p.on("exit", (code) => resolve3(code === 0));
77677
+ p.on("error", () => resolve4(false));
77678
+ p.on("exit", (code) => resolve4(code === 0));
77591
77679
  } catch {
77592
- resolve3(false);
77680
+ resolve4(false);
77593
77681
  }
77594
77682
  });
77595
77683
  return ffmpegAvailable;
@@ -77628,13 +77716,13 @@ function startMp4Encoder(width, height, fps) {
77628
77716
  const exitError = (code) => new Error(
77629
77717
  `ffmpeg exited ${code}: ${Buffer.concat(errChunks).toString().slice(-400)}`
77630
77718
  );
77631
- const done = new Promise((resolve3, reject) => {
77719
+ const done = new Promise((resolve4, reject) => {
77632
77720
  p.on("error", (e) => {
77633
77721
  failure = e;
77634
77722
  reject(e);
77635
77723
  });
77636
77724
  p.on("close", (code) => {
77637
- if (code === 0) resolve3(Buffer.concat(out));
77725
+ if (code === 0) resolve4(Buffer.concat(out));
77638
77726
  else {
77639
77727
  failure ??= exitError(code);
77640
77728
  reject(failure);
@@ -77645,15 +77733,15 @@ function startMp4Encoder(width, height, fps) {
77645
77733
  });
77646
77734
  return {
77647
77735
  writeFrame(rgba) {
77648
- return new Promise((resolve3, reject) => {
77736
+ return new Promise((resolve4, reject) => {
77649
77737
  if (failure) return reject(failure);
77650
77738
  if (p.exitCode !== null || p.stdin.destroyed) {
77651
77739
  return reject(failure ?? exitError(p.exitCode));
77652
77740
  }
77653
- if (p.stdin.write(rgba)) return resolve3();
77741
+ if (p.stdin.write(rgba)) return resolve4();
77654
77742
  const onDrain = () => {
77655
77743
  p.removeListener("close", onClose);
77656
- resolve3();
77744
+ resolve4();
77657
77745
  };
77658
77746
  const onClose = () => {
77659
77747
  p.stdin.removeListener("drain", onDrain);
@@ -79051,8 +79139,8 @@ var init_server3 = __esm({
79051
79139
  init_order_feed();
79052
79140
  init_animate();
79053
79141
  PKG_VERSION = (() => {
79054
- if ("0.9.4-main.29") {
79055
- return "0.9.4-main.29";
79142
+ if ("0.9.4-main.31") {
79143
+ return "0.9.4-main.31";
79056
79144
  }
79057
79145
  try {
79058
79146
  const req = createRequire2(import.meta.url);
@@ -79061,8 +79149,8 @@ var init_server3 = __esm({
79061
79149
  return "0.0.0";
79062
79150
  }
79063
79151
  })();
79064
- BUILD_SHA = "d80a65e336d1c8b8ebf75dd1bebc553b41f7ca93";
79065
- BUILD_TIME = "2026-07-26T00:45:54.095Z";
79152
+ BUILD_SHA = "ab04c2e57eeea3736ac3421fabbc01e89959f953";
79153
+ BUILD_TIME = "2026-07-26T01:25:19.323Z";
79066
79154
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
79067
79155
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
79068
79156
  INSTANCE_ID = randomUUID6().slice(0, 8);
@@ -79380,12 +79468,12 @@ var init_index = __esm({
79380
79468
 
79381
79469
  // src/npx-entry.ts
79382
79470
  init_dist();
79383
- import { readFileSync as readFileSync6 } from "node:fs";
79384
- import { dirname as dirname3, join as join3 } from "node:path";
79471
+ import { readFileSync as readFileSync7 } from "node:fs";
79472
+ import { dirname as dirname3, join as join4 } from "node:path";
79385
79473
  import { fileURLToPath as fileURLToPath3 } from "node:url";
79386
79474
  primeKernelWasm(
79387
- readFileSync6(
79388
- join3(
79475
+ readFileSync7(
79476
+ join4(
79389
79477
  dirname3(fileURLToPath3(import.meta.url)),
79390
79478
  "vcad_kernel_wasm_bg.wasm"
79391
79479
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcad/mcp",
3
- "version": "0.9.4-main.29",
3
+ "version": "0.9.4-main.31",
4
4
  "description": "vcad MCP server — parametric CAD + PCB design tools for AI agents (self-contained: bundled server + kernel WASM)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "homepage": "https://vcad.io",
21
21
  "vcadBuild": {
22
- "sha": "d80a65e336d1c8b8ebf75dd1bebc553b41f7ca93",
23
- "builtAt": "2026-07-26T00:45:54.095Z"
22
+ "sha": "ab04c2e57eeea3736ac3421fabbc01e89959f953",
23
+ "builtAt": "2026-07-26T01:25:19.323Z"
24
24
  }
25
25
  }
Binary file