@vcad/mcp 0.9.4-main.5 → 0.9.4-main.7

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 5d4ed6fea90eb5e449458ec1cf2fcda1ba7aef52 at 2026-07-23T16:38:26.435Z. Source: https://github.com/ecto/vcad
9
+ Built from 3f7779633a486aaa3e0e6d984834c539769923cc at 2026-07-23T23:44:37.186Z. Source: https://github.com/ecto/vcad
package/index.mjs CHANGED
@@ -538,6 +538,7 @@ __export(vcad_kernel_wasm_exports, {
538
538
  camGenerateRoughing3d: () => camGenerateRoughing3d,
539
539
  camGetDefaultTools: () => camGetDefaultTools,
540
540
  camToolpathStats: () => camToolpathStats,
541
+ checkDesignConstraints: () => checkDesignConstraints,
541
542
  checkPrintability: () => checkPrintability,
542
543
  checkSheetMetal: () => checkSheetMetal,
543
544
  circuitAcResponse: () => circuitAcResponse,
@@ -688,6 +689,7 @@ __export(vcad_kernel_wasm_exports, {
688
689
  sliceMesh: () => sliceMesh,
689
690
  sliceMeshWithProgress: () => sliceMeshWithProgress,
690
691
  sliceSolid: () => sliceSolid,
692
+ solveDesignConstraints: () => solveDesignConstraints,
691
693
  solveForwardKinematics: () => solveForwardKinematics,
692
694
  solveSketchSegments: () => solveSketchSegments,
693
695
  textBounds: () => textBounds,
@@ -1139,6 +1141,27 @@ function camToolpathStats(toolpath_json) {
1139
1141
  }
1140
1142
  return takeFromExternrefTable0(ret[0]);
1141
1143
  }
1144
+ function checkDesignConstraints(doc_json) {
1145
+ let deferred3_0;
1146
+ let deferred3_1;
1147
+ try {
1148
+ const ptr0 = passStringToWasm0(doc_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1149
+ const len0 = WASM_VECTOR_LEN;
1150
+ const ret = wasm.checkDesignConstraints(ptr0, len0);
1151
+ var ptr2 = ret[0];
1152
+ var len2 = ret[1];
1153
+ if (ret[3]) {
1154
+ ptr2 = 0;
1155
+ len2 = 0;
1156
+ throw takeFromExternrefTable0(ret[2]);
1157
+ }
1158
+ deferred3_0 = ptr2;
1159
+ deferred3_1 = len2;
1160
+ return getStringFromWasm0(ptr2, len2);
1161
+ } finally {
1162
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
1163
+ }
1164
+ }
1142
1165
  function checkPrintability(solid, printer_profile) {
1143
1166
  _assertClass(solid, Solid);
1144
1167
  const ptr0 = passStringToWasm0(printer_profile, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -2986,6 +3009,29 @@ function sliceSolid(solid, settings, segments) {
2986
3009
  }
2987
3010
  return SliceResult.__wrap(ret[0]);
2988
3011
  }
3012
+ function solveDesignConstraints(doc_json, options_json) {
3013
+ let deferred4_0;
3014
+ let deferred4_1;
3015
+ try {
3016
+ const ptr0 = passStringToWasm0(doc_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
3017
+ const len0 = WASM_VECTOR_LEN;
3018
+ const ptr1 = passStringToWasm0(options_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
3019
+ const len1 = WASM_VECTOR_LEN;
3020
+ const ret = wasm.solveDesignConstraints(ptr0, len0, ptr1, len1);
3021
+ var ptr3 = ret[0];
3022
+ var len3 = ret[1];
3023
+ if (ret[3]) {
3024
+ ptr3 = 0;
3025
+ len3 = 0;
3026
+ throw takeFromExternrefTable0(ret[2]);
3027
+ }
3028
+ deferred4_0 = ptr3;
3029
+ deferred4_1 = len3;
3030
+ return getStringFromWasm0(ptr3, len3);
3031
+ } finally {
3032
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
3033
+ }
3034
+ }
2989
3035
  function solveForwardKinematics(doc_json) {
2990
3036
  const ptr0 = passStringToWasm0(doc_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
2991
3037
  const len0 = WASM_VECTOR_LEN;
@@ -10211,7 +10257,7 @@ async function isEcadAvailable2() {
10211
10257
  const wasm2 = await loadEcadWasm();
10212
10258
  return wasm2 !== null;
10213
10259
  }
10214
- async function solveDesignConstraints(doc, options) {
10260
+ async function solveDesignConstraints2(doc, options) {
10215
10261
  return verifyWithKernel("design constraints", (wasm2) => {
10216
10262
  if (typeof wasm2.solveDesignConstraints !== "function") {
10217
10263
  throw new Error("kernel WASM predates solveDesignConstraints");
@@ -10219,7 +10265,7 @@ async function solveDesignConstraints(doc, options) {
10219
10265
  return JSON.parse(wasm2.solveDesignConstraints(JSON.stringify(doc), JSON.stringify(options ?? {})));
10220
10266
  });
10221
10267
  }
10222
- async function checkDesignConstraints(doc) {
10268
+ async function checkDesignConstraints2(doc) {
10223
10269
  return verifyWithKernel("design constraints (check)", (wasm2) => {
10224
10270
  if (typeof wasm2.checkDesignConstraints !== "function") {
10225
10271
  throw new Error("kernel WASM predates checkDesignConstraints");
@@ -11885,7 +11931,7 @@ __export(dist_exports, {
11885
11931
  buildPartDocument: () => buildPartDocument,
11886
11932
  buildReceipt: () => buildReceipt,
11887
11933
  builtinSymbols: () => builtinSymbols,
11888
- checkDesignConstraints: () => checkDesignConstraints,
11934
+ checkDesignConstraints: () => checkDesignConstraints2,
11889
11935
  checkEnclosureFit: () => checkEnclosureFit,
11890
11936
  checkErc: () => checkErc,
11891
11937
  circuitAcResponse: () => circuitAcResponse2,
@@ -11976,7 +12022,7 @@ __export(dist_exports, {
11976
12022
  searchParts: () => searchParts,
11977
12023
  semanticDiff: () => semanticDiff,
11978
12024
  semanticDiffFallback: () => semanticDiffFallback,
11979
- solveDesignConstraints: () => solveDesignConstraints,
12025
+ solveDesignConstraints: () => solveDesignConstraints2,
11980
12026
  solveForwardKinematics: () => solveForwardKinematics2,
11981
12027
  threeWayMerge: () => threeWayMerge,
11982
12028
  toWorld: () => toWorld,
@@ -41634,6 +41680,20 @@ var init_CHANGELOG = __esm({
41634
41680
  "route_nets"
41635
41681
  ]
41636
41682
  },
41683
+ {
41684
+ id: "2026-07-23-render-view-triangle-guard",
41685
+ version: "0.9.4",
41686
+ date: "2026-07-23",
41687
+ category: "fix",
41688
+ title: "render_view refuses over-dense documents instead of crashing",
41689
+ summary: "Documents tessellating past 120k triangles (e.g. large sphere patterns) now get an actionable error from render_view instead of OOM-killing the server with a >100 MB per-triangle SVG.",
41690
+ features: [
41691
+ "render"
41692
+ ],
41693
+ mcpTools: [
41694
+ "render_view"
41695
+ ]
41696
+ },
41637
41697
  {
41638
41698
  id: "2026-07-23-plane-stitch-maze-rescue",
41639
41699
  version: "0.9.4",
@@ -48462,6 +48522,7 @@ function undoLastSnapshot(documentId) {
48462
48522
  function clearHistory(documentId) {
48463
48523
  sessionHistory.delete(documentId);
48464
48524
  lastChangedParts.delete(documentId);
48525
+ lastTriangleCount.delete(documentId);
48465
48526
  }
48466
48527
  function recordLastChanged(documentId, partIds) {
48467
48528
  if (!documentId) return;
@@ -48470,7 +48531,14 @@ function recordLastChanged(documentId, partIds) {
48470
48531
  function getLastChanged(documentId) {
48471
48532
  return lastChangedParts.get(documentId) ?? null;
48472
48533
  }
48473
- var fallbackDocuments, scopeProvider, documents, nextId, MAX_HISTORY, sessionHistory, lastChangedParts;
48534
+ function recordTriangles(documentId, triangles) {
48535
+ if (!documentId) return;
48536
+ lastTriangleCount.set(documentId, triangles);
48537
+ }
48538
+ function getLastTriangles(documentId) {
48539
+ return lastTriangleCount.get(documentId) ?? null;
48540
+ }
48541
+ var fallbackDocuments, scopeProvider, documents, nextId, MAX_HISTORY, sessionHistory, lastChangedParts, lastTriangleCount;
48474
48542
  var init_session_core = __esm({
48475
48543
  "src/tools/session-core.ts"() {
48476
48544
  "use strict";
@@ -48490,6 +48558,7 @@ var init_session_core = __esm({
48490
48558
  MAX_HISTORY = 50;
48491
48559
  sessionHistory = /* @__PURE__ */ new Map();
48492
48560
  lastChangedParts = /* @__PURE__ */ new Map();
48561
+ lastTriangleCount = /* @__PURE__ */ new Map();
48493
48562
  }
48494
48563
  });
48495
48564
 
@@ -49682,7 +49751,10 @@ var init_loon = __esm({
49682
49751
  if (doc) {
49683
49752
  if (targetId) documents.set(targetId, doc);
49684
49753
  const integrity = computeIntegrity(doc, ctx.engine);
49685
- if (integrity) appendIntegrity(result, integrity);
49754
+ if (integrity) {
49755
+ appendIntegrity(result, integrity);
49756
+ if (targetId) recordTriangles(targetId, integrity.triangles);
49757
+ }
49686
49758
  }
49687
49759
  } catch {
49688
49760
  }
@@ -51570,7 +51642,10 @@ function dispatchRegistryTool(toolName, args, engine) {
51570
51642
  ]);
51571
51643
  if (engine) {
51572
51644
  const integrity = computeIntegrity(doc, engine);
51573
- if (integrity) appendIntegrity(result, integrity);
51645
+ if (integrity) {
51646
+ appendIntegrity(result, integrity);
51647
+ recordTriangles(documentId, integrity.triangles);
51648
+ }
51574
51649
  }
51575
51650
  }
51576
51651
  return result;
@@ -53224,7 +53299,10 @@ function applyEdits(args, engine) {
53224
53299
  ]);
53225
53300
  if (engine) {
53226
53301
  const integrity = computeIntegrity(working, engine);
53227
- if (integrity) appendIntegrity(result, integrity);
53302
+ if (integrity) {
53303
+ appendIntegrity(result, integrity);
53304
+ recordTriangles(documentId, integrity.triangles);
53305
+ }
53228
53306
  }
53229
53307
  }
53230
53308
  return result;
@@ -57223,7 +57301,7 @@ async function setParameters(input, engine) {
57223
57301
  }
57224
57302
  let constraintSolve;
57225
57303
  if ((doc.constraints ?? []).length > 0) {
57226
- const outcome = await solveDesignConstraints(doc);
57304
+ const outcome = await solveDesignConstraints2(doc);
57227
57305
  if (outcome.status === "ok") {
57228
57306
  doc.nodes = outcome.value.document.nodes;
57229
57307
  doc.constraints = outcome.value.document.constraints;
@@ -57240,7 +57318,10 @@ async function setParameters(input, engine) {
57240
57318
  });
57241
57319
  result.structuredContent = { changed };
57242
57320
  const integrity = computeIntegrity(doc, engine);
57243
- if (integrity) appendIntegrity(result, integrity);
57321
+ if (integrity) {
57322
+ appendIntegrity(result, integrity);
57323
+ recordTriangles(documentId, integrity.triangles);
57324
+ }
57244
57325
  return result;
57245
57326
  }
57246
57327
  function parameterGradient(input, engine) {
@@ -57363,7 +57444,7 @@ function describe(c) {
57363
57444
  async function constraintReceiptClaims(doc) {
57364
57445
  const constraints = doc.constraints ?? [];
57365
57446
  if (constraints.length === 0) return [];
57366
- const outcome = await checkDesignConstraints(doc);
57447
+ const outcome = await checkDesignConstraints2(doc);
57367
57448
  const byId = /* @__PURE__ */ new Map();
57368
57449
  if (outcome.status === "ok") {
57369
57450
  for (const r of outcome.value.residuals) byId.set(r.id, r);
@@ -57440,7 +57521,7 @@ function worst(statuses) {
57440
57521
  }
57441
57522
  async function verifyConstraintClaims(doc, receipt) {
57442
57523
  const checks = [];
57443
- const outcome = await checkDesignConstraints(doc);
57524
+ const outcome = await checkDesignConstraints2(doc);
57444
57525
  const byId = /* @__PURE__ */ new Map();
57445
57526
  if (outcome.status === "ok") {
57446
57527
  for (const r of outcome.value.residuals) byId.set(r.id, r);
@@ -67961,7 +68042,7 @@ async function placementDrcIfMoved(doc, report) {
67961
68042
  return pcb ? await summarizePlacementDrc(pcb) : void 0;
67962
68043
  }
67963
68044
  async function solveAndReport(doc, documentId) {
67964
- const outcome = await solveDesignConstraints(doc);
68045
+ const outcome = await solveDesignConstraints2(doc);
67965
68046
  if (outcome.status !== "ok") {
67966
68047
  return err6(`constraint solve unavailable: ${outcome.reason}`);
67967
68048
  }
@@ -68087,7 +68168,7 @@ async function listConstraints(args) {
68087
68168
  hint: "add_constraint persists solver-enforced geometric relationships"
68088
68169
  });
68089
68170
  }
68090
- const outcome = await checkDesignConstraints(ctx.doc);
68171
+ const outcome = await checkDesignConstraints2(ctx.doc);
68091
68172
  const report = outcome.status === "ok" ? outcome.value : void 0;
68092
68173
  const residuals = new Map(report?.residuals.map((r) => [r.id, r.residual]) ?? []);
68093
68174
  const measured = new Map(report?.drivenValues.map((d) => [d.id, d.value]) ?? []);
@@ -68614,6 +68695,13 @@ function complexityGuard(doc) {
68614
68695
  return null;
68615
68696
  }
68616
68697
  async function rasterize(svg, widthPx, background = "white") {
68698
+ const svgBytes = Buffer.byteLength(svg, "utf8");
68699
+ if (svgBytes > MAX_RASTER_SVG_BYTES) {
68700
+ return {
68701
+ png: null,
68702
+ reason: `rasterization refused: SVG is ${svgBytes} bytes (cap ${MAX_RASTER_SVG_BYTES}) \u2014 the document is too dense to raster safely`
68703
+ };
68704
+ }
68617
68705
  let ResvgCtor;
68618
68706
  try {
68619
68707
  ({ Resvg: ResvgCtor } = await import("@resvg/resvg-js"));
@@ -68648,6 +68736,22 @@ async function renderView(args) {
68648
68736
  2048,
68649
68737
  Math.max(64, Number.isFinite(widthRaw) ? Math.round(widthRaw) : DEFAULT_WIDTH_PX)
68650
68738
  );
68739
+ const knownTriangles = documentId ? getLastTriangles(documentId) : null;
68740
+ if (knownTriangles !== null && knownTriangles > MAX_RENDER_TRIANGLES) {
68741
+ return {
68742
+ content: [
68743
+ {
68744
+ type: "text",
68745
+ text: JSON.stringify({
68746
+ error: `render refused: document tessellates to ${knownTriangles} triangles (render limit ${MAX_RENDER_TRIANGLES})`,
68747
+ document_id: documentId,
68748
+ hint: "The renderer emits per-triangle SVG, so documents this dense exhaust memory. Reduce pattern counts / sphere counts, render a subset via `focus`, or inspect numerically via inspect_cad / measure. For full geometry use export_cad."
68749
+ })
68750
+ }
68751
+ ],
68752
+ isError: true
68753
+ };
68754
+ }
68651
68755
  const tooComplex = complexityGuard(doc);
68652
68756
  if (tooComplex) {
68653
68757
  return {
@@ -68901,8 +69005,25 @@ async function renderView(args) {
68901
69005
  ]
68902
69006
  }, [asset]);
68903
69007
  }
68904
- const note = raster.reason === "module-missing" ? "Install @resvg/resvg-js for PNG output; returning raw SVG." : `PNG ${raster.reason}; returning raw SVG.`;
68905
69008
  const svgBytes = Buffer.byteLength(svg, "utf8");
69009
+ if (raster.reason !== "module-missing" && svgBytes > MAX_INLINE_SVG_BYTES) {
69010
+ return {
69011
+ content: [
69012
+ {
69013
+ type: "text",
69014
+ text: JSON.stringify({
69015
+ error: `render failed: ${raster.reason}`,
69016
+ document_id: documentId,
69017
+ view: viewLabel,
69018
+ svg_bytes: svgBytes,
69019
+ hint: "The document is too dense for the per-triangle SVG renderer. Reduce pattern counts, render a subset via `focus`, or inspect numerically via inspect_cad / measure. For full geometry use export_cad."
69020
+ })
69021
+ }
69022
+ ],
69023
+ isError: true
69024
+ };
69025
+ }
69026
+ const note = raster.reason === "module-missing" ? "Install @resvg/resvg-js for PNG output; returning raw SVG." : `PNG ${raster.reason}; returning raw SVG.`;
68906
69027
  return {
68907
69028
  content: [
68908
69029
  {
@@ -69307,7 +69428,7 @@ async function renderStackup(args) {
69307
69428
  });
69308
69429
  return withRenderAssets({ content }, assets);
69309
69430
  }
69310
- var renderViewSchema, SVG_SCALE, DEFAULT_WIDTH_PX, MAX_NODES, MAX_PATTERN_INSTANCES, MAX_INLINE_SVG_BYTES, renderPcbSchema, PCB_MARGIN_MM, RATSNEST_COLOR, renderRatsnestSchema, COPPER_LAYERS2, renderStackupSchema, toolDefs23;
69431
+ var renderViewSchema, SVG_SCALE, DEFAULT_WIDTH_PX, MAX_NODES, MAX_PATTERN_INSTANCES, MAX_RENDER_TRIANGLES, MAX_RASTER_SVG_BYTES, MAX_INLINE_SVG_BYTES, renderPcbSchema, PCB_MARGIN_MM, RATSNEST_COLOR, renderRatsnestSchema, COPPER_LAYERS2, renderStackupSchema, toolDefs23;
69311
69432
  var init_render2 = __esm({
69312
69433
  "src/tools/render.ts"() {
69313
69434
  "use strict";
@@ -69386,6 +69507,8 @@ var init_render2 = __esm({
69386
69507
  DEFAULT_WIDTH_PX = 800;
69387
69508
  MAX_NODES = 1e4;
69388
69509
  MAX_PATTERN_INSTANCES = 5e4;
69510
+ MAX_RENDER_TRIANGLES = 12e4;
69511
+ MAX_RASTER_SVG_BYTES = 64 * 1024 * 1024;
69389
69512
  MAX_INLINE_SVG_BYTES = 256 * 1024;
69390
69513
  renderPcbSchema = {
69391
69514
  type: "object",
@@ -78780,8 +78903,8 @@ var init_server3 = __esm({
78780
78903
  init_order_feed();
78781
78904
  init_animate();
78782
78905
  PKG_VERSION = (() => {
78783
- if ("0.9.4-main.5") {
78784
- return "0.9.4-main.5";
78906
+ if ("0.9.4-main.7") {
78907
+ return "0.9.4-main.7";
78785
78908
  }
78786
78909
  try {
78787
78910
  const req = createRequire2(import.meta.url);
@@ -78790,8 +78913,8 @@ var init_server3 = __esm({
78790
78913
  return "0.0.0";
78791
78914
  }
78792
78915
  })();
78793
- BUILD_SHA = "5d4ed6fea90eb5e449458ec1cf2fcda1ba7aef52";
78794
- BUILD_TIME = "2026-07-23T16:38:26.435Z";
78916
+ BUILD_SHA = "3f7779633a486aaa3e0e6d984834c539769923cc";
78917
+ BUILD_TIME = "2026-07-23T23:44:37.186Z";
78795
78918
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
78796
78919
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
78797
78920
  INSTANCE_ID = randomUUID6().slice(0, 8);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcad/mcp",
3
- "version": "0.9.4-main.5",
3
+ "version": "0.9.4-main.7",
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": "5d4ed6fea90eb5e449458ec1cf2fcda1ba7aef52",
23
- "builtAt": "2026-07-23T16:38:26.435Z"
22
+ "sha": "3f7779633a486aaa3e0e6d984834c539769923cc",
23
+ "builtAt": "2026-07-23T23:44:37.186Z"
24
24
  }
25
25
  }
Binary file