@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 +1 -1
- package/index.mjs +76 -301
- package/package.json +3 -3
- package/vcad_kernel_wasm_bg.wasm +2 -2
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 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))
|
|
55898
|
-
|
|
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")
|
|
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")
|
|
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
|
|
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
|
|
55832
|
+
function runMutation(toolName, rawArgs, doc, documentId) {
|
|
55955
55833
|
const toolArgs = normalizeNodeRefs(toolName, rawArgs, doc);
|
|
55956
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
87495
|
-
return "0.10.0-main.
|
|
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 = "
|
|
87505
|
-
BUILD_TIME = "2026-10-
|
|
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.
|
|
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": "
|
|
23
|
-
"builtAt": "2026-10-
|
|
22
|
+
"sha": "8c6f34bb566075c1bcec315c497a5c29fac2b820",
|
|
23
|
+
"builtAt": "2026-10-21T00:57:52.178Z"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/vcad_kernel_wasm_bg.wasm
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/vcad_kernel_wasm_bg.wasm
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 35331246 bytes
|
|
4
|
+
sha256: ec88981a02ecc87b2594ff2052c095e5c041220594f11f4834bb99da3b4e6187
|