@vcad/mcp 0.9.4-main.38 → 0.9.4-main.39

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 254d4e9d13825ac74a895590703959d77898e1f3 at 2026-07-26T13:42:07.736Z. Source: https://github.com/ecto/vcad
9
+ Built from 08cd95d4487b522463d0a1a3b58b84935a1f7d08 at 2026-07-26T13:56:33.334Z. Source: https://github.com/ecto/vcad
package/index.mjs CHANGED
@@ -177,6 +177,7 @@ __export(vcad_kernel_wasm_exports, {
177
177
  exprEvaluate: () => exprEvaluate,
178
178
  exprParse: () => exprParse,
179
179
  feaAnalyzeMesh: () => feaAnalyzeMesh,
180
+ feaCheckBeam: () => feaCheckBeam,
180
181
  generate3mf: () => generate3mf,
181
182
  generate3mfWithGcode: () => generate3mfWithGcode,
182
183
  generateGcode: () => generateGcode,
@@ -1925,6 +1926,15 @@ function feaAnalyzeMesh(spec_json, options_json, positions, indices) {
1925
1926
  }
1926
1927
  return takeFromExternrefTable0(ret[0]);
1927
1928
  }
1929
+ function feaCheckBeam(case_json) {
1930
+ const ptr0 = passStringToWasm0(case_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1931
+ const len0 = WASM_VECTOR_LEN;
1932
+ const ret = wasm.feaCheckBeam(ptr0, len0);
1933
+ if (ret[2]) {
1934
+ throw takeFromExternrefTable0(ret[1]);
1935
+ }
1936
+ return takeFromExternrefTable0(ret[0]);
1937
+ }
1928
1938
  function generate3mf(name, vertices, indices, settings_json) {
1929
1939
  const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1930
1940
  const len0 = WASM_VECTOR_LEN;
@@ -11526,6 +11536,7 @@ var init_dist = __esm({
11526
11536
  evaluateDocument: wasmModule7.evaluateDocument,
11527
11537
  evalVcadSource: wasmModule7.evalVcadSource,
11528
11538
  evalVcadSourceWithModules: wasmModule7.evalVcadSourceWithModules,
11539
+ evalVcadSourceParametric: wasmModule7.evalVcadSourceParametric,
11529
11540
  documentParameterGradient: wasmModule7.documentParameterGradient,
11530
11541
  getPartsManifest: wasmModule7.getPartsManifest,
11531
11542
  buildPart: wasmModule7.buildPart,
@@ -12230,6 +12241,22 @@ var init_dist = __esm({
12230
12241
  const json = this.kernel.evalVcadSourceWithModules(source, JSON.stringify(modules));
12231
12242
  return JSON.parse(json);
12232
12243
  }
12244
+ /**
12245
+ * Evaluate loon source, returning the document alongside any parametric
12246
+ * warnings — intent the bridge could *not* preserve, such as a declared
12247
+ * parameter that ends up driving no geometry, or a field whose dependence
12248
+ * on a parameter is not affine and so keeps its literal.
12249
+ *
12250
+ * The document is identical to {@link evalVcadSourceWithModules}; only the
12251
+ * authoring feedback is extra. Returns null on kernels predating the
12252
+ * parametric loon forms, so callers can fall back.
12253
+ */
12254
+ evalVcadSourceParametric(source, modules = {}) {
12255
+ if (!this.kernel.evalVcadSourceParametric)
12256
+ return null;
12257
+ const json = this.kernel.evalVcadSourceParametric(source, Object.keys(modules).length ? JSON.stringify(modules) : void 0);
12258
+ return JSON.parse(json);
12259
+ }
12233
12260
  /** Evaluate a preview extrusion without adding to document */
12234
12261
  evaluateExtrudePreview(origin, xDir, yDir, segments, direction) {
12235
12262
  if (segments.length === 0)
@@ -40963,6 +40990,26 @@ var init_CHANGELOG = __esm({
40963
40990
  "rendering"
40964
40991
  ]
40965
40992
  },
40993
+ {
40994
+ id: "2026-07-26-parametric-loon-datums",
40995
+ version: "0.9.4",
40996
+ date: "2026-07-26",
40997
+ category: "feat",
40998
+ title: "Named parameters, datums and stacks in loon",
40999
+ summary: "Values named with [defparam] survive into the document and are settable with set_parameters; [datum-plane]/[stack] make shared planes and packing clearances named entities instead of repeated literals.",
41000
+ features: [
41001
+ "loon",
41002
+ "parameters",
41003
+ "datums",
41004
+ "assemblies"
41005
+ ],
41006
+ mcpTools: [
41007
+ "create_cad_loon",
41008
+ "list_parameters",
41009
+ "set_parameters",
41010
+ "parameter_gradient"
41011
+ ]
41012
+ },
40966
41013
  {
40967
41014
  id: "2026-07-26-loon-mirror-symmetry",
40968
41015
  version: "0.9.4",
@@ -48729,9 +48776,45 @@ function createCadLoon(input, engine) {
48729
48776
  return { content: [{ type: "text", text: text2 }] };
48730
48777
  }
48731
48778
  const text = format === "json" ? JSON.stringify(doc, null, 2) : toVCode2(doc);
48732
- return {
48733
- content: [{ type: "text", text }]
48734
- };
48779
+ const content = [{ type: "text", text }];
48780
+ const note = parametricNote(source, modules, engine, doc);
48781
+ if (note) content.push({ type: "text", text: note });
48782
+ return { content };
48783
+ }
48784
+ function parametricNote(source, modules, engine, doc) {
48785
+ const names = Object.keys(doc.parameters ?? {}).sort();
48786
+ if (!names.length) return null;
48787
+ const lines = [];
48788
+ const value = (n) => doc.parameters[n].value;
48789
+ const bound = new Set(
48790
+ Object.values(doc.bindings ?? {}).flatMap(
48791
+ (expr) => typeof expr === "string" ? expr.match(/[A-Za-z_]\w*/g) ?? [] : []
48792
+ )
48793
+ );
48794
+ const base = names.filter((n) => typeof value(n) === "number");
48795
+ const derived = names.filter((n) => typeof value(n) === "string");
48796
+ if (base.length) {
48797
+ lines.push(
48798
+ `Parameters (${base.length}, settable): ${base.map((n) => bound.has(n) ? n : `${n} (drives nothing)`).join(", ")}`
48799
+ );
48800
+ }
48801
+ if (derived.length) {
48802
+ lines.push(
48803
+ `Derived (${derived.length}, follow from the above): ${derived.map((n) => `${n} = "${value(n)}"`).join(", ")}`
48804
+ );
48805
+ }
48806
+ const datums = Object.keys(doc.datums ?? {}).sort();
48807
+ if (datums.length) lines.push(`Datums (${datums.length}): ${datums.join(", ")}`);
48808
+ if (base.length) {
48809
+ lines.push("Change any settable parameter with set_parameters \u2014 no re-authoring needed.");
48810
+ }
48811
+ let warnings = [];
48812
+ try {
48813
+ warnings = engine.evalVcadSourceParametric(source, modules)?.warnings ?? [];
48814
+ } catch {
48815
+ }
48816
+ for (const w of warnings) lines.push(`- ${w}`);
48817
+ return lines.join("\n");
48735
48818
  }
48736
48819
  var createCadLoonSchema, toolDefs2;
48737
48820
  var init_loon = __esm({
@@ -48793,7 +48876,7 @@ var init_loon = __esm({
48793
48876
  {
48794
48877
  name: "create_cad_loon",
48795
48878
  pack: null,
48796
- 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], [torus major-r minor-r], [wedge x y z], [prism sides radius height]\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], [mirror ox oy oz nx ny nz s] (plane through the origin point with that normal) \u2014 plus the axis sugar [mirror-x s] / [mirror-y s] / [mirror-z s], which mirror through the origin, negating that one coordinate. NEVER hand-mirror by negating coordinates; use these.\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]\nSymmetric patterns (subject-last): [mirror-pattern nx ny nz s] and its sugar [mirror-pattern-x s] / -y / -z union a solid with its mirror image (a left/right pair in one expression, so the halves can\'t drift apart); [quad-pattern s] is the 4-fold X-and-Y version \u2014 a quadruped\'s legs, a 4-post frame, a vehicle chassis\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]], [loft-closed #[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]\nAssembly symmetry: author ONE side as an assembly, then [mirror-group-x "-r" side] (or -y / -z) returns it plus a mirrored, suffixed copy \u2014 parts reflected, placements and joint anchors mirrored, and joint axes flipped by the correct rule (a hinge across its own mirror normal keeps its axis; the other two flip), so the same joint state drives both sides symmetrically. Splice it back with [assembly-join chassis mirrored]. NEVER hand-mirror an assembly.\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"], with [material name r g b metallic roughness] to define one\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',
48879
+ 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], [torus major-r minor-r], [wedge x y z], [prism sides radius height]\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], [mirror ox oy oz nx ny nz s] (plane through the origin point with that normal) \u2014 plus the axis sugar [mirror-x s] / [mirror-y s] / [mirror-z s], which mirror through the origin, negating that one coordinate. NEVER hand-mirror by negating coordinates; use these.\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]\nSymmetric patterns (subject-last): [mirror-pattern nx ny nz s] and its sugar [mirror-pattern-x s] / -y / -z union a solid with its mirror image (a left/right pair in one expression, so the halves can\'t drift apart); [quad-pattern s] is the 4-fold X-and-Y version \u2014 a quadruped\'s legs, a 4-post frame, a vehicle chassis\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]], [loft-closed #[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]\nAssembly symmetry: author ONE side as an assembly, then [mirror-group-x "-r" side] (or -y / -z) returns it plus a mirrored, suffixed copy \u2014 parts reflected, placements and joint anchors mirrored, and joint axes flipped by the correct rule (a hinge across its own mirror normal keeps its axis; the other two flip), so the same joint state drives both sides symmetrically. Splice it back with [assembly-join chassis mirrored]. NEVER hand-mirror an assembly.\nPipe: [pipe [cube 50 30 5] [difference [cylinder 3 10]] [fillet 1.0]]\nLet bindings: [let body [cube 50 30 5]] \u2014 note these are inlined and do NOT survive into the document; use [defparam ...] for a value you want to change later\nParameters (survive into the document and are settable afterwards with set_parameters, and differentiable with parameter_gradient): [defparam pitch_axis_x 310.0], [defparam wall "bore * 0.2"] for derived values, plus optional :unit/:min/:max/:description. Names must be identifier-safe (underscores, not dashes)\nDatums \u2014 named reference geometry, so two parts cannot each hold their own copy of a shared plane: [datum-plane "femur_inner" y 131.0], [datum-axis "pitch" x 0 0 310], [datum-point "hip" 0 0 310], read back with [datum "femur_inner"], [datum+ "femur_inner" 3.0] (3 mm outboard), [datum-x/-y/-z "pitch"]\nStacks \u2014 declarative packing, where each running clearance is a named value instead of an arbitrary number: [stack y "leg" 131.0 [lane "femur_inner" 5.0] [gap "idler_run" 1.0] [lane "idler_boss" 3.0]] declares datum planes leg_femur_inner_lo/_hi, leg_idler_boss_lo/_hi and leg_end; widening leg_idler_run slides everything outboard of it\nScene: [root solid "material-name"], with [material name r g b metallic roughness] to define one\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',
48797
48880
  inputSchema: createCadLoonSchema,
48798
48881
  handler: async (args, ctx) => {
48799
48882
  const useLoons = Array.isArray(args.use_loons) ? args.use_loons : void 0;
@@ -58029,6 +58112,18 @@ function listParameters(input) {
58029
58112
  );
58030
58113
  return jsonResult({ count: parameters.length, parameters });
58031
58114
  }
58115
+ function referencedNames(doc) {
58116
+ const out = /* @__PURE__ */ new Set();
58117
+ const harvest = (expr) => {
58118
+ if (typeof expr !== "string") return;
58119
+ for (const v of expr.match(/[A-Za-z_]\w*/g) ?? []) out.add(v);
58120
+ };
58121
+ for (const expr of Object.values(doc.bindings ?? {})) harvest(expr);
58122
+ for (const c of doc.constraints ?? []) {
58123
+ for (const v of Object.values(c)) harvest(v);
58124
+ }
58125
+ return out;
58126
+ }
58032
58127
  async function setParameters(input, engine) {
58033
58128
  const args = input ?? {};
58034
58129
  const documentId = String(args.document_id ?? "");
@@ -58058,6 +58153,18 @@ async function setParameters(input, engine) {
58058
58153
  `Unknown parameter(s): ${unknown2.join(", ")}. Use list_parameters to see declared names.`
58059
58154
  );
58060
58155
  }
58156
+ const referenced = referencedNames(doc);
58157
+ const noEffect = entries.map(([name]) => name).filter((name) => !referenced.has(name)).map((name) => {
58158
+ const value = doc.parameters[name].value;
58159
+ const inputs = typeof value === "string" ? [...new Set(value.match(/[A-Za-z_]\w*/g) ?? [])].filter(
58160
+ (v) => v in doc.parameters && referenced.has(v)
58161
+ ) : [];
58162
+ return {
58163
+ name,
58164
+ reason: typeof value === "string" ? `derived (${JSON.stringify(value)}) \u2014 no field is bound to it` : "no field is bound to it",
58165
+ ...inputs.length ? { set_instead: inputs } : {}
58166
+ };
58167
+ });
58061
58168
  const changed = [];
58062
58169
  for (const [name, value] of entries) {
58063
58170
  const param = doc.parameters[name];
@@ -58079,6 +58186,7 @@ async function setParameters(input, engine) {
58079
58186
  document_id: documentId,
58080
58187
  updated: changed.length,
58081
58188
  changed,
58189
+ ...noEffect.length ? { no_effect: noEffect } : {},
58082
58190
  ...constraintSolve ? { constraint_solve: constraintSolve } : {}
58083
58191
  });
58084
58192
  result.structuredContent = { changed };
@@ -80215,8 +80323,8 @@ var init_server3 = __esm({
80215
80323
  init_order_feed();
80216
80324
  init_animate();
80217
80325
  PKG_VERSION = (() => {
80218
- if ("0.9.4-main.38") {
80219
- return "0.9.4-main.38";
80326
+ if ("0.9.4-main.39") {
80327
+ return "0.9.4-main.39";
80220
80328
  }
80221
80329
  try {
80222
80330
  const req = createRequire2(import.meta.url);
@@ -80225,8 +80333,8 @@ var init_server3 = __esm({
80225
80333
  return "0.0.0";
80226
80334
  }
80227
80335
  })();
80228
- BUILD_SHA = "254d4e9d13825ac74a895590703959d77898e1f3";
80229
- BUILD_TIME = "2026-07-26T13:42:07.736Z";
80336
+ BUILD_SHA = "08cd95d4487b522463d0a1a3b58b84935a1f7d08";
80337
+ BUILD_TIME = "2026-07-26T13:56:33.334Z";
80230
80338
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
80231
80339
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
80232
80340
  INSTANCE_ID = randomUUID6().slice(0, 8);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcad/mcp",
3
- "version": "0.9.4-main.38",
3
+ "version": "0.9.4-main.39",
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": "254d4e9d13825ac74a895590703959d77898e1f3",
23
- "builtAt": "2026-07-26T13:42:07.736Z"
22
+ "sha": "08cd95d4487b522463d0a1a3b58b84935a1f7d08",
23
+ "builtAt": "2026-07-26T13:56:33.334Z"
24
24
  }
25
25
  }
Binary file