@vcad/mcp 0.10.0-main.115 → 0.10.0-main.117

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 8ad12f403ad86c964867f288c8ef53b7790be8c8 at 2026-10-20T23:56:43.258Z. Source: https://github.com/ecto/vcad
9
+ Built from 8c6f34bb566075c1bcec315c497a5c29fac2b820 at 2026-10-21T00:57:52.178Z. Source: https://github.com/ecto/vcad
package/index.mjs CHANGED
@@ -40941,24 +40941,6 @@ var init_registry2 = __esm({
40941
40941
  setWasm(wasm3) {
40942
40942
  this.wasm = wasm3;
40943
40943
  }
40944
- /**
40945
- * Plan a CRUD tool call via the Rust executor. Returns `null` if the
40946
- * wasm binding isn't present (old kernel build, test harness, etc.)
40947
- * so callers can fall back to the TS `executeCrud` path.
40948
- *
40949
- * `docJson` is a `JSON.stringify(document)` of the current store
40950
- * snapshot — the planner needs it for id/existence validation.
40951
- */
40952
- planCrud(tool, args, docJson) {
40953
- if (!this.wasm?.plan_chat_tool)
40954
- return null;
40955
- try {
40956
- const raw = this.wasm.plan_chat_tool(tool, JSON.stringify(args), docJson);
40957
- return JSON.parse(raw);
40958
- } catch {
40959
- return null;
40960
- }
40961
- }
40962
40944
  /** Get all loaded schema entries. */
40963
40945
  getSchemas() {
40964
40946
  return this.schemas;
@@ -41539,20 +41521,6 @@ function partIdMap(doc) {
41539
41521
  const copy = { roots: doc.roots, partIds: { ...doc.partIds ?? {} } };
41540
41522
  return ensurePartIds(copy).partIds;
41541
41523
  }
41542
- function movePartId(doc, oldRoot, newRoot) {
41543
- const map = idMap(doc);
41544
- const from = String(oldRoot);
41545
- const to = String(newRoot);
41546
- if (from === to)
41547
- return;
41548
- const id = map[from] ?? mint(map, newRoot);
41549
- delete map[from];
41550
- map[to] = id;
41551
- }
41552
- function forgetPartId(doc, root) {
41553
- if (doc.partIds)
41554
- delete doc.partIds[String(root)];
41555
- }
41556
41524
  function rootForPartId(doc, id) {
41557
41525
  const key3 = String(id).trim();
41558
41526
  if (!key3)
@@ -41577,9 +41545,6 @@ function rootForPartId(doc, id) {
41577
41545
  return done(byName, "name");
41578
41546
  return void 0;
41579
41547
  }
41580
- function rootIndexForPartId(doc, id) {
41581
- return rootForPartId(doc, id)?.index ?? -1;
41582
- }
41583
41548
  function partIdDeprecation(input, resolved) {
41584
41549
  if (!resolved || resolved.matchedBy === "stable")
41585
41550
  return void 0;
@@ -41595,150 +41560,6 @@ var init_part_ids = __esm({
41595
41560
  });
41596
41561
 
41597
41562
  // ../core/dist/commands/document-mutations.js
41598
- function nextNodeId(doc) {
41599
- let max = 0;
41600
- for (const k of Object.keys(doc.nodes)) {
41601
- const n = Number(k);
41602
- if (Number.isFinite(n) && n > max)
41603
- max = n;
41604
- }
41605
- return max + 1;
41606
- }
41607
- function findRootIndex(doc, partId) {
41608
- return rootIndexForPartId(doc, partId);
41609
- }
41610
- function collectChildNodeIds(doc, rootId) {
41611
- const seen = /* @__PURE__ */ new Set();
41612
- const stack = [rootId];
41613
- while (stack.length > 0) {
41614
- const id = stack.pop();
41615
- if (seen.has(id))
41616
- continue;
41617
- seen.add(id);
41618
- const node = doc.nodes[String(id)];
41619
- if (!node)
41620
- continue;
41621
- for (const child of opChildren(node.op))
41622
- stack.push(child);
41623
- }
41624
- return seen;
41625
- }
41626
- function opChildren(op) {
41627
- switch (op.type) {
41628
- case "Union":
41629
- case "Difference":
41630
- case "Intersection":
41631
- return [op.left, op.right];
41632
- case "Translate":
41633
- case "Rotate":
41634
- case "Scale":
41635
- case "LinearPattern":
41636
- case "CircularPattern":
41637
- case "Shell":
41638
- case "Fillet":
41639
- case "Chamfer":
41640
- case "EdgeBlend":
41641
- return [op.child];
41642
- case "Extrude":
41643
- case "Revolve":
41644
- return [op.sketch];
41645
- case "Sweep":
41646
- return [op.sketch];
41647
- case "Loft":
41648
- return op.sketches ?? [];
41649
- default:
41650
- return [];
41651
- }
41652
- }
41653
- function applyToolOutcome(doc, outcome) {
41654
- switch (outcome.kind) {
41655
- case "add_feature": {
41656
- const id = nextNodeId(doc);
41657
- const node = {
41658
- id,
41659
- name: outcome.name ?? null,
41660
- op: outcome.op
41661
- };
41662
- doc.nodes[String(id)] = node;
41663
- const children = new Set(opChildren(node.op));
41664
- const consumedIdxs = [];
41665
- doc.roots.forEach((r, i) => {
41666
- if (children.has(r.root))
41667
- consumedIdxs.push(i);
41668
- });
41669
- if (consumedIdxs.length > 0) {
41670
- const first = doc.roots[consumedIdxs[0]];
41671
- const firstPartId = String(first.root);
41672
- doc.roots[consumedIdxs[0]] = { ...first, root: id };
41673
- movePartId(doc, first.root, id);
41674
- if (doc.part_materials[firstPartId] !== void 0) {
41675
- doc.part_materials[String(id)] = doc.part_materials[firstPartId];
41676
- delete doc.part_materials[firstPartId];
41677
- }
41678
- for (let k = consumedIdxs.length - 1; k >= 1; k--) {
41679
- const idx = consumedIdxs[k];
41680
- delete doc.part_materials[String(doc.roots[idx].root)];
41681
- forgetPartId(doc, doc.roots[idx].root);
41682
- doc.roots.splice(idx, 1);
41683
- }
41684
- } else {
41685
- const entry = { root: id, material: "default" };
41686
- doc.roots.push(entry);
41687
- }
41688
- return { partId: partIdForNode(doc, id), nodeId: String(id) };
41689
- }
41690
- case "remove_part": {
41691
- const idx = findRootIndex(doc, outcome.part_id);
41692
- if (idx < 0) {
41693
- throw new Error(`remove_part: no root entry with id "${outcome.part_id}" in document`);
41694
- }
41695
- const entry = doc.roots[idx];
41696
- const removedId = partIdForRoot(doc, entry);
41697
- doc.roots.splice(idx, 1);
41698
- forgetPartId(doc, entry.root);
41699
- const reachableFromOthers = /* @__PURE__ */ new Set();
41700
- for (const r of doc.roots) {
41701
- for (const id of collectChildNodeIds(doc, r.root)) {
41702
- reachableFromOthers.add(id);
41703
- }
41704
- }
41705
- const exclusive = collectChildNodeIds(doc, entry.root);
41706
- for (const id of exclusive) {
41707
- if (!reachableFromOthers.has(id))
41708
- delete doc.nodes[String(id)];
41709
- }
41710
- delete doc.part_materials[String(entry.root)];
41711
- return { partId: removedId };
41712
- }
41713
- case "set_part_material": {
41714
- const idx = findRootIndex(doc, outcome.part_id);
41715
- if (idx < 0) {
41716
- throw new Error(`set_part_material: no root entry with id "${outcome.part_id}"`);
41717
- }
41718
- const prev = doc.roots[idx];
41719
- doc.roots[idx] = { root: prev.root, material: outcome.material, visible: prev.visible };
41720
- doc.part_materials[String(prev.root)] = outcome.material;
41721
- return { partId: partIdForRoot(doc, prev) };
41722
- }
41723
- case "update_params": {
41724
- const node = doc.nodes[outcome.node_id];
41725
- if (!node) {
41726
- throw new Error(`update_params: no node with id "${outcome.node_id}" in document`);
41727
- }
41728
- const op = node.op;
41729
- for (const [k, v] of Object.entries(outcome.params)) {
41730
- if (k === "type")
41731
- continue;
41732
- op[k] = v;
41733
- }
41734
- const owner = doc.roots.find((r) => collectChildNodeIds(doc, r.root).has(Number(outcome.node_id)));
41735
- return {
41736
- partId: owner ? partIdForRoot(doc, owner) : outcome.node_id,
41737
- nodeId: outcome.node_id
41738
- };
41739
- }
41740
- }
41741
- }
41742
41563
  function listPartsFromDocument(doc) {
41743
41564
  return doc.roots.map((entry) => {
41744
41565
  const node = doc.nodes[String(entry.root)];
@@ -42132,6 +41953,39 @@ var init_CHANGELOG = __esm({
42132
41953
  "apply_edits"
42133
41954
  ]
42134
41955
  },
41956
+ {
41957
+ id: "2026-10-20-mcp-apply-edits-engine",
41958
+ version: "0.10.0",
41959
+ date: "2026-10-20",
41960
+ category: "fix",
41961
+ title: "apply_edits runs on the document engine",
41962
+ summary: `MCP apply_edits now applies every op through the Rust document engine, like the single-op tools; "@N" resolves to op N's stable part id. The legacy planner fallback is gone.`,
41963
+ features: [
41964
+ "mcp",
41965
+ "engine",
41966
+ "batch-edit"
41967
+ ],
41968
+ mcpTools: [
41969
+ "apply_edits",
41970
+ "create",
41971
+ "update",
41972
+ "delete",
41973
+ "set_material"
41974
+ ]
41975
+ },
41976
+ {
41977
+ id: "2026-10-20-mac-editor-engine",
41978
+ version: "0.10.0",
41979
+ date: "2026-10-20",
41980
+ category: "feat",
41981
+ title: "Mac app edits documents through the shared engine",
41982
+ summary: "The native Mac editor now sends add, delete, combine, material, rename, transform and undo/redo through the Rust document engine, the same one the web app uses.",
41983
+ features: [
41984
+ "native-app",
41985
+ "document-engine",
41986
+ "undo"
41987
+ ]
41988
+ },
42135
41989
  {
42136
41990
  id: "2026-10-20-lossless-import",
42137
41991
  version: "undefined",
@@ -42271,6 +42125,20 @@ var init_CHANGELOG = __esm({
42271
42125
  "documents"
42272
42126
  ]
42273
42127
  },
42128
+ {
42129
+ id: "2026-10-20-cli-photoreal-render",
42130
+ version: "0.10.0",
42131
+ date: "2026-10-20",
42132
+ category: "feat",
42133
+ title: "Headless photoreal rendering in the vcad CLI",
42134
+ summary: "vcad render --photoreal path-traces .loon/.vcad files on a headless GPU with the app's materials, lighting and tonemap; presets, --camera, --size, --samples, --cpu fallback.",
42135
+ features: [
42136
+ "render",
42137
+ "photoreal",
42138
+ "cli",
42139
+ "path-tracing"
42140
+ ]
42141
+ },
42274
42142
  {
42275
42143
  id: "2026-10-20-ai-update-targets-owner",
42276
42144
  version: "undefined",
@@ -55894,23 +55762,31 @@ function engineErrorMessage(toolName, args, err9) {
55894
55762
  }
55895
55763
  }
55896
55764
  function runMutationViaEngine(toolName, toolArgs, doc, documentId) {
55897
- if (!ENGINE_LOWERED_TOOLS.has(toolName)) return null;
55898
- if (process.env.VCAD_MCP_ENGINE_APPLY === "0") return null;
55765
+ if (!ENGINE_LOWERED_TOOLS.has(toolName)) {
55766
+ throw new Error(`Tool "${toolName}" is not lowered by the document engine.`);
55767
+ }
55899
55768
  const wasm3 = getKernelWasmSync();
55900
55769
  const factory = wasm3?.WasmDocumentEngine;
55901
- if (!factory || typeof factory.withReplica !== "function") return null;
55770
+ if (!factory || typeof factory.withReplica !== "function") {
55771
+ throw new Error(
55772
+ `${toolName}: kernel WASM has no WasmDocumentEngine \u2014 ensure the kernel is initialized and up to date.`
55773
+ );
55774
+ }
55902
55775
  const cachedEngine = takeCachedEngine(documentId, doc);
55903
55776
  const engine = cachedEngine ?? factory.withReplica(0);
55904
55777
  let keep = false;
55905
55778
  try {
55906
- if (typeof engine.apply_tool !== "function") return null;
55779
+ if (typeof engine.apply_tool !== "function") {
55780
+ throw new Error(
55781
+ `${toolName}: kernel WASM predates WasmDocumentEngine.apply_tool \u2014 rebuild the kernel WASM.`
55782
+ );
55783
+ }
55907
55784
  const deprecation = typeof toolArgs.part_id === "string" ? partIdDeprecation(toolArgs.part_id, rootForPartId(doc, toolArgs.part_id)) : void 0;
55908
55785
  const args = engineArgs(toolName, toolArgs, doc);
55909
55786
  if (!cachedEngine) engine.import_ir(JSON.stringify(doc));
55910
55787
  const envelope = JSON.parse(engine.apply_tool(toolName, JSON.stringify(args)));
55911
55788
  if (!envelope.ok || !envelope.result) {
55912
55789
  const err9 = envelope.error ?? { kind: "invalid", message: "engine returned no result" };
55913
- if (err9.kind === "unsupported") return null;
55914
55790
  throw new Error(engineErrorMessage(toolName, args, err9));
55915
55791
  }
55916
55792
  const next = envelope.result.document;
@@ -55933,7 +55809,9 @@ function runMutationViaEngine(toolName, toolArgs, doc, documentId) {
55933
55809
  payload.part_id = args.part_id ?? args.part_ids;
55934
55810
  payload.material = toolArgs.material;
55935
55811
  } else {
55936
- const partId = envelope.result.createdFeatureId ?? (typeof args.part_id === "string" ? args.part_id : void 0);
55812
+ const params = args.params;
55813
+ const edited = args.part_id ?? args.child ?? params?.child;
55814
+ const partId = envelope.result.createdFeatureId ?? (typeof edited === "string" ? edited : void 0);
55937
55815
  if (partId) {
55938
55816
  payload.part_id = partId;
55939
55817
  const part = parts.find((p) => p.id === partId);
@@ -55951,102 +55829,9 @@ function runMutationViaEngine(toolName, toolArgs, doc, documentId) {
55951
55829
  if (!keep) engine.free?.();
55952
55830
  }
55953
55831
  }
55954
- function runMutation(toolName, rawArgs, doc, documentId, opts = {}) {
55832
+ function runMutation(toolName, rawArgs, doc, documentId) {
55955
55833
  const toolArgs = normalizeNodeRefs(toolName, rawArgs, doc);
55956
- if (opts.engine !== false) {
55957
- const viaEngine = runMutationViaEngine(toolName, toolArgs, doc, documentId);
55958
- if (viaEngine) return viaEngine;
55959
- }
55960
- if (documentId) markDocumentWritten(documentId);
55961
- if (toolName === "delete") {
55962
- const partId = String(toolArgs.part_id ?? "");
55963
- if (!partId) throw new Error("delete: missing `part_id`");
55964
- const deprecation = partIdDeprecation(partId, rootForPartId(doc, partId));
55965
- const applied2 = applyToolOutcome(doc, {
55966
- kind: "remove_part",
55967
- part_id: partId
55968
- });
55969
- return {
55970
- content: [
55971
- {
55972
- type: "text",
55973
- text: JSON.stringify({
55974
- document_id: documentId,
55975
- removed: applied2.partId,
55976
- ...deprecation ? { warnings: [deprecation] } : {}
55977
- })
55978
- }
55979
- ]
55980
- };
55981
- }
55982
- if (toolName === "set_material") {
55983
- const partId = String(toolArgs.part_id ?? "");
55984
- const material = String(
55985
- toolArgs.material ?? ""
55986
- );
55987
- if (!partId) throw new Error("set_material: missing `part_id` (bulk forms not yet supported via MCP)");
55988
- if (!material) throw new Error("set_material: missing `material`");
55989
- const deprecation = partIdDeprecation(partId, rootForPartId(doc, partId));
55990
- const applied2 = applyToolOutcome(doc, {
55991
- kind: "set_part_material",
55992
- part_id: partId,
55993
- material
55994
- });
55995
- return {
55996
- content: [
55997
- {
55998
- type: "text",
55999
- text: JSON.stringify({
56000
- document_id: documentId,
56001
- part_id: applied2.partId,
56002
- material,
56003
- ...deprecation ? { warnings: [deprecation] } : {}
56004
- })
56005
- }
56006
- ]
56007
- };
56008
- }
56009
- const plannerArgs = toolName === "create" ? applyParamDefaults(toolArgs) : toolArgs;
56010
- const planned = commandRegistry.planCrud(
56011
- toolName,
56012
- plannerArgs,
56013
- JSON.stringify(doc)
56014
- );
56015
- if (!planned) {
56016
- throw new Error(
56017
- `Tool "${toolName}" \u2014 Rust planner unavailable. Ensure commandRegistry.setWasm() has been called during MCP server boot.`
56018
- );
56019
- }
56020
- if (planned.status === "error") {
56021
- throw new Error(enrichPlannerError(toolName, plannerArgs, planned.result));
56022
- }
56023
- if (!planned.outcome) {
56024
- return {
56025
- content: [
56026
- {
56027
- type: "text",
56028
- text: JSON.stringify({
56029
- document_id: documentId,
56030
- result: planned.result
56031
- })
56032
- }
56033
- ]
56034
- };
56035
- }
56036
- const applied = applyToolOutcome(doc, planned.outcome);
56037
- return {
56038
- content: [
56039
- {
56040
- type: "text",
56041
- text: JSON.stringify({
56042
- document_id: documentId,
56043
- result: planned.result,
56044
- part_id: applied.partId,
56045
- node_id: applied.nodeId
56046
- })
56047
- }
56048
- ]
56049
- };
55834
+ return runMutationViaEngine(toolName, toolArgs, doc, documentId);
56050
55835
  }
56051
55836
  function handleMeasureRead(toolName, doc, args, engine, pose) {
56052
55837
  if (!engine) {
@@ -58248,7 +58033,7 @@ function normalizeOp(op, index) {
58248
58033
  }
58249
58034
  return { name, args };
58250
58035
  }
58251
- function resolveSymbolicRefs(args, createdIds, index, doc) {
58036
+ function resolveSymbolicRefs(args, createdIds, index) {
58252
58037
  const resolve7 = (v) => {
58253
58038
  if (typeof v !== "string") return v;
58254
58039
  const m = SYMBOLIC_REF.exec(v);
@@ -58262,7 +58047,7 @@ function resolveSymbolicRefs(args, createdIds, index, doc) {
58262
58047
  const id = createdIds[refIdx];
58263
58048
  if (id === void 0) {
58264
58049
  throw new Error(
58265
- `"@${refIdx}" refers to op ${refIdx}, which did not create a node`
58050
+ `"@${refIdx}" refers to op ${refIdx}, which did not create a part`
58266
58051
  );
58267
58052
  }
58268
58053
  return id;
@@ -58271,12 +58056,6 @@ function resolveSymbolicRefs(args, createdIds, index, doc) {
58271
58056
  for (const key3 of ["part_id", "node_id"]) {
58272
58057
  if (key3 in out) out[key3] = resolve7(out[key3]);
58273
58058
  }
58274
- if (typeof args.part_id === "string" && SYMBOLIC_REF.test(args.part_id)) {
58275
- const nodeId = String(out.part_id);
58276
- if (doc.roots.some((r) => String(r.root) === nodeId)) {
58277
- out.part_id = partIdForNode(doc, nodeId);
58278
- }
58279
- }
58280
58059
  if (out.params && typeof out.params === "object" && !Array.isArray(out.params)) {
58281
58060
  const params = { ...out.params };
58282
58061
  for (const key3 of ["child", "left", "right", "sketch"]) {
@@ -58298,12 +58077,9 @@ function applyOps(doc, ops, documentId) {
58298
58077
  try {
58299
58078
  result = runMutation(
58300
58079
  name,
58301
- resolveSymbolicRefs(args, createdIds, i, doc),
58080
+ resolveSymbolicRefs(args, createdIds, i),
58302
58081
  doc,
58303
- documentId,
58304
- // Legacy planner: "@N" and raw numeric child refs address the
58305
- // planner's node numbering (see runMutation).
58306
- { engine: false }
58082
+ documentId
58307
58083
  );
58308
58084
  } catch (err9) {
58309
58085
  throw new BatchOpError(i, name, err9);
@@ -58317,7 +58093,7 @@ function applyOps(doc, ops, documentId) {
58317
58093
  const { document_id: _docId, ...rest } = parsed;
58318
58094
  void _docId;
58319
58095
  createdIds.push(
58320
- name === "create" && typeof rest.node_id === "string" ? rest.node_id : void 0
58096
+ name === "create" && typeof rest.part_id === "string" ? rest.part_id : void 0
58321
58097
  );
58322
58098
  results.push({ index: i, op: name, ...rest });
58323
58099
  }
@@ -58405,7 +58181,6 @@ var init_batch_edit = __esm({
58405
58181
  init_registry_dispatch();
58406
58182
  init_integrity();
58407
58183
  init_tool_def();
58408
- init_part_ids2();
58409
58184
  ALLOWED_OPS = ["create", "update", "delete", "set_material"];
58410
58185
  MAX_BATCH_OPS = 50;
58411
58186
  applyEditsSchema = {
@@ -58417,7 +58192,7 @@ var init_batch_edit = __esm({
58417
58192
  },
58418
58193
  ops: {
58419
58194
  type: "array",
58420
- description: `Ordered list of edit ops applied atomically (all-or-nothing). Each op is \`{op, ...args}\` where op is one of create/update/delete/set_material and the remaining fields are that tool's arguments (minus document_id): create {op, type, params}, update {op, node_id|part_id, ...fields}, delete {op, part_id}, set_material {op, part_id, material}. Later ops may reference nodes created by earlier ops in the same batch symbolically: "@N" (in params.child/left/right/sketch or part_id/node_id) resolves to the node created by op index N \u2014 no need to predict node ids. Max ${MAX_BATCH_OPS} per call.`,
58195
+ description: `Ordered list of edit ops applied atomically (all-or-nothing). Each op is \`{op, ...args}\` where op is one of create/update/delete/set_material and the remaining fields are that tool's arguments (minus document_id): create {op, type, params}, update {op, node_id|part_id, ...fields}, delete {op, part_id}, set_material {op, part_id, material}. Later ops may reference parts from earlier ops in the same batch symbolically: "@N" (in params.child/left/right/sketch or part_id/node_id) resolves to the part created by op index N (for an in-place edit such as translate, the part it edited) \u2014 no need to predict ids. Max ${MAX_BATCH_OPS} per call.`,
58421
58196
  items: { type: "object" }
58422
58197
  },
58423
58198
  dry_run: {
@@ -58445,7 +58220,7 @@ var init_batch_edit = __esm({
58445
58220
  {
58446
58221
  name: "apply_edits",
58447
58222
  pack: null,
58448
- description: "Apply an ordered list of edit ops (create/update/delete/set_material) to a session document atomically \u2014 all-or-nothing. One call replaces N single-node round-trips: it returns one aggregated `changed` diff over every op and one integrity certificate, and a subsequent `undo` rewinds the whole batch. Any op failing restores the pre-call document byte-for-byte and reports the failing op index. Pass `dry_run: true` to validate and get the per-op plan without mutating. Each op is `{op, ...args}` (e.g. {op:'create', type:'cube', params:{size:{x,y,z}}}); later ops reference nodes created earlier in the batch as \"@N\" (op index N), so a boolean chain never predicts node ids.",
58223
+ description: "Apply an ordered list of edit ops (create/update/delete/set_material) to a session document atomically \u2014 all-or-nothing. One call replaces N single-node round-trips: it returns one aggregated `changed` diff over every op and one integrity certificate, and a subsequent `undo` rewinds the whole batch. Any op failing restores the pre-call document byte-for-byte and reports the failing op index. Pass `dry_run: true` to validate and get the per-op plan without mutating. Each op is `{op, ...args}` (e.g. {op:'create', type:'cube', params:{size:{x,y,z}}}); later ops reference parts created earlier in the batch as \"@N\" (op index N), so a boolean chain never predicts node ids.",
58449
58224
  inputSchema: applyEditsSchema,
58450
58225
  handler: (a, c) => applyEdits(a, c.engine),
58451
58226
  // mount: a batch edit is a milestone — refresh the viewer at the bottom
@@ -87491,8 +87266,8 @@ var init_server3 = __esm({
87491
87266
  init_order_feed();
87492
87267
  init_animate();
87493
87268
  PKG_VERSION = (() => {
87494
- if ("0.10.0-main.115") {
87495
- return "0.10.0-main.115";
87269
+ if ("0.10.0-main.117") {
87270
+ return "0.10.0-main.117";
87496
87271
  }
87497
87272
  try {
87498
87273
  const req = createRequire2(import.meta.url);
@@ -87501,8 +87276,8 @@ var init_server3 = __esm({
87501
87276
  return "0.0.0";
87502
87277
  }
87503
87278
  })();
87504
- BUILD_SHA = "8ad12f403ad86c964867f288c8ef53b7790be8c8";
87505
- BUILD_TIME = "2026-10-20T23:56:43.258Z";
87279
+ BUILD_SHA = "8c6f34bb566075c1bcec315c497a5c29fac2b820";
87280
+ BUILD_TIME = "2026-10-21T00:57:52.178Z";
87506
87281
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
87507
87282
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
87508
87283
  INSTANCE_ID = randomUUID7().slice(0, 8);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcad/mcp",
3
- "version": "0.10.0-main.115",
3
+ "version": "0.10.0-main.117",
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": "8ad12f403ad86c964867f288c8ef53b7790be8c8",
23
- "builtAt": "2026-10-20T23:56:43.258Z"
22
+ "sha": "8c6f34bb566075c1bcec315c497a5c29fac2b820",
23
+ "builtAt": "2026-10-21T00:57:52.178Z"
24
24
  }
25
25
  }
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/vcad_kernel_wasm_bg.wasm
3
- size: 35323488 bytes
4
- sha256: 3a4f66bfab16cafe28c6409cd71c6dedcf0a910a856e0649adcff9dce4b7db5a
3
+ size: 35331246 bytes
4
+ sha256: ec88981a02ecc87b2594ff2052c095e5c041220594f11f4834bb99da3b4e6187