@vcad/mcp 0.10.0-main.131 → 0.10.0-main.133

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.mjs +163 -90
  3. package/package.json +3 -3
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 5ba01b5a98082b32c94a94415030e2581bf3288e at 2026-10-21T16:56:57.309Z. Source: https://github.com/ecto/vcad
9
+ Built from ed9b701622258f961cdbb3fa2c2430c283a8fc5b at 2026-10-21T17:47:36.984Z. Source: https://github.com/ecto/vcad
package/index.mjs CHANGED
@@ -12492,6 +12492,7 @@ var init_dist = __esm({
12492
12492
  evaluateDocument: wasmModule7.evaluateDocument,
12493
12493
  evalVcadSource: wasmModule7.evalVcadSource,
12494
12494
  evalVcadSourceWithModules: wasmModule7.evalVcadSourceWithModules,
12495
+ evalLoonDocument: wasmModule7.evalLoonDocument,
12495
12496
  evalVcadSourceParametric: wasmModule7.evalVcadSourceParametric,
12496
12497
  documentParameterGradient: wasmModule7.documentParameterGradient,
12497
12498
  documentSensitivities: wasmModule7.documentSensitivities,
@@ -13394,6 +13395,22 @@ var init_dist = __esm({
13394
13395
  * With an empty map this is exactly {@link evalVcadSource}. Returns null
13395
13396
  * if the kernel doesn't support module-aware loon evaluation.
13396
13397
  */
13398
+ /**
13399
+ * Evaluate loon source to a session document stamped with its provenance
13400
+ * (`doc.source`: program, modules, origin dir/file, SHA-256), in the kernel.
13401
+ * Null on kernels predating the export.
13402
+ */
13403
+ evalLoonDocument(source, modules, origin = {}) {
13404
+ if (typeof this.kernel.evalLoonDocument !== "function")
13405
+ return null;
13406
+ const doc = JSON.parse(this.kernel.evalLoonDocument(source, JSON.stringify(modules), origin.baseDir, origin.path));
13407
+ if (doc.source) {
13408
+ doc.source.diverged ??= false;
13409
+ doc.source.diverged_by ??= [];
13410
+ doc.source.modules ??= {};
13411
+ }
13412
+ return doc;
13413
+ }
13397
13414
  evalVcadSourceWithModules(source, modules) {
13398
13415
  if (!Object.keys(modules).length)
13399
13416
  return this.evalVcadSource(source);
@@ -41647,6 +41664,31 @@ var init_CHANGELOG = __esm({
41647
41664
  CHANGELOG_default = {
41648
41665
  $schema: "./changelog.schema.json",
41649
41666
  entries: [
41667
+ {
41668
+ id: "2026-10-21-vcad-printer-cli",
41669
+ version: "0.10.0",
41670
+ date: "2026-10-21",
41671
+ category: "feat",
41672
+ title: "vcad printer: LAN control of a Bambu P1S",
41673
+ summary: "New `vcad printer` CLI: discover, status, camera snapshot and upload test over LAN; printing refuses without --confirm-print and a passing printcheck and file/printer match.",
41674
+ features: [
41675
+ "printing",
41676
+ "bambu",
41677
+ "cli"
41678
+ ]
41679
+ },
41680
+ {
41681
+ id: "2026-10-21-slicer-first-layer-z",
41682
+ version: "0.10.0",
41683
+ date: "2026-10-21",
41684
+ category: "fix",
41685
+ title: "Slicer: layers print at full height, not half",
41686
+ summary: "Sliced G-code sent the nozzle to each layer's mid-plane, so the first layer was squashed to half height into the plate. Layers now print at their top.",
41687
+ features: [
41688
+ "printing",
41689
+ "slicer"
41690
+ ]
41691
+ },
41650
41692
  {
41651
41693
  id: "2026-10-21-planar-cap-normals",
41652
41694
  version: "0.10.0",
@@ -41686,6 +41728,32 @@ var init_CHANGELOG = __esm({
41686
41728
  "cli"
41687
41729
  ]
41688
41730
  },
41731
+ {
41732
+ id: "2026-10-21-photoreal-cyclorama",
41733
+ version: "0.10.0",
41734
+ date: "2026-10-21",
41735
+ category: "feat",
41736
+ title: "Seamless cyclorama backdrop for photoreal renders",
41737
+ summary: "--backdrop cyclorama (vcad render --cyclorama) puts the subject on a matte floor that curves into a back wall, with no horizon line or edge in frame.",
41738
+ features: [
41739
+ "photoreal",
41740
+ "render"
41741
+ ]
41742
+ },
41743
+ {
41744
+ id: "2026-10-21-mac-welcome",
41745
+ version: "0.10.0",
41746
+ date: "2026-10-21",
41747
+ category: "feat",
41748
+ title: "Mac app welcome window with samples and a loon tutorial",
41749
+ summary: "First launch opens a welcome window with four sample projects bundled in the app and a short tutorial, \u201Cyour first part in loon\u201D. Help \u25B8 Welcome to vcad reopens it.",
41750
+ features: [
41751
+ "desktop",
41752
+ "mac",
41753
+ "onboarding",
41754
+ "loon"
41755
+ ]
41756
+ },
41689
41757
  {
41690
41758
  id: "2026-10-21-mac-sim-teardown-race",
41691
41759
  version: "0.10.0",
@@ -41723,6 +41791,19 @@ var init_CHANGELOG = __esm({
41723
41791
  "file-io"
41724
41792
  ]
41725
41793
  },
41794
+ {
41795
+ id: "2026-10-21-mac-loon-loop",
41796
+ version: "0.10.0",
41797
+ date: "2026-10-21",
41798
+ category: "feat",
41799
+ title: "Mac app: loon errors inline, last good model kept",
41800
+ summary: "Open a .loon file or project folder and every save rebuilds; a broken edit shows file:line with Go to Line and keeps the last good model on screen. Module edits rebuild too.",
41801
+ features: [
41802
+ "desktop",
41803
+ "mac",
41804
+ "loon"
41805
+ ]
41806
+ },
41726
41807
  {
41727
41808
  id: "2026-10-21-mac-labs-toggle",
41728
41809
  version: "0.10.0",
@@ -41775,6 +41856,19 @@ var init_CHANGELOG = __esm({
41775
41856
  "cli"
41776
41857
  ]
41777
41858
  },
41859
+ {
41860
+ id: "2026-10-21-incremental-eval-hosts",
41861
+ version: "0.10.0",
41862
+ date: "2026-10-21",
41863
+ category: "perf",
41864
+ title: "Parameter scrubs re-evaluate only what changed",
41865
+ summary: "The Mac app and web eval worker keep an incremental evaluation session per document, so scrubbing a parameter rebuilds only the features downstream of it.",
41866
+ features: [
41867
+ "engine",
41868
+ "evaluation",
41869
+ "performance"
41870
+ ]
41871
+ },
41778
41872
  {
41779
41873
  id: "2026-10-21-gpu-photoreal-limits",
41780
41874
  version: "0.10.0",
@@ -51705,79 +51799,8 @@ var init_loon_assets = __esm({
51705
51799
  }
51706
51800
  });
51707
51801
 
51708
- // src/tools/source-provenance.ts
51709
- import { createHash } from "node:crypto";
51710
- import { readFileSync as readFileSync3 } from "node:fs";
51711
- function sourceHash(text) {
51712
- return createHash("sha256").update(text, "utf8").digest("hex");
51713
- }
51714
- function attachLoonSource(doc, { text, modules, base_dir, path }) {
51715
- const source = {
51716
- language: "loon",
51717
- text,
51718
- modules: modules && Object.keys(modules).length ? { ...modules } : {},
51719
- hash: sourceHash(text),
51720
- diverged: false,
51721
- diverged_by: []
51722
- };
51723
- if (base_dir) source.base_dir = base_dir;
51724
- if (path) source.path = path;
51725
- doc.source = source;
51726
- }
51727
- function markSourceDiverged(documentId, tool) {
51728
- const src = documents.get(documentId)?.source;
51729
- if (!src) return false;
51730
- const firstTime = !src.diverged;
51731
- src.diverged = true;
51732
- const by = src.diverged_by ??= [];
51733
- if (by[by.length - 1] !== tool) by.push(tool);
51734
- if (by.length > MAX_DIVERGED_BY) by.splice(0, by.length - MAX_DIVERGED_BY);
51735
- return firstTime;
51736
- }
51737
- function sourceStatus(doc) {
51738
- const src = doc?.source;
51739
- if (!src) return { source_stale: false };
51740
- const path = src.path;
51741
- if (path) {
51742
- let onDisk = null;
51743
- try {
51744
- onDisk = readFileSync3(path, "utf8");
51745
- } catch {
51746
- return {
51747
- source_stale: true,
51748
- source_path: path,
51749
- reason: `The source file ${path} can no longer be read \u2014 this session is no longer backed by the file it was loaded from.`
51750
- };
51751
- }
51752
- if (sourceHash(onDisk) !== src.hash) {
51753
- return {
51754
- source_stale: true,
51755
- source_path: path,
51756
- reason: `${path} changed on disk since this session was evaluated from it \u2014 the session's geometry is from the OLD source. Re-load the file to pick up the edits.`
51757
- };
51758
- }
51759
- }
51760
- if (src.diverged) {
51761
- const by = src.diverged_by ?? [];
51762
- return {
51763
- source_stale: true,
51764
- ...path ? { source_path: path } : {},
51765
- reason: `This session has been edited directly (${by.join(", ") || "mutation"}), and those edits cannot round-trip back to loon. The stored source describes the document's ORIGIN, not its current state` + (path ? ` \u2014 saving would not reproduce ${path}, and re-evaluating ${path} would discard the edits.` : `.`)
51766
- };
51767
- }
51768
- return { source_stale: false, ...path ? { source_path: path } : {} };
51769
- }
51770
- var MAX_DIVERGED_BY;
51771
- var init_source_provenance = __esm({
51772
- "src/tools/source-provenance.ts"() {
51773
- "use strict";
51774
- init_session_core();
51775
- MAX_DIVERGED_BY = 8;
51776
- }
51777
- });
51778
-
51779
51802
  // src/tools/loon.ts
51780
- import { readFileSync as readFileSync4 } from "node:fs";
51803
+ import { readFileSync as readFileSync3 } from "node:fs";
51781
51804
  import { delimiter, isAbsolute as isAbsolute3, join as join2, resolve as resolve2, sep as sep2 } from "node:path";
51782
51805
  function composeLoonProgram(input) {
51783
51806
  const { source, use_loons, loons } = input;
@@ -51803,7 +51826,7 @@ function readModule(base, name) {
51803
51826
  const file = resolve2(join2(root, ...name.split(".")) + ext);
51804
51827
  if (file !== root && !file.startsWith(root + sep2)) continue;
51805
51828
  try {
51806
- return readFileSync4(file, "utf8");
51829
+ return readFileSync3(file, "utf8");
51807
51830
  } catch {
51808
51831
  }
51809
51832
  }
@@ -51870,11 +51893,10 @@ function evalCadLoon(input, engine) {
51870
51893
  const modules = composeLoonModules(input);
51871
51894
  let doc;
51872
51895
  try {
51873
- doc = engine.evalVcadSourceWithModules(source, modules);
51896
+ doc = engine.evalLoonDocument(source, modules, { baseDir: base_dir });
51874
51897
  } catch (e) {
51875
51898
  throw explainEvalError(e, parsed, missingModules(source, modules));
51876
51899
  }
51877
- if (doc) attachLoonSource(doc, { text: source, modules, base_dir });
51878
51900
  if (!doc) {
51879
51901
  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";
51880
51902
  return { result: { content: [{ type: "text", text: text2 }] }, doc: null };
@@ -51948,7 +51970,6 @@ var init_loon = __esm({
51948
51970
  init_session_core();
51949
51971
  init_loon_assets();
51950
51972
  init_remote();
51951
- init_source_provenance();
51952
51973
  init_tool_def();
51953
51974
  createCadLoonSchema = {
51954
51975
  type: "object",
@@ -52604,7 +52625,7 @@ var init_session_store = __esm({
52604
52625
  });
52605
52626
 
52606
52627
  // src/tools/artifact-store.ts
52607
- import { createHash as createHash2, randomBytes } from "node:crypto";
52628
+ import { createHash, randomBytes } from "node:crypto";
52608
52629
  function ttlMs() {
52609
52630
  const raw = process.env.MCP_ARTIFACT_TTL_MS;
52610
52631
  const n = raw ? parseInt(raw, 10) : NaN;
@@ -52700,7 +52721,7 @@ function storeArtifact(files) {
52700
52721
  const manifest = stored.map((f) => ({
52701
52722
  file: f.name,
52702
52723
  bytes: f.buf.length,
52703
- sha256: createHash2("sha256").update(f.buf).digest("hex")
52724
+ sha256: createHash("sha256").update(f.buf).digest("hex")
52704
52725
  }));
52705
52726
  const bytes = stored.reduce((n, f) => n + f.buf.length, 0);
52706
52727
  const expiresAt = now + ttlMs();
@@ -52835,7 +52856,7 @@ async function loadDurable(id) {
52835
52856
  manifest: Array.isArray(row.manifest) ? row.manifest : files.map((f) => ({
52836
52857
  file: f.name,
52837
52858
  bytes: f.buf.length,
52838
- sha256: createHash2("sha256").update(f.buf).digest("hex")
52859
+ sha256: createHash("sha256").update(f.buf).digest("hex")
52839
52860
  })),
52840
52861
  bytes: typeof row.bytes === "number" ? row.bytes : files.reduce((n, f) => n + f.buf.length, 0),
52841
52862
  createdAt: row.created_at ? Date.parse(row.created_at) : Date.now(),
@@ -52895,6 +52916,64 @@ var init_artifact_store = __esm({
52895
52916
  }
52896
52917
  });
52897
52918
 
52919
+ // src/tools/source-provenance.ts
52920
+ import { createHash as createHash2 } from "node:crypto";
52921
+ import { readFileSync as readFileSync4 } from "node:fs";
52922
+ function sourceHash(text) {
52923
+ return createHash2("sha256").update(text, "utf8").digest("hex");
52924
+ }
52925
+ function markSourceDiverged(documentId, tool) {
52926
+ const src = documents.get(documentId)?.source;
52927
+ if (!src) return false;
52928
+ const firstTime = !src.diverged;
52929
+ src.diverged = true;
52930
+ const by = src.diverged_by ??= [];
52931
+ if (by[by.length - 1] !== tool) by.push(tool);
52932
+ if (by.length > MAX_DIVERGED_BY) by.splice(0, by.length - MAX_DIVERGED_BY);
52933
+ return firstTime;
52934
+ }
52935
+ function sourceStatus(doc) {
52936
+ const src = doc?.source;
52937
+ if (!src) return { source_stale: false };
52938
+ const path = src.path;
52939
+ if (path) {
52940
+ let onDisk = null;
52941
+ try {
52942
+ onDisk = readFileSync4(path, "utf8");
52943
+ } catch {
52944
+ return {
52945
+ source_stale: true,
52946
+ source_path: path,
52947
+ reason: `The source file ${path} can no longer be read \u2014 this session is no longer backed by the file it was loaded from.`
52948
+ };
52949
+ }
52950
+ if (sourceHash(onDisk) !== src.hash) {
52951
+ return {
52952
+ source_stale: true,
52953
+ source_path: path,
52954
+ reason: `${path} changed on disk since this session was evaluated from it \u2014 the session's geometry is from the OLD source. Re-load the file to pick up the edits.`
52955
+ };
52956
+ }
52957
+ }
52958
+ if (src.diverged) {
52959
+ const by = src.diverged_by ?? [];
52960
+ return {
52961
+ source_stale: true,
52962
+ ...path ? { source_path: path } : {},
52963
+ reason: `This session has been edited directly (${by.join(", ") || "mutation"}), and those edits cannot round-trip back to loon. The stored source describes the document's ORIGIN, not its current state` + (path ? ` \u2014 saving would not reproduce ${path}, and re-evaluating ${path} would discard the edits.` : `.`)
52964
+ };
52965
+ }
52966
+ return { source_stale: false, ...path ? { source_path: path } : {} };
52967
+ }
52968
+ var MAX_DIVERGED_BY;
52969
+ var init_source_provenance = __esm({
52970
+ "src/tools/source-provenance.ts"() {
52971
+ "use strict";
52972
+ init_session_core();
52973
+ MAX_DIVERGED_BY = 8;
52974
+ }
52975
+ });
52976
+
52898
52977
  // src/tools/session.ts
52899
52978
  import { writeFileSync as writeFileSync2, readFileSync as readFileSync5 } from "node:fs";
52900
52979
  import { dirname, resolve as resolve3 } from "node:path";
@@ -53211,18 +53290,12 @@ async function loadDocumentFromPath(path, store, engine) {
53211
53290
  const modules = composeLoonModules({ source: raw, base_dir: baseDir });
53212
53291
  let doc;
53213
53292
  try {
53214
- doc = engine.evalVcadSourceWithModules(raw, modules) ?? null;
53293
+ doc = engine.evalLoonDocument(raw, modules, { baseDir, path: abs });
53215
53294
  } catch (e) {
53216
53295
  return fail2(`Loon evaluation of ${abs} failed: ${e.message}`);
53217
53296
  }
53218
53297
  if (!doc) return fail2(`Loon evaluation of ${abs} produced no document.`);
53219
53298
  const imports = assetNote(resolveLoonAssets(doc, { base_dir: baseDir }, engine));
53220
- attachLoonSource(doc, {
53221
- text: raw,
53222
- modules,
53223
- base_dir: baseDir,
53224
- path: abs
53225
- });
53226
53299
  const id = registerSession(doc);
53227
53300
  return {
53228
53301
  content: [
@@ -87053,8 +87126,8 @@ var init_server3 = __esm({
87053
87126
  init_order_feed();
87054
87127
  init_animate();
87055
87128
  PKG_VERSION = (() => {
87056
- if ("0.10.0-main.131") {
87057
- return "0.10.0-main.131";
87129
+ if ("0.10.0-main.133") {
87130
+ return "0.10.0-main.133";
87058
87131
  }
87059
87132
  try {
87060
87133
  const req = createRequire2(import.meta.url);
@@ -87063,8 +87136,8 @@ var init_server3 = __esm({
87063
87136
  return "0.0.0";
87064
87137
  }
87065
87138
  })();
87066
- BUILD_SHA = "5ba01b5a98082b32c94a94415030e2581bf3288e";
87067
- BUILD_TIME = "2026-10-21T16:56:57.309Z";
87139
+ BUILD_SHA = "ed9b701622258f961cdbb3fa2c2430c283a8fc5b";
87140
+ BUILD_TIME = "2026-10-21T17:47:36.984Z";
87068
87141
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
87069
87142
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
87070
87143
  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.131",
3
+ "version": "0.10.0-main.133",
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": "5ba01b5a98082b32c94a94415030e2581bf3288e",
23
- "builtAt": "2026-10-21T16:56:57.309Z"
22
+ "sha": "ed9b701622258f961cdbb3fa2c2430c283a8fc5b",
23
+ "builtAt": "2026-10-21T17:47:36.984Z"
24
24
  }
25
25
  }