@vcad/mcp 0.10.0-main.131 → 0.10.0-main.132
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 +1 -1
- package/index.mjs +98 -90
- 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
|
|
9
|
+
Built from 362c46b798588ed58f200c9d529b3d11b2b2ff73 at 2026-10-21T17:31:51.205Z. 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);
|
|
@@ -41686,6 +41703,18 @@ var init_CHANGELOG = __esm({
|
|
|
41686
41703
|
"cli"
|
|
41687
41704
|
]
|
|
41688
41705
|
},
|
|
41706
|
+
{
|
|
41707
|
+
id: "2026-10-21-photoreal-cyclorama",
|
|
41708
|
+
version: "0.10.0",
|
|
41709
|
+
date: "2026-10-21",
|
|
41710
|
+
category: "feat",
|
|
41711
|
+
title: "Seamless cyclorama backdrop for photoreal renders",
|
|
41712
|
+
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.",
|
|
41713
|
+
features: [
|
|
41714
|
+
"photoreal",
|
|
41715
|
+
"render"
|
|
41716
|
+
]
|
|
41717
|
+
},
|
|
41689
41718
|
{
|
|
41690
41719
|
id: "2026-10-21-mac-sim-teardown-race",
|
|
41691
41720
|
version: "0.10.0",
|
|
@@ -51705,79 +51734,8 @@ var init_loon_assets = __esm({
|
|
|
51705
51734
|
}
|
|
51706
51735
|
});
|
|
51707
51736
|
|
|
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
51737
|
// src/tools/loon.ts
|
|
51780
|
-
import { readFileSync as
|
|
51738
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
51781
51739
|
import { delimiter, isAbsolute as isAbsolute3, join as join2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
51782
51740
|
function composeLoonProgram(input) {
|
|
51783
51741
|
const { source, use_loons, loons } = input;
|
|
@@ -51803,7 +51761,7 @@ function readModule(base, name) {
|
|
|
51803
51761
|
const file = resolve2(join2(root, ...name.split(".")) + ext);
|
|
51804
51762
|
if (file !== root && !file.startsWith(root + sep2)) continue;
|
|
51805
51763
|
try {
|
|
51806
|
-
return
|
|
51764
|
+
return readFileSync3(file, "utf8");
|
|
51807
51765
|
} catch {
|
|
51808
51766
|
}
|
|
51809
51767
|
}
|
|
@@ -51870,11 +51828,10 @@ function evalCadLoon(input, engine) {
|
|
|
51870
51828
|
const modules = composeLoonModules(input);
|
|
51871
51829
|
let doc;
|
|
51872
51830
|
try {
|
|
51873
|
-
doc = engine.
|
|
51831
|
+
doc = engine.evalLoonDocument(source, modules, { baseDir: base_dir });
|
|
51874
51832
|
} catch (e) {
|
|
51875
51833
|
throw explainEvalError(e, parsed, missingModules(source, modules));
|
|
51876
51834
|
}
|
|
51877
|
-
if (doc) attachLoonSource(doc, { text: source, modules, base_dir });
|
|
51878
51835
|
if (!doc) {
|
|
51879
51836
|
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
51837
|
return { result: { content: [{ type: "text", text: text2 }] }, doc: null };
|
|
@@ -51948,7 +51905,6 @@ var init_loon = __esm({
|
|
|
51948
51905
|
init_session_core();
|
|
51949
51906
|
init_loon_assets();
|
|
51950
51907
|
init_remote();
|
|
51951
|
-
init_source_provenance();
|
|
51952
51908
|
init_tool_def();
|
|
51953
51909
|
createCadLoonSchema = {
|
|
51954
51910
|
type: "object",
|
|
@@ -52604,7 +52560,7 @@ var init_session_store = __esm({
|
|
|
52604
52560
|
});
|
|
52605
52561
|
|
|
52606
52562
|
// src/tools/artifact-store.ts
|
|
52607
|
-
import { createHash
|
|
52563
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
52608
52564
|
function ttlMs() {
|
|
52609
52565
|
const raw = process.env.MCP_ARTIFACT_TTL_MS;
|
|
52610
52566
|
const n = raw ? parseInt(raw, 10) : NaN;
|
|
@@ -52700,7 +52656,7 @@ function storeArtifact(files) {
|
|
|
52700
52656
|
const manifest = stored.map((f) => ({
|
|
52701
52657
|
file: f.name,
|
|
52702
52658
|
bytes: f.buf.length,
|
|
52703
|
-
sha256:
|
|
52659
|
+
sha256: createHash("sha256").update(f.buf).digest("hex")
|
|
52704
52660
|
}));
|
|
52705
52661
|
const bytes = stored.reduce((n, f) => n + f.buf.length, 0);
|
|
52706
52662
|
const expiresAt = now + ttlMs();
|
|
@@ -52835,7 +52791,7 @@ async function loadDurable(id) {
|
|
|
52835
52791
|
manifest: Array.isArray(row.manifest) ? row.manifest : files.map((f) => ({
|
|
52836
52792
|
file: f.name,
|
|
52837
52793
|
bytes: f.buf.length,
|
|
52838
|
-
sha256:
|
|
52794
|
+
sha256: createHash("sha256").update(f.buf).digest("hex")
|
|
52839
52795
|
})),
|
|
52840
52796
|
bytes: typeof row.bytes === "number" ? row.bytes : files.reduce((n, f) => n + f.buf.length, 0),
|
|
52841
52797
|
createdAt: row.created_at ? Date.parse(row.created_at) : Date.now(),
|
|
@@ -52895,6 +52851,64 @@ var init_artifact_store = __esm({
|
|
|
52895
52851
|
}
|
|
52896
52852
|
});
|
|
52897
52853
|
|
|
52854
|
+
// src/tools/source-provenance.ts
|
|
52855
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
52856
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
52857
|
+
function sourceHash(text) {
|
|
52858
|
+
return createHash2("sha256").update(text, "utf8").digest("hex");
|
|
52859
|
+
}
|
|
52860
|
+
function markSourceDiverged(documentId, tool) {
|
|
52861
|
+
const src = documents.get(documentId)?.source;
|
|
52862
|
+
if (!src) return false;
|
|
52863
|
+
const firstTime = !src.diverged;
|
|
52864
|
+
src.diverged = true;
|
|
52865
|
+
const by = src.diverged_by ??= [];
|
|
52866
|
+
if (by[by.length - 1] !== tool) by.push(tool);
|
|
52867
|
+
if (by.length > MAX_DIVERGED_BY) by.splice(0, by.length - MAX_DIVERGED_BY);
|
|
52868
|
+
return firstTime;
|
|
52869
|
+
}
|
|
52870
|
+
function sourceStatus(doc) {
|
|
52871
|
+
const src = doc?.source;
|
|
52872
|
+
if (!src) return { source_stale: false };
|
|
52873
|
+
const path = src.path;
|
|
52874
|
+
if (path) {
|
|
52875
|
+
let onDisk = null;
|
|
52876
|
+
try {
|
|
52877
|
+
onDisk = readFileSync4(path, "utf8");
|
|
52878
|
+
} catch {
|
|
52879
|
+
return {
|
|
52880
|
+
source_stale: true,
|
|
52881
|
+
source_path: path,
|
|
52882
|
+
reason: `The source file ${path} can no longer be read \u2014 this session is no longer backed by the file it was loaded from.`
|
|
52883
|
+
};
|
|
52884
|
+
}
|
|
52885
|
+
if (sourceHash(onDisk) !== src.hash) {
|
|
52886
|
+
return {
|
|
52887
|
+
source_stale: true,
|
|
52888
|
+
source_path: path,
|
|
52889
|
+
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.`
|
|
52890
|
+
};
|
|
52891
|
+
}
|
|
52892
|
+
}
|
|
52893
|
+
if (src.diverged) {
|
|
52894
|
+
const by = src.diverged_by ?? [];
|
|
52895
|
+
return {
|
|
52896
|
+
source_stale: true,
|
|
52897
|
+
...path ? { source_path: path } : {},
|
|
52898
|
+
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.` : `.`)
|
|
52899
|
+
};
|
|
52900
|
+
}
|
|
52901
|
+
return { source_stale: false, ...path ? { source_path: path } : {} };
|
|
52902
|
+
}
|
|
52903
|
+
var MAX_DIVERGED_BY;
|
|
52904
|
+
var init_source_provenance = __esm({
|
|
52905
|
+
"src/tools/source-provenance.ts"() {
|
|
52906
|
+
"use strict";
|
|
52907
|
+
init_session_core();
|
|
52908
|
+
MAX_DIVERGED_BY = 8;
|
|
52909
|
+
}
|
|
52910
|
+
});
|
|
52911
|
+
|
|
52898
52912
|
// src/tools/session.ts
|
|
52899
52913
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
52900
52914
|
import { dirname, resolve as resolve3 } from "node:path";
|
|
@@ -53211,18 +53225,12 @@ async function loadDocumentFromPath(path, store, engine) {
|
|
|
53211
53225
|
const modules = composeLoonModules({ source: raw, base_dir: baseDir });
|
|
53212
53226
|
let doc;
|
|
53213
53227
|
try {
|
|
53214
|
-
doc = engine.
|
|
53228
|
+
doc = engine.evalLoonDocument(raw, modules, { baseDir, path: abs });
|
|
53215
53229
|
} catch (e) {
|
|
53216
53230
|
return fail2(`Loon evaluation of ${abs} failed: ${e.message}`);
|
|
53217
53231
|
}
|
|
53218
53232
|
if (!doc) return fail2(`Loon evaluation of ${abs} produced no document.`);
|
|
53219
53233
|
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
53234
|
const id = registerSession(doc);
|
|
53227
53235
|
return {
|
|
53228
53236
|
content: [
|
|
@@ -87053,8 +87061,8 @@ var init_server3 = __esm({
|
|
|
87053
87061
|
init_order_feed();
|
|
87054
87062
|
init_animate();
|
|
87055
87063
|
PKG_VERSION = (() => {
|
|
87056
|
-
if ("0.10.0-main.
|
|
87057
|
-
return "0.10.0-main.
|
|
87064
|
+
if ("0.10.0-main.132") {
|
|
87065
|
+
return "0.10.0-main.132";
|
|
87058
87066
|
}
|
|
87059
87067
|
try {
|
|
87060
87068
|
const req = createRequire2(import.meta.url);
|
|
@@ -87063,8 +87071,8 @@ var init_server3 = __esm({
|
|
|
87063
87071
|
return "0.0.0";
|
|
87064
87072
|
}
|
|
87065
87073
|
})();
|
|
87066
|
-
BUILD_SHA = "
|
|
87067
|
-
BUILD_TIME = "2026-10-
|
|
87074
|
+
BUILD_SHA = "362c46b798588ed58f200c9d529b3d11b2b2ff73";
|
|
87075
|
+
BUILD_TIME = "2026-10-21T17:31:51.205Z";
|
|
87068
87076
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
87069
87077
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
87070
87078
|
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.
|
|
3
|
+
"version": "0.10.0-main.132",
|
|
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": "
|
|
23
|
-
"builtAt": "2026-10-
|
|
22
|
+
"sha": "362c46b798588ed58f200c9d529b3d11b2b2ff73",
|
|
23
|
+
"builtAt": "2026-10-21T17:31:51.205Z"
|
|
24
24
|
}
|
|
25
25
|
}
|