@vcad/mcp 0.9.4-main.36 → 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 +1 -1
- package/index.mjs +287 -12
- package/package.json +3 -3
- package/vcad_kernel_wasm_bg.wasm +0 -0
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 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,
|
|
@@ -11559,6 +11570,7 @@ var init_dist = __esm({
|
|
|
11559
11570
|
circuitTransient: wasmModule7.circuitTransient,
|
|
11560
11571
|
circuitTune: wasmModule7.circuitTune,
|
|
11561
11572
|
feaAnalyzeMesh: wasmModule7.feaAnalyzeMesh,
|
|
11573
|
+
feaCheckBeam: wasmModule7.feaCheckBeam,
|
|
11562
11574
|
emSimulate: wasmModule7.emSimulate,
|
|
11563
11575
|
antennaAnalyze: wasmModule7.antennaAnalyze,
|
|
11564
11576
|
photonicsSimulate: wasmModule7.photonicsSimulate,
|
|
@@ -11887,6 +11899,22 @@ var init_dist = __esm({
|
|
|
11887
11899
|
}
|
|
11888
11900
|
return fn(specJson, optionsJson, positions, indices);
|
|
11889
11901
|
}
|
|
11902
|
+
/**
|
|
11903
|
+
* Closed-form check of a prismatic member: exact section properties,
|
|
11904
|
+
* beam bending with the Timoshenko shear term, Bredt thin-wall (or
|
|
11905
|
+
* Saint-Venant series) torsion, and Euler buckling — the route for
|
|
11906
|
+
* thin-walled sheet-metal and tube-frame members, which no affordable
|
|
11907
|
+
* lattice pitch can resolve. Same fail-closed contract and
|
|
11908
|
+
* `vcad.fea-claims/1` predicted claims as `feaAnalyzeMesh`; see
|
|
11909
|
+
* `vcad-kernel-fea::section`.
|
|
11910
|
+
*/
|
|
11911
|
+
feaCheckBeam(caseJson) {
|
|
11912
|
+
const fn = this.kernel.feaCheckBeam;
|
|
11913
|
+
if (typeof fn !== "function") {
|
|
11914
|
+
throw new Error("feaCheckBeam is not exported by this kernel WASM build \u2014 rebuild packages/kernel-wasm");
|
|
11915
|
+
}
|
|
11916
|
+
return fn(caseJson);
|
|
11917
|
+
}
|
|
11890
11918
|
/**
|
|
11891
11919
|
* EM field simulation (2D/axisymmetric finite-volume magnetostatics or
|
|
11892
11920
|
* electrostatics): inductance, force, torque, capacitance extraction and
|
|
@@ -12213,6 +12241,22 @@ var init_dist = __esm({
|
|
|
12213
12241
|
const json = this.kernel.evalVcadSourceWithModules(source, JSON.stringify(modules));
|
|
12214
12242
|
return JSON.parse(json);
|
|
12215
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
|
+
}
|
|
12216
12260
|
/** Evaluate a preview extrusion without adding to document */
|
|
12217
12261
|
evaluateExtrudePreview(origin, xDir, yDir, segments, direction) {
|
|
12218
12262
|
if (segments.length === 0)
|
|
@@ -40946,6 +40990,26 @@ var init_CHANGELOG = __esm({
|
|
|
40946
40990
|
"rendering"
|
|
40947
40991
|
]
|
|
40948
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
|
+
},
|
|
40949
41013
|
{
|
|
40950
41014
|
id: "2026-07-26-loon-mirror-symmetry",
|
|
40951
41015
|
version: "0.9.4",
|
|
@@ -40984,6 +41048,25 @@ var init_CHANGELOG = __esm({
|
|
|
40984
41048
|
"export_cad"
|
|
40985
41049
|
]
|
|
40986
41050
|
},
|
|
41051
|
+
{
|
|
41052
|
+
id: "2026-07-26-fastener-forms",
|
|
41053
|
+
version: "0.9.4",
|
|
41054
|
+
date: "2026-07-26",
|
|
41055
|
+
category: "feat",
|
|
41056
|
+
title: "Fasteners are a first-class form",
|
|
41057
|
+
summary: "bolt, bolt-circle, clearance-hole and tapped-hole: catalog-backed hardware oriented by its own axis, with real countersunk heads, length checks, and BOM lines counted off the geometry.",
|
|
41058
|
+
features: [
|
|
41059
|
+
"loon",
|
|
41060
|
+
"fasteners",
|
|
41061
|
+
"bom",
|
|
41062
|
+
"assembly"
|
|
41063
|
+
],
|
|
41064
|
+
mcpTools: [
|
|
41065
|
+
"create_cad_loon",
|
|
41066
|
+
"bom_create",
|
|
41067
|
+
"search_mechanical_parts"
|
|
41068
|
+
]
|
|
41069
|
+
},
|
|
40987
41070
|
{
|
|
40988
41071
|
id: "2026-07-26-document-source-provenance",
|
|
40989
41072
|
version: "0.9.4",
|
|
@@ -41003,6 +41086,23 @@ var init_CHANGELOG = __esm({
|
|
|
41003
41086
|
"load_document"
|
|
41004
41087
|
]
|
|
41005
41088
|
},
|
|
41089
|
+
{
|
|
41090
|
+
id: "2026-07-26-beam-check-thin-walled-fea",
|
|
41091
|
+
version: "0.9.4",
|
|
41092
|
+
date: "2026-07-26",
|
|
41093
|
+
category: "feat",
|
|
41094
|
+
title: "beam_check: audited structural answer for thin walls",
|
|
41095
|
+
summary: "Sheet-metal and tube-frame members had no audited FEA answer at any resolution; beam_check prices them in closed form (Bredt torsion, beam bending, Euler buckling), and analyze_structure now measures the wall and refuses with the cell arithmetic instead of a bare Unverifiable.",
|
|
41096
|
+
features: [
|
|
41097
|
+
"fea",
|
|
41098
|
+
"sheet-metal",
|
|
41099
|
+
"receipts"
|
|
41100
|
+
],
|
|
41101
|
+
mcpTools: [
|
|
41102
|
+
"beam_check",
|
|
41103
|
+
"analyze_structure"
|
|
41104
|
+
]
|
|
41105
|
+
},
|
|
41006
41106
|
{
|
|
41007
41107
|
id: "2026-07-26-backwards-extrude-inverted-winding",
|
|
41008
41108
|
version: "0.9.4",
|
|
@@ -48676,9 +48776,45 @@ function createCadLoon(input, engine) {
|
|
|
48676
48776
|
return { content: [{ type: "text", text: text2 }] };
|
|
48677
48777
|
}
|
|
48678
48778
|
const text = format === "json" ? JSON.stringify(doc, null, 2) : toVCode2(doc);
|
|
48679
|
-
|
|
48680
|
-
|
|
48681
|
-
};
|
|
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");
|
|
48682
48818
|
}
|
|
48683
48819
|
var createCadLoonSchema, toolDefs2;
|
|
48684
48820
|
var init_loon = __esm({
|
|
@@ -48740,7 +48876,7 @@ var init_loon = __esm({
|
|
|
48740
48876
|
{
|
|
48741
48877
|
name: "create_cad_loon",
|
|
48742
48878
|
pack: null,
|
|
48743
|
-
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',
|
|
48744
48880
|
inputSchema: createCadLoonSchema,
|
|
48745
48881
|
handler: async (args, ctx) => {
|
|
48746
48882
|
const useLoons = Array.isArray(args.use_loons) ? args.use_loons : void 0;
|
|
@@ -53324,6 +53460,7 @@ var init_tool_metadata = __esm({
|
|
|
53324
53460
|
solve_thermal: { title: "Solve Thermal", annotations: RO },
|
|
53325
53461
|
simulate_flow: { title: "Simulate Flow", annotations: RO },
|
|
53326
53462
|
analyze_structure: { title: "Analyze Structure", annotations: RO },
|
|
53463
|
+
beam_check: { title: "Check Beam / Section", annotations: RO },
|
|
53327
53464
|
simulate_neutron_shield: {
|
|
53328
53465
|
title: "Simulate Neutron Shield",
|
|
53329
53466
|
annotations: RO
|
|
@@ -57148,6 +57285,14 @@ function totalsSummary(totals) {
|
|
|
57148
57285
|
currency: totals.currency
|
|
57149
57286
|
};
|
|
57150
57287
|
}
|
|
57288
|
+
function documentHardware(documentId) {
|
|
57289
|
+
try {
|
|
57290
|
+
const doc = documents.get(documentId);
|
|
57291
|
+
return Array.isArray(doc?.hardware) ? doc.hardware : [];
|
|
57292
|
+
} catch {
|
|
57293
|
+
return [];
|
|
57294
|
+
}
|
|
57295
|
+
}
|
|
57151
57296
|
async function bomCreate(input, store, user) {
|
|
57152
57297
|
const args = input ?? {};
|
|
57153
57298
|
const owner = ownerId(user);
|
|
@@ -57162,7 +57307,23 @@ async function bomCreate(input, store, user) {
|
|
|
57162
57307
|
created_at: now,
|
|
57163
57308
|
updated_at: now
|
|
57164
57309
|
};
|
|
57165
|
-
const
|
|
57310
|
+
const fromGeometry = args.from_geometry !== false;
|
|
57311
|
+
const geometryLines = [];
|
|
57312
|
+
if (fromGeometry && bom.document_id) {
|
|
57313
|
+
for (const hw of documentHardware(bom.document_id)) {
|
|
57314
|
+
geometryLines.push({
|
|
57315
|
+
kind: "cots",
|
|
57316
|
+
name: hw.spec,
|
|
57317
|
+
catalog_id: hw.catalog_id ?? void 0,
|
|
57318
|
+
qty: hw.qty,
|
|
57319
|
+
notes: "counted from the model geometry"
|
|
57320
|
+
});
|
|
57321
|
+
}
|
|
57322
|
+
}
|
|
57323
|
+
const lineInputs = [
|
|
57324
|
+
...geometryLines,
|
|
57325
|
+
...Array.isArray(args.lines) ? args.lines : []
|
|
57326
|
+
];
|
|
57166
57327
|
for (let i = 0; i < lineInputs.length; i++) {
|
|
57167
57328
|
const built = await buildLine(lineInputs[i] ?? {}, store, owner);
|
|
57168
57329
|
if ("error" in built) return err4(`lines[${i}]: ${built.error}`);
|
|
@@ -57338,6 +57499,7 @@ var init_bom = __esm({
|
|
|
57338
57499
|
init_pricing();
|
|
57339
57500
|
init_types4();
|
|
57340
57501
|
init_mech_parts();
|
|
57502
|
+
init_session_core();
|
|
57341
57503
|
init_tool_def();
|
|
57342
57504
|
PRICING_NOTE = "All prices are ESTIMATES (Phase-0 quote estimates, catalog price bands, or caller-supplied numbers) \u2014 not binding quotes.";
|
|
57343
57505
|
memBoms = /* @__PURE__ */ new Map();
|
|
@@ -57403,6 +57565,10 @@ var init_bom = __esm({
|
|
|
57403
57565
|
type: "array",
|
|
57404
57566
|
items: { type: "object", properties: lineProperties, required: ["kind"] },
|
|
57405
57567
|
description: "Optional full line list to build the BOM in ONE call (recommended on serverless \u2014 BOMs are in-memory per instance, so one-shot creation is the robust path)."
|
|
57568
|
+
},
|
|
57569
|
+
from_geometry: {
|
|
57570
|
+
type: "boolean",
|
|
57571
|
+
description: "Seed COTS lines from the hardware the document's geometry declares \u2014 every loon `bolt`/`bolt-circle` (plus its washers and nuts), with quantities already multiplied through patterns. Default true when document_id is given."
|
|
57406
57572
|
}
|
|
57407
57573
|
},
|
|
57408
57574
|
required: []
|
|
@@ -57437,7 +57603,7 @@ var init_bom = __esm({
|
|
|
57437
57603
|
{
|
|
57438
57604
|
name: "bom_create",
|
|
57439
57605
|
pack: "bom",
|
|
57440
|
-
description: "Create a project bill of materials \u2014 the deliverable that collects every manufactured part (PCBs, sheet metal, 3D prints; link quote_manufacturing quotes by quote_id) and COTS part (bearings, shafts, screws, magnets; link search_mechanical_parts entries by catalog_id) in one place. Optionally attach a document_id and assembly notes. Pass the full `lines` array to build the whole BOM in ONE call \u2014 recommended, since BOMs are in-memory per server instance. All prices are estimates and flagged as such.",
|
|
57606
|
+
description: "Create a project bill of materials \u2014 the deliverable that collects every manufactured part (PCBs, sheet metal, 3D prints; link quote_manufacturing quotes by quote_id) and COTS part (bearings, shafts, screws, magnets; link search_mechanical_parts entries by catalog_id) in one place. Optionally attach a document_id and assembly notes. Pass the full `lines` array to build the whole BOM in ONE call \u2014 recommended, since BOMs are in-memory per server instance. When document_id names a resident session, fastener lines are seeded from the geometry itself (every loon `bolt`/`bolt-circle`, quantities multiplied through patterns) \u2014 pass from_geometry:false to opt out. All prices are estimates and flagged as such.",
|
|
57441
57607
|
inputSchema: bomCreateSchema,
|
|
57442
57608
|
handler: (a, c) => bomCreate(a, c.fabricateStore, c.user),
|
|
57443
57609
|
behavior: behavior({})
|
|
@@ -57946,6 +58112,18 @@ function listParameters(input) {
|
|
|
57946
58112
|
);
|
|
57947
58113
|
return jsonResult({ count: parameters.length, parameters });
|
|
57948
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
|
+
}
|
|
57949
58127
|
async function setParameters(input, engine) {
|
|
57950
58128
|
const args = input ?? {};
|
|
57951
58129
|
const documentId = String(args.document_id ?? "");
|
|
@@ -57975,6 +58153,18 @@ async function setParameters(input, engine) {
|
|
|
57975
58153
|
`Unknown parameter(s): ${unknown2.join(", ")}. Use list_parameters to see declared names.`
|
|
57976
58154
|
);
|
|
57977
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
|
+
});
|
|
57978
58168
|
const changed = [];
|
|
57979
58169
|
for (const [name, value] of entries) {
|
|
57980
58170
|
const param = doc.parameters[name];
|
|
@@ -57996,6 +58186,7 @@ async function setParameters(input, engine) {
|
|
|
57996
58186
|
document_id: documentId,
|
|
57997
58187
|
updated: changed.length,
|
|
57998
58188
|
changed,
|
|
58189
|
+
...noEffect.length ? { no_effect: noEffect } : {},
|
|
57999
58190
|
...constraintSolve ? { constraint_solve: constraintSolve } : {}
|
|
58000
58191
|
});
|
|
58001
58192
|
result.structuredContent = { changed };
|
|
@@ -72230,7 +72421,7 @@ var init_structure = __esm({
|
|
|
72230
72421
|
{
|
|
72231
72422
|
name: "analyze_structure",
|
|
72232
72423
|
pack: null,
|
|
72233
|
-
description: 'Static structural FEA on a part\'s real geometry with fail-closed mesh-convergence gating: linear-tet fill of the tessellated interior at 2+ refinement levels, linear-elastic solve (PCG), returning max von Mises stress, max displacement, discretization-error estimates, and (with yield_strength_mpa) a safety factor \u2014 plus `vcad.fea-claims/1` and unified-receipt claims. If QoIs disagree across levels the verdict is Unverifiable and NO stress/displacement claim is emitted (raise resolution, or fillet the singular corner it points at). Claims carry basis "predicted" and roll up Provisional until hardware is load-tested. Scope: small-displacement linear elasticity, one isotropic material; no plasticity, buckling, contact, or dynamic loads; boundary is staircase-approximated at the lattice pitch. Use predict_physics for the fast coarse steering loop; use this for the audited answer.',
|
|
72424
|
+
description: 'Static structural FEA on a part\'s real geometry with fail-closed mesh-convergence gating: linear-tet fill of the tessellated interior at 2+ refinement levels, linear-elastic solve (PCG), returning max von Mises stress, max displacement, discretization-error estimates, and (with yield_strength_mpa) a safety factor \u2014 plus `vcad.fea-claims/1` and unified-receipt claims. If QoIs disagree across levels the verdict is Unverifiable and NO stress/displacement claim is emitted (raise resolution, or fillet the singular corner it points at). Claims carry basis "predicted" and roll up Provisional until hardware is load-tested. Scope: small-displacement linear elasticity, one isotropic material; no plasticity, buckling, contact, or dynamic loads; boundary is staircase-approximated at the lattice pitch. THIN-WALLED PARTS (sheet metal, tube frame, plate) are outside this tool: it measures the thinnest load-bearing section and fails closed with the cell arithmetic and a pointer to beam_check, which is the more accurate answer for a prismatic member anyway. Use predict_physics for the fast coarse steering loop; use this for the audited answer on chunky geometry.',
|
|
72234
72425
|
inputSchema: {
|
|
72235
72426
|
type: "object",
|
|
72236
72427
|
required: ["document_id", "part", "loads", "supports"],
|
|
@@ -72295,7 +72486,7 @@ var init_structure = __esm({
|
|
|
72295
72486
|
},
|
|
72296
72487
|
resolution: {
|
|
72297
72488
|
type: "number",
|
|
72298
|
-
description: "Coarse-level lattice cells along the longest bbox axis (default 24). The finest level is resolution * 2^(levels-1), capped at 160 for this tier. Keep >= ~6 cells through the thinnest load-bearing section."
|
|
72489
|
+
description: "Coarse-level lattice cells along the longest bbox axis (default 24). The finest level is resolution * 2^(levels-1), capped at 160 for this tier. Keep >= ~6 cells through the thinnest load-bearing section \u2014 the tool measures the part and refuses below ~4, with the cell arithmetic attached. Raising this is NOT the lever for a thin wall (a 2 mm wall on a 300 mm member needs ~950 cells): use beam_check."
|
|
72299
72490
|
},
|
|
72300
72491
|
levels: {
|
|
72301
72492
|
type: "number",
|
|
@@ -72346,6 +72537,89 @@ var init_structure = __esm({
|
|
|
72346
72537
|
});
|
|
72347
72538
|
},
|
|
72348
72539
|
behavior: behavior({})
|
|
72540
|
+
},
|
|
72541
|
+
{
|
|
72542
|
+
name: "beam_check",
|
|
72543
|
+
pack: null,
|
|
72544
|
+
description: 'Closed-form structural check of a PRISMATIC member \u2014 the audited answer for thin-walled geometry, where the lattice in analyze_structure cannot put enough cells through the wall at any affordable resolution. Give it a profile (rect, rect_tube, round, round_tube, i_beam), a span, an end condition, and a load case; get section properties (A, I_y, I_z, J, section moduli, torsional stiffness), stresses (bending, axial, torsional and transverse shear, von Mises), deflection with the Timoshenko shear term, twist, the Euler buckling load under compression, and a safety factor \u2014 plus `vcad.fea-claims/1` and unified-receipt claims under `structure.beam.*`. Exact integrals where they exist (round, round tube, rectangle bending), the convergent Saint-Venant series for solid-rectangle torsion, Bredt closed thin-wall theory for tube torsion \u2014 every approximation is stated in the returned notes. For a constant cross-section this is not a fallback: it beats a staircased lattice on accuracy and costs microseconds. Fail-closed: too stubby for beam theory (L/depth < 5), too thick-walled for Bredt, deflecting past a tenth of the span, torque on an open section, or buckling before it yields \u2014 any of those makes the verdict Unverifiable and NO QoI is claimed, with the reason naming the route forward. Claims carry basis "predicted" and roll up Provisional until hardware is load-tested. Scope: one uniform section over the whole span, idealized ends, no stress concentrations (holes, welds, corner radii), no local wall buckling, no fatigue. Needs no document \u2014 it is geometry-by-description, so it works before the part exists.',
|
|
72545
|
+
inputSchema: {
|
|
72546
|
+
type: "object",
|
|
72547
|
+
required: ["profile", "length_mm", "end_condition"],
|
|
72548
|
+
properties: {
|
|
72549
|
+
profile: {
|
|
72550
|
+
type: "object",
|
|
72551
|
+
required: ["type"],
|
|
72552
|
+
description: 'Cross-section. The member runs along X and the section lives in (Y, Z): width_mm is the Y extent, height_mm the Z extent. One of: {"type":"rect",width_mm,height_mm}, {"type":"rect_tube",width_mm,height_mm,wall_mm}, {"type":"round",diameter_mm}, {"type":"round_tube",diameter_mm,wall_mm}, {"type":"i_beam",width_mm,height_mm,flange_mm,web_mm}. Tube dimensions are OUTSIDE dimensions.',
|
|
72553
|
+
properties: {
|
|
72554
|
+
type: {
|
|
72555
|
+
type: "string",
|
|
72556
|
+
enum: ["rect", "rect_tube", "round", "round_tube", "i_beam"]
|
|
72557
|
+
},
|
|
72558
|
+
width_mm: { type: "number", description: "Y extent, mm." },
|
|
72559
|
+
height_mm: { type: "number", description: "Z extent, mm." },
|
|
72560
|
+
wall_mm: { type: "number", description: "Wall thickness, mm." },
|
|
72561
|
+
diameter_mm: { type: "number", description: "Outside diameter, mm." },
|
|
72562
|
+
flange_mm: { type: "number", description: "Flange thickness, mm." },
|
|
72563
|
+
web_mm: { type: "number", description: "Web thickness, mm." }
|
|
72564
|
+
}
|
|
72565
|
+
},
|
|
72566
|
+
length_mm: {
|
|
72567
|
+
type: "number",
|
|
72568
|
+
description: "Free span between supports, mm."
|
|
72569
|
+
},
|
|
72570
|
+
end_condition: {
|
|
72571
|
+
type: "string",
|
|
72572
|
+
enum: [
|
|
72573
|
+
"cantilever_tip",
|
|
72574
|
+
"cantilever_uniform",
|
|
72575
|
+
"simple_center",
|
|
72576
|
+
"simple_uniform",
|
|
72577
|
+
"fixed_fixed_center",
|
|
72578
|
+
"fixed_fixed_uniform"
|
|
72579
|
+
],
|
|
72580
|
+
description: "Support and load arrangement. `*_uniform` spreads the transverse force over the span; `*_center`/`*_tip` concentrates it. Also sets the Euler effective-length factor (cantilever K=2, simple K=1, fixed-fixed K=0.5)."
|
|
72581
|
+
},
|
|
72582
|
+
transverse_force_n: {
|
|
72583
|
+
type: "number",
|
|
72584
|
+
description: "Total transverse force, N (magnitude). Default 0."
|
|
72585
|
+
},
|
|
72586
|
+
bend_axis: {
|
|
72587
|
+
type: "string",
|
|
72588
|
+
enum: ["y", "z"],
|
|
72589
|
+
description: "Which principal axis to bend about: `y` uses I_y and deflects along Z (default), `z` uses I_z and deflects along Y. Matters for any non-square section."
|
|
72590
|
+
},
|
|
72591
|
+
torque_nmm: {
|
|
72592
|
+
type: "number",
|
|
72593
|
+
description: "Torque about the member axis, N\xB7mm (1 N\xB7m = 1000 N\xB7mm). Default 0. This is the case closed-form theory answers best and the lattice answers worst."
|
|
72594
|
+
},
|
|
72595
|
+
axial_force_n: {
|
|
72596
|
+
type: "number",
|
|
72597
|
+
description: "Axial force, N \u2014 positive tension, negative compression. Compression is also checked against Euler buckling, and a member that buckles is reported Unverifiable rather than 'safe'. Default 0."
|
|
72598
|
+
},
|
|
72599
|
+
youngs_modulus_mpa: {
|
|
72600
|
+
type: "number",
|
|
72601
|
+
description: "Young's modulus, MPa. Default 69000 (6061 aluminum); steel ~200000, PLA ~2300."
|
|
72602
|
+
},
|
|
72603
|
+
poisson: {
|
|
72604
|
+
type: "number",
|
|
72605
|
+
description: "Poisson's ratio in [0, 0.5). Default 0.33; sets G = E/(2(1+nu))."
|
|
72606
|
+
},
|
|
72607
|
+
yield_strength_mpa: {
|
|
72608
|
+
type: "number",
|
|
72609
|
+
description: "Yield strength, MPa. When given, safety_factor = yield / von Mises is computed and claimed (applicable checks only)."
|
|
72610
|
+
}
|
|
72611
|
+
}
|
|
72612
|
+
},
|
|
72613
|
+
handler: (args, ctx) => {
|
|
72614
|
+
const a = args;
|
|
72615
|
+
const started = performance.now();
|
|
72616
|
+
const out = ctx.engine.feaCheckBeam(JSON.stringify(a));
|
|
72617
|
+
return textResult7({
|
|
72618
|
+
solve_ms: Math.round(performance.now() - started),
|
|
72619
|
+
...out
|
|
72620
|
+
});
|
|
72621
|
+
},
|
|
72622
|
+
behavior: behavior({})
|
|
72349
72623
|
}
|
|
72350
72624
|
];
|
|
72351
72625
|
}
|
|
@@ -80049,8 +80323,8 @@ var init_server3 = __esm({
|
|
|
80049
80323
|
init_order_feed();
|
|
80050
80324
|
init_animate();
|
|
80051
80325
|
PKG_VERSION = (() => {
|
|
80052
|
-
if ("0.9.4-main.
|
|
80053
|
-
return "0.9.4-main.
|
|
80326
|
+
if ("0.9.4-main.39") {
|
|
80327
|
+
return "0.9.4-main.39";
|
|
80054
80328
|
}
|
|
80055
80329
|
try {
|
|
80056
80330
|
const req = createRequire2(import.meta.url);
|
|
@@ -80059,8 +80333,8 @@ var init_server3 = __esm({
|
|
|
80059
80333
|
return "0.0.0";
|
|
80060
80334
|
}
|
|
80061
80335
|
})();
|
|
80062
|
-
BUILD_SHA = "
|
|
80063
|
-
BUILD_TIME = "2026-07-26T13:
|
|
80336
|
+
BUILD_SHA = "08cd95d4487b522463d0a1a3b58b84935a1f7d08";
|
|
80337
|
+
BUILD_TIME = "2026-07-26T13:56:33.334Z";
|
|
80064
80338
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
80065
80339
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
80066
80340
|
INSTANCE_ID = randomUUID6().slice(0, 8);
|
|
@@ -80208,6 +80482,7 @@ var init_server3 = __esm({
|
|
|
80208
80482
|
"solve_thermal",
|
|
80209
80483
|
"simulate_flow",
|
|
80210
80484
|
"analyze_structure",
|
|
80485
|
+
"beam_check",
|
|
80211
80486
|
"simulate_neutron_shield",
|
|
80212
80487
|
"simulate_lattice_gauge",
|
|
80213
80488
|
"analyze_tolerance_stackup",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcad/mcp",
|
|
3
|
-
"version": "0.9.4-main.
|
|
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": "
|
|
23
|
-
"builtAt": "2026-07-26T13:
|
|
22
|
+
"sha": "08cd95d4487b522463d0a1a3b58b84935a1f7d08",
|
|
23
|
+
"builtAt": "2026-07-26T13:56:33.334Z"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/vcad_kernel_wasm_bg.wasm
CHANGED
|
Binary file
|