@vcad/mcp 0.9.4-main.72 → 0.9.4-main.73
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 +379 -43
- 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 c94448044a725e430c38faf507027d5a10b2c68d at 2026-08-17T23:42:23.328Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -10874,6 +10874,46 @@ async function parseKicadPcb2(content) {
|
|
|
10874
10874
|
return null;
|
|
10875
10875
|
}
|
|
10876
10876
|
}
|
|
10877
|
+
function importBindings(wasm3) {
|
|
10878
|
+
const mod = wasm3;
|
|
10879
|
+
const bind = (name) => {
|
|
10880
|
+
const fn = mod[name];
|
|
10881
|
+
if (typeof fn !== "function") {
|
|
10882
|
+
throw new Error(`Altium import is unavailable: this kernel WASM build predates ${name}. Rebuild the kernel WASM (npm run build -w vcad-kernel-wasm) or update @vcad/mcp.`);
|
|
10883
|
+
}
|
|
10884
|
+
return fn;
|
|
10885
|
+
};
|
|
10886
|
+
return {
|
|
10887
|
+
parseAltiumAsciiPcb: (content) => bind("parseAltiumAsciiPcb")(content),
|
|
10888
|
+
parseAltiumPcbDoc: (bytes) => bind("parseAltiumPcbDoc")(bytes),
|
|
10889
|
+
parseAltiumPcbLib: (bytes) => bind("parseAltiumPcbLib")(bytes),
|
|
10890
|
+
parseEagleBrd: (content) => bind("parseEagleBrd")(content)
|
|
10891
|
+
};
|
|
10892
|
+
}
|
|
10893
|
+
async function parseEagleBrd(content) {
|
|
10894
|
+
const wasm3 = await loadEcadWasm();
|
|
10895
|
+
if (!wasm3)
|
|
10896
|
+
return null;
|
|
10897
|
+
return importBindings(wasm3).parseEagleBrd(content);
|
|
10898
|
+
}
|
|
10899
|
+
async function parseAltiumAsciiPcb(content) {
|
|
10900
|
+
const wasm3 = await loadEcadWasm();
|
|
10901
|
+
if (!wasm3)
|
|
10902
|
+
return null;
|
|
10903
|
+
return importBindings(wasm3).parseAltiumAsciiPcb(content);
|
|
10904
|
+
}
|
|
10905
|
+
async function parseAltiumPcbDoc(bytes) {
|
|
10906
|
+
const wasm3 = await loadEcadWasm();
|
|
10907
|
+
if (!wasm3)
|
|
10908
|
+
return null;
|
|
10909
|
+
return importBindings(wasm3).parseAltiumPcbDoc(bytes);
|
|
10910
|
+
}
|
|
10911
|
+
async function parseAltiumPcbLib(bytes) {
|
|
10912
|
+
const wasm3 = await loadEcadWasm();
|
|
10913
|
+
if (!wasm3)
|
|
10914
|
+
return null;
|
|
10915
|
+
return importBindings(wasm3).parseAltiumPcbLib(bytes);
|
|
10916
|
+
}
|
|
10877
10917
|
async function exportKicadPcb2(pcb) {
|
|
10878
10918
|
const wasm3 = await loadEcadWasm();
|
|
10879
10919
|
if (!wasm3)
|
|
@@ -11719,7 +11759,11 @@ __export(dist_exports, {
|
|
|
11719
11759
|
mountingHolesFromPcb: () => mountingHolesFromPcb,
|
|
11720
11760
|
netContinuity: () => netContinuity,
|
|
11721
11761
|
outlineAabb: () => outlineAabb,
|
|
11762
|
+
parseAltiumAsciiPcb: () => parseAltiumAsciiPcb,
|
|
11763
|
+
parseAltiumPcbDoc: () => parseAltiumPcbDoc,
|
|
11764
|
+
parseAltiumPcbLib: () => parseAltiumPcbLib,
|
|
11722
11765
|
parseBindingKey: () => parseBindingKey,
|
|
11766
|
+
parseEagleBrd: () => parseEagleBrd,
|
|
11723
11767
|
parseExpression: () => parse,
|
|
11724
11768
|
parseKicadPcb: () => parseKicadPcb2,
|
|
11725
11769
|
parseXyz: () => parseXyz,
|
|
@@ -41547,6 +41591,26 @@ var init_CHANGELOG = __esm({
|
|
|
41547
41591
|
CHANGELOG_default = {
|
|
41548
41592
|
$schema: "./changelog.schema.json",
|
|
41549
41593
|
entries: [
|
|
41594
|
+
{
|
|
41595
|
+
id: "2026-08-17-altium-pcb-import",
|
|
41596
|
+
version: "0.9.4",
|
|
41597
|
+
date: "2026-08-17",
|
|
41598
|
+
category: "feat",
|
|
41599
|
+
title: "Altium and Eagle PCB import",
|
|
41600
|
+
summary: "Import Altium .PcbDoc boards (native binary or ASCII) and .PcbLib libraries, plus Eagle .brd boards, straight into a live session.",
|
|
41601
|
+
features: [
|
|
41602
|
+
"pcb",
|
|
41603
|
+
"import",
|
|
41604
|
+
"ecad",
|
|
41605
|
+
"altium",
|
|
41606
|
+
"eagle"
|
|
41607
|
+
],
|
|
41608
|
+
mcpTools: [
|
|
41609
|
+
"import_altium",
|
|
41610
|
+
"import_altium_library",
|
|
41611
|
+
"import_eagle"
|
|
41612
|
+
]
|
|
41613
|
+
},
|
|
41550
41614
|
{
|
|
41551
41615
|
id: "2026-08-16-render-z-up",
|
|
41552
41616
|
version: "0.9.4",
|
|
@@ -41559,6 +41623,48 @@ var init_CHANGELOG = __esm({
|
|
|
41559
41623
|
"rendering"
|
|
41560
41624
|
]
|
|
41561
41625
|
},
|
|
41626
|
+
{
|
|
41627
|
+
id: "2026-08-16-render-hidden-lines",
|
|
41628
|
+
version: "0.9.4",
|
|
41629
|
+
date: "2026-08-16",
|
|
41630
|
+
category: "fix",
|
|
41631
|
+
title: "SVG renders draw complete, actually-dashed hidden lines",
|
|
41632
|
+
summary: "Hidden edges on curved features were mostly deleted and the survivors drew solid, leaving a stray line across the part; they now run the full occluded length as proper dashes.",
|
|
41633
|
+
features: [
|
|
41634
|
+
"rendering",
|
|
41635
|
+
"drafting"
|
|
41636
|
+
]
|
|
41637
|
+
},
|
|
41638
|
+
{
|
|
41639
|
+
id: "2026-08-16-render-annotation-scaling",
|
|
41640
|
+
version: "0.9.4",
|
|
41641
|
+
date: "2026-08-16",
|
|
41642
|
+
category: "fix",
|
|
41643
|
+
title: "Render overlays stay readable at any size",
|
|
41644
|
+
summary: "The axes gizmo, part labels and dimensions now scale with the canvas instead of staying 1024px-sized, dimension lines clear round parts, and labels no longer land on the geometry they name.",
|
|
41645
|
+
features: [
|
|
41646
|
+
"rendering",
|
|
41647
|
+
"drafting"
|
|
41648
|
+
],
|
|
41649
|
+
mcpTools: [
|
|
41650
|
+
"render_view"
|
|
41651
|
+
]
|
|
41652
|
+
},
|
|
41653
|
+
{
|
|
41654
|
+
id: "2026-08-16-render-aa-and-depth-cue",
|
|
41655
|
+
version: "0.9.4",
|
|
41656
|
+
date: "2026-08-16",
|
|
41657
|
+
category: "fix",
|
|
41658
|
+
title: "Smoother renders: anti-aliasing and a per-pixel depth cue",
|
|
41659
|
+
summary: "Isometric renders anti-alias their silhouettes and linework, flat faces no longer break into hard tonal wedges at triangle boundaries, and edge strokes sit on their geometry instead of half a stroke off.",
|
|
41660
|
+
features: [
|
|
41661
|
+
"rendering",
|
|
41662
|
+
"tessellation"
|
|
41663
|
+
],
|
|
41664
|
+
mcpTools: [
|
|
41665
|
+
"render_view"
|
|
41666
|
+
]
|
|
41667
|
+
},
|
|
41562
41668
|
{
|
|
41563
41669
|
id: "2026-08-16-loon-sheet-metal-bindings",
|
|
41564
41670
|
version: "0.9.4",
|
|
@@ -41578,6 +41684,20 @@ var init_CHANGELOG = __esm({
|
|
|
41578
41684
|
"flat_pattern_from_solid"
|
|
41579
41685
|
]
|
|
41580
41686
|
},
|
|
41687
|
+
{
|
|
41688
|
+
id: "2026-08-16-cli-sheet-metal-fold",
|
|
41689
|
+
version: "0.9.4",
|
|
41690
|
+
date: "2026-08-16",
|
|
41691
|
+
category: "fix",
|
|
41692
|
+
title: "Sheet-metal parts export from the CLI again",
|
|
41693
|
+
summary: "vcad info/export and vcad-render now fold sheet-metal chains \u2014 including jogs and bend relief \u2014 instead of reporting zero triangles, and a root that fails to evaluate says why.",
|
|
41694
|
+
features: [
|
|
41695
|
+
"sheet-metal",
|
|
41696
|
+
"cli",
|
|
41697
|
+
"loon",
|
|
41698
|
+
"export"
|
|
41699
|
+
]
|
|
41700
|
+
},
|
|
41581
41701
|
{
|
|
41582
41702
|
id: "2026-08-16-cli-loon-input-everywhere",
|
|
41583
41703
|
version: "0.9.4",
|
|
@@ -55478,6 +55598,8 @@ var init_tool_metadata = __esm({
|
|
|
55478
55598
|
import_step: { title: "Import STEP", annotations: RW },
|
|
55479
55599
|
import_urdf: { title: "Import URDF", annotations: RW },
|
|
55480
55600
|
import_kicad: { title: "Import KiCad", annotations: RW },
|
|
55601
|
+
import_altium: { title: "Import Altium", annotations: RW },
|
|
55602
|
+
import_altium_library: { title: "Import Altium Library", annotations: RO },
|
|
55481
55603
|
import_eagle: { title: "Import EAGLE", annotations: RW },
|
|
55482
55604
|
open_in_browser: { title: "Open in Browser", annotations: RW },
|
|
55483
55605
|
// ── Physics gym ────────────────────────────────────────────────────────
|
|
@@ -78484,10 +78606,72 @@ function readSource(input, label) {
|
|
|
78484
78606
|
}
|
|
78485
78607
|
throw new Error("Provide either `filename` or `content_base64`");
|
|
78486
78608
|
}
|
|
78609
|
+
function readSourceBytes(input, label) {
|
|
78610
|
+
const { filename, content_base64 } = input;
|
|
78611
|
+
if (content_base64) {
|
|
78612
|
+
const buf = Buffer.from(content_base64, "base64");
|
|
78613
|
+
if (buf.length === 0) throw new Error("content_base64 decoded to zero bytes");
|
|
78614
|
+
if (buf.length > MAX_PCB_BYTES) {
|
|
78615
|
+
throw new Error(`${label} content exceeds the ${MAX_PCB_BYTES} byte limit`);
|
|
78616
|
+
}
|
|
78617
|
+
return buf;
|
|
78618
|
+
}
|
|
78619
|
+
if (filename) {
|
|
78620
|
+
if (isRemoteDeployment()) {
|
|
78621
|
+
throw new Error(
|
|
78622
|
+
`This hosted server has no filesystem access \u2014 pass the ${label} contents as \`content_base64\` instead of \`filename\`.`
|
|
78623
|
+
);
|
|
78624
|
+
}
|
|
78625
|
+
const filepath = resolveWithinRoot(
|
|
78626
|
+
filename,
|
|
78627
|
+
process.env.VCAD_MCP_EXPORT_DIR ?? process.cwd()
|
|
78628
|
+
);
|
|
78629
|
+
if (!existsSync5(filepath)) throw new Error(`${label} file not found`);
|
|
78630
|
+
const stat = statSync3(filepath);
|
|
78631
|
+
if (!stat.isFile()) throw new Error(`${label} path is not a regular file`);
|
|
78632
|
+
if (stat.size > MAX_PCB_BYTES) {
|
|
78633
|
+
throw new Error(`${label} file exceeds the ${MAX_PCB_BYTES} byte limit`);
|
|
78634
|
+
}
|
|
78635
|
+
return readFileSync8(filepath);
|
|
78636
|
+
}
|
|
78637
|
+
throw new Error("Provide either `filename` or `content_base64`");
|
|
78638
|
+
}
|
|
78639
|
+
function registerBoard(pcb, name) {
|
|
78640
|
+
const doc = createDocument();
|
|
78641
|
+
doc.nodes["1"] = {
|
|
78642
|
+
id: 1,
|
|
78643
|
+
name,
|
|
78644
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78645
|
+
op: { type: "PcbBoard", board: pcb }
|
|
78646
|
+
};
|
|
78647
|
+
doc.roots.push({ root: 1, material: "__pcb_fr4__" });
|
|
78648
|
+
doc.materials["__pcb_fr4__"] = {
|
|
78649
|
+
name: "__pcb_fr4__",
|
|
78650
|
+
color: [0.05, 0.35, 0.15],
|
|
78651
|
+
roughness: 0.6,
|
|
78652
|
+
metallic: 0
|
|
78653
|
+
};
|
|
78654
|
+
return registerSession(doc);
|
|
78655
|
+
}
|
|
78656
|
+
function boardSummary(pcb) {
|
|
78657
|
+
return {
|
|
78658
|
+
footprints: pcb.footprints.length,
|
|
78659
|
+
nets: pcb.nets.length,
|
|
78660
|
+
outline_vertices: pcb.outline?.vertices?.length ?? 0,
|
|
78661
|
+
traces: pcb.traces.length,
|
|
78662
|
+
vias: pcb.vias.length,
|
|
78663
|
+
zones: pcb.zones.length,
|
|
78664
|
+
design_rules: {
|
|
78665
|
+
trace_width: pcb.rules?.defaultRules?.traceWidth,
|
|
78666
|
+
clearance: pcb.rules?.defaultRules?.clearance,
|
|
78667
|
+
via_diameter: pcb.rules?.defaultRules?.viaDiameter
|
|
78668
|
+
}
|
|
78669
|
+
};
|
|
78670
|
+
}
|
|
78487
78671
|
function boardName(input) {
|
|
78488
78672
|
if (input.name) return input.name;
|
|
78489
78673
|
if (input.filename) {
|
|
78490
|
-
return input.filename.replace(/^.*[/\\]/, "").replace(/\.kicad_pcb$/i, "");
|
|
78674
|
+
return input.filename.replace(/^.*[/\\]/, "").replace(/\.(kicad_pcb|pcbdoc|pcblib|brd)$/i, "");
|
|
78491
78675
|
}
|
|
78492
78676
|
return "Imported board";
|
|
78493
78677
|
}
|
|
@@ -78512,21 +78696,7 @@ async function importKicad(input) {
|
|
|
78512
78696
|
"KiCad parsing is unavailable (kernel WASM is missing parseKicadPcb) or the file produced no board."
|
|
78513
78697
|
);
|
|
78514
78698
|
}
|
|
78515
|
-
const
|
|
78516
|
-
doc.nodes["1"] = {
|
|
78517
|
-
id: 1,
|
|
78518
|
-
name: boardName(args),
|
|
78519
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78520
|
-
op: { type: "PcbBoard", board: pcb }
|
|
78521
|
-
};
|
|
78522
|
-
doc.roots.push({ root: 1, material: "__pcb_fr4__" });
|
|
78523
|
-
doc.materials["__pcb_fr4__"] = {
|
|
78524
|
-
name: "__pcb_fr4__",
|
|
78525
|
-
color: [0.05, 0.35, 0.15],
|
|
78526
|
-
roughness: 0.6,
|
|
78527
|
-
metallic: 0
|
|
78528
|
-
};
|
|
78529
|
-
const documentId = registerSession(doc);
|
|
78699
|
+
const documentId = registerBoard(pcb, boardName(args));
|
|
78530
78700
|
const onlyPlacement = pcb.traces.length === 0 && pcb.vias.length === 0;
|
|
78531
78701
|
return {
|
|
78532
78702
|
content: [
|
|
@@ -78535,19 +78705,7 @@ async function importKicad(input) {
|
|
|
78535
78705
|
text: JSON.stringify({
|
|
78536
78706
|
success: true,
|
|
78537
78707
|
document_id: documentId,
|
|
78538
|
-
summary:
|
|
78539
|
-
footprints: pcb.footprints.length,
|
|
78540
|
-
nets: pcb.nets.length,
|
|
78541
|
-
outline_vertices: pcb.outline?.vertices?.length ?? 0,
|
|
78542
|
-
traces: pcb.traces.length,
|
|
78543
|
-
vias: pcb.vias.length,
|
|
78544
|
-
zones: pcb.zones.length,
|
|
78545
|
-
design_rules: {
|
|
78546
|
-
trace_width: pcb.rules?.defaultRules?.traceWidth,
|
|
78547
|
-
clearance: pcb.rules?.defaultRules?.clearance,
|
|
78548
|
-
via_diameter: pcb.rules?.defaultRules?.viaDiameter
|
|
78549
|
-
}
|
|
78550
|
-
},
|
|
78708
|
+
summary: boardSummary(pcb),
|
|
78551
78709
|
...onlyPlacement ? {
|
|
78552
78710
|
note: "Placement + netlist only \u2014 the file carried no routed copper."
|
|
78553
78711
|
} : {}
|
|
@@ -78556,12 +78714,138 @@ async function importKicad(input) {
|
|
|
78556
78714
|
]
|
|
78557
78715
|
};
|
|
78558
78716
|
}
|
|
78559
|
-
function
|
|
78560
|
-
return
|
|
78561
|
-
|
|
78562
|
-
|
|
78717
|
+
function isCompoundFile(bytes) {
|
|
78718
|
+
return bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([208, 207, 17, 224, 161, 177, 26, 225]));
|
|
78719
|
+
}
|
|
78720
|
+
async function importAltium(input) {
|
|
78721
|
+
const args = input ?? {};
|
|
78722
|
+
let bytes;
|
|
78723
|
+
try {
|
|
78724
|
+
bytes = readSourceBytes(args, "Altium");
|
|
78725
|
+
} catch (e) {
|
|
78726
|
+
return importError(e instanceof Error ? e.message : String(e));
|
|
78727
|
+
}
|
|
78728
|
+
const binary = isCompoundFile(bytes);
|
|
78729
|
+
let pcb;
|
|
78730
|
+
try {
|
|
78731
|
+
pcb = binary ? await parseAltiumPcbDoc(new Uint8Array(bytes)) : await parseAltiumAsciiPcb(bytes.toString("utf8"));
|
|
78732
|
+
} catch (e) {
|
|
78733
|
+
return importError(
|
|
78734
|
+
`Could not parse the Altium ${binary ? "binary" : "ASCII"} .PcbDoc: ` + (e instanceof Error ? e.message : String(e))
|
|
78735
|
+
);
|
|
78736
|
+
}
|
|
78737
|
+
if (!pcb) {
|
|
78738
|
+
return importError(
|
|
78739
|
+
"Altium parsing is unavailable (kernel WASM is missing parseAltiumPcbDoc)."
|
|
78740
|
+
);
|
|
78741
|
+
}
|
|
78742
|
+
const documentId = registerBoard(pcb, boardName(args));
|
|
78743
|
+
const onlyPlacement = pcb.traces.length === 0 && pcb.vias.length === 0;
|
|
78744
|
+
return {
|
|
78745
|
+
content: [
|
|
78746
|
+
{
|
|
78747
|
+
type: "text",
|
|
78748
|
+
text: JSON.stringify({
|
|
78749
|
+
success: true,
|
|
78750
|
+
document_id: documentId,
|
|
78751
|
+
source_format: binary ? "PcbDoc (binary)" : "PcbDoc (ASCII)",
|
|
78752
|
+
summary: boardSummary(pcb),
|
|
78753
|
+
...onlyPlacement ? { note: "Placement + netlist only \u2014 the file carried no routed copper." } : {},
|
|
78754
|
+
...binary ? {
|
|
78755
|
+
note_binary: "Binary .PcbDoc record layouts are reconstructed, not specified by Altium, and are validated against real open-hardware projects. Geometry that decoded implausibly fails the import outright rather than importing wrong, but spot-check with render_pcb before fabricating. Copper pours and text are not imported."
|
|
78756
|
+
} : {}
|
|
78757
|
+
})
|
|
78758
|
+
}
|
|
78759
|
+
]
|
|
78760
|
+
};
|
|
78761
|
+
}
|
|
78762
|
+
async function importAltiumLibrary(input) {
|
|
78763
|
+
const args = input ?? {};
|
|
78764
|
+
let bytes;
|
|
78765
|
+
try {
|
|
78766
|
+
bytes = readSourceBytes(args, "Altium library");
|
|
78767
|
+
} catch (e) {
|
|
78768
|
+
return importError(e instanceof Error ? e.message : String(e));
|
|
78769
|
+
}
|
|
78770
|
+
let lib;
|
|
78771
|
+
try {
|
|
78772
|
+
lib = await parseAltiumPcbLib(new Uint8Array(bytes));
|
|
78773
|
+
} catch (e) {
|
|
78774
|
+
return importError(
|
|
78775
|
+
`Could not parse the Altium .PcbLib: ${e instanceof Error ? e.message : String(e)}`
|
|
78776
|
+
);
|
|
78777
|
+
}
|
|
78778
|
+
if (!lib) {
|
|
78779
|
+
return importError(
|
|
78780
|
+
"Altium parsing is unavailable (kernel WASM is missing parseAltiumPcbLib)."
|
|
78781
|
+
);
|
|
78782
|
+
}
|
|
78783
|
+
return {
|
|
78784
|
+
content: [
|
|
78785
|
+
{
|
|
78786
|
+
type: "text",
|
|
78787
|
+
text: JSON.stringify({
|
|
78788
|
+
success: true,
|
|
78789
|
+
footprint_count: lib.footprints.length,
|
|
78790
|
+
footprints: lib.footprints.map((f) => ({
|
|
78791
|
+
name: f.name,
|
|
78792
|
+
pad_count: f.pads.length,
|
|
78793
|
+
pads: f.pads.map((p) => ({
|
|
78794
|
+
number: p.number,
|
|
78795
|
+
type: p.pad_type,
|
|
78796
|
+
position: p.position,
|
|
78797
|
+
rotation: p.rotation,
|
|
78798
|
+
drill: p.drill?.diameter ?? null
|
|
78799
|
+
}))
|
|
78800
|
+
}))
|
|
78801
|
+
})
|
|
78802
|
+
}
|
|
78803
|
+
]
|
|
78804
|
+
};
|
|
78805
|
+
}
|
|
78806
|
+
async function importEagle(input) {
|
|
78807
|
+
const args = input ?? {};
|
|
78808
|
+
let content;
|
|
78809
|
+
try {
|
|
78810
|
+
content = readSource(args, "Eagle");
|
|
78811
|
+
} catch (e) {
|
|
78812
|
+
return importError(e instanceof Error ? e.message : String(e));
|
|
78813
|
+
}
|
|
78814
|
+
if (!content.includes("<eagle") && !content.includes("<board")) {
|
|
78815
|
+
return importError(
|
|
78816
|
+
"This does not look like an Eagle XML .brd file. Eagle 5 and earlier wrote a binary .brd \u2014 open it in Eagle 6+ (which converts it to XML) and save, then import that."
|
|
78817
|
+
);
|
|
78818
|
+
}
|
|
78819
|
+
let pcb;
|
|
78820
|
+
try {
|
|
78821
|
+
pcb = await parseEagleBrd(content);
|
|
78822
|
+
} catch (e) {
|
|
78823
|
+
return importError(
|
|
78824
|
+
`Could not parse the Eagle .brd: ${e instanceof Error ? e.message : String(e)}`
|
|
78825
|
+
);
|
|
78826
|
+
}
|
|
78827
|
+
if (!pcb) {
|
|
78828
|
+
return importError(
|
|
78829
|
+
"Eagle parsing is unavailable (kernel WASM is missing parseEagleBrd)."
|
|
78830
|
+
);
|
|
78831
|
+
}
|
|
78832
|
+
const documentId = registerBoard(pcb, boardName(args));
|
|
78833
|
+
const onlyPlacement = pcb.traces.length === 0 && pcb.vias.length === 0;
|
|
78834
|
+
return {
|
|
78835
|
+
content: [
|
|
78836
|
+
{
|
|
78837
|
+
type: "text",
|
|
78838
|
+
text: JSON.stringify({
|
|
78839
|
+
success: true,
|
|
78840
|
+
document_id: documentId,
|
|
78841
|
+
summary: boardSummary(pcb),
|
|
78842
|
+
...onlyPlacement ? { note: "Placement + netlist only \u2014 the file carried no routed copper." } : {}
|
|
78843
|
+
})
|
|
78844
|
+
}
|
|
78845
|
+
]
|
|
78846
|
+
};
|
|
78563
78847
|
}
|
|
78564
|
-
var MAX_PCB_BYTES, importKicadSchema, importEagleSchema, toolDefs46;
|
|
78848
|
+
var MAX_PCB_BYTES, importKicadSchema, importAltiumSchema, importAltiumLibrarySchema, importEagleSchema, toolDefs46;
|
|
78565
78849
|
var init_import_pcb = __esm({
|
|
78566
78850
|
"src/tools/import-pcb.ts"() {
|
|
78567
78851
|
"use strict";
|
|
@@ -78589,16 +78873,50 @@ var init_import_pcb = __esm({
|
|
|
78589
78873
|
}
|
|
78590
78874
|
}
|
|
78591
78875
|
};
|
|
78876
|
+
importAltiumSchema = {
|
|
78877
|
+
type: "object",
|
|
78878
|
+
properties: {
|
|
78879
|
+
filename: {
|
|
78880
|
+
type: "string",
|
|
78881
|
+
description: "Path to a .PcbDoc file on the server filesystem (relative to VCAD_MCP_EXPORT_DIR or cwd). Native binary and ASCII exports are both accepted. On hosted servers pass content_base64 instead."
|
|
78882
|
+
},
|
|
78883
|
+
content_base64: {
|
|
78884
|
+
type: "string",
|
|
78885
|
+
description: "Base64-encoded .PcbDoc contents (preferred for hosted servers)."
|
|
78886
|
+
},
|
|
78887
|
+
name: {
|
|
78888
|
+
type: "string",
|
|
78889
|
+
description: "Board name for display (default: filename without extension)."
|
|
78890
|
+
}
|
|
78891
|
+
}
|
|
78892
|
+
};
|
|
78893
|
+
importAltiumLibrarySchema = {
|
|
78894
|
+
type: "object",
|
|
78895
|
+
properties: {
|
|
78896
|
+
filename: {
|
|
78897
|
+
type: "string",
|
|
78898
|
+
description: "Path to a .PcbLib footprint library (binary or ASCII)."
|
|
78899
|
+
},
|
|
78900
|
+
content_base64: {
|
|
78901
|
+
type: "string",
|
|
78902
|
+
description: "Base64-encoded .PcbLib contents."
|
|
78903
|
+
}
|
|
78904
|
+
}
|
|
78905
|
+
};
|
|
78592
78906
|
importEagleSchema = {
|
|
78593
78907
|
type: "object",
|
|
78594
78908
|
properties: {
|
|
78595
78909
|
filename: {
|
|
78596
78910
|
type: "string",
|
|
78597
|
-
description: "Path to a .brd file (Eagle XML
|
|
78911
|
+
description: "Path to a .brd file (Eagle XML, Eagle 6+)."
|
|
78598
78912
|
},
|
|
78599
78913
|
content_base64: {
|
|
78600
78914
|
type: "string",
|
|
78601
|
-
description: "Base64-encoded .brd contents."
|
|
78915
|
+
description: "Base64-encoded .brd contents (preferred for hosted servers)."
|
|
78916
|
+
},
|
|
78917
|
+
name: {
|
|
78918
|
+
type: "string",
|
|
78919
|
+
description: "Board name for display (default: filename without extension)."
|
|
78602
78920
|
}
|
|
78603
78921
|
}
|
|
78604
78922
|
};
|
|
@@ -78611,13 +78929,29 @@ var init_import_pcb = __esm({
|
|
|
78611
78929
|
handler: async (a) => await importKicad(a),
|
|
78612
78930
|
behavior: behavior({ writesDoc: true, geometry: true })
|
|
78613
78931
|
},
|
|
78932
|
+
{
|
|
78933
|
+
name: "import_altium",
|
|
78934
|
+
pack: "ecad",
|
|
78935
|
+
description: "Import an Altium .PcbDoc board into a live session \u2014 board outline, footprints with pads + nets, design rules, and any routed traces/vias. Accepts both the native binary file and an ASCII export (detected automatically). Returns a document_id ready for render_pcb, run_drc, get_pad_positions, route_nets, and export_gerber. The binary path fails closed on record layouts it cannot decode rather than importing a partially-correct board. Pass content_base64 on hosted servers.",
|
|
78936
|
+
inputSchema: importAltiumSchema,
|
|
78937
|
+
handler: async (a) => await importAltium(a),
|
|
78938
|
+
behavior: behavior({ writesDoc: true, geometry: true })
|
|
78939
|
+
},
|
|
78940
|
+
{
|
|
78941
|
+
name: "import_altium_library",
|
|
78942
|
+
pack: "ecad",
|
|
78943
|
+
description: "Read an Altium .PcbLib footprint library (binary or ASCII) and return its footprint patterns with pads, shapes, and drills. A library is not a board, so no document_id is minted.",
|
|
78944
|
+
inputSchema: importAltiumLibrarySchema,
|
|
78945
|
+
handler: async (a) => await importAltiumLibrary(a),
|
|
78946
|
+
behavior: behavior({})
|
|
78947
|
+
},
|
|
78614
78948
|
{
|
|
78615
78949
|
name: "import_eagle",
|
|
78616
78950
|
pack: "ecad",
|
|
78617
|
-
description: "Import an Eagle .brd
|
|
78951
|
+
description: "Import an Eagle .brd board (XML, Eagle 6+) into a live session \u2014 board outline, packages placed as footprints with pads, signals as nets, and any hand-routed traces/vias. Returns a document_id ready for render_pcb, run_drc, get_pad_positions, route_nets, and export_gerber. Pass content_base64 on hosted servers.",
|
|
78618
78952
|
inputSchema: importEagleSchema,
|
|
78619
|
-
handler: (a) => importEagle(a),
|
|
78620
|
-
behavior: behavior({})
|
|
78953
|
+
handler: async (a) => await importEagle(a),
|
|
78954
|
+
behavior: behavior({ writesDoc: true, geometry: true })
|
|
78621
78955
|
}
|
|
78622
78956
|
];
|
|
78623
78957
|
}
|
|
@@ -83649,8 +83983,8 @@ var init_server3 = __esm({
|
|
|
83649
83983
|
init_order_feed();
|
|
83650
83984
|
init_animate();
|
|
83651
83985
|
PKG_VERSION = (() => {
|
|
83652
|
-
if ("0.9.4-main.
|
|
83653
|
-
return "0.9.4-main.
|
|
83986
|
+
if ("0.9.4-main.73") {
|
|
83987
|
+
return "0.9.4-main.73";
|
|
83654
83988
|
}
|
|
83655
83989
|
try {
|
|
83656
83990
|
const req = createRequire2(import.meta.url);
|
|
@@ -83659,8 +83993,8 @@ var init_server3 = __esm({
|
|
|
83659
83993
|
return "0.0.0";
|
|
83660
83994
|
}
|
|
83661
83995
|
})();
|
|
83662
|
-
BUILD_SHA = "
|
|
83663
|
-
BUILD_TIME = "2026-08-
|
|
83996
|
+
BUILD_SHA = "c94448044a725e430c38faf507027d5a10b2c68d";
|
|
83997
|
+
BUILD_TIME = "2026-08-17T23:42:23.328Z";
|
|
83664
83998
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
83665
83999
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
83666
84000
|
INSTANCE_ID = randomUUID7().slice(0, 8);
|
|
@@ -83856,6 +84190,8 @@ var init_server3 = __esm({
|
|
|
83856
84190
|
"import_step",
|
|
83857
84191
|
"import_urdf",
|
|
83858
84192
|
"import_kicad",
|
|
84193
|
+
"import_altium",
|
|
84194
|
+
"import_altium_library",
|
|
83859
84195
|
"import_eagle",
|
|
83860
84196
|
"open_in_browser",
|
|
83861
84197
|
// ── Physics gym ────────────────────────────────────────────
|
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.73",
|
|
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-08-
|
|
22
|
+
"sha": "c94448044a725e430c38faf507027d5a10b2c68d",
|
|
23
|
+
"builtAt": "2026-08-17T23:42:23.328Z"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/vcad_kernel_wasm_bg.wasm
CHANGED
|
Binary file
|