Commit Graph

137 Commits

Author SHA1 Message Date
Gavin Colonese
b257b7276d feat: add export_sch_pdf tool (kicad-cli, schematic PDF)
New MCP tool `export_sch_pdf` wrapping `kicad-cli sch export pdf`: exposes
the full option set (drawing-sheet override, theme, B&W,
exclude-drawing-sheet, default-font, the PDF property-popup /
hierarchical-link / metadata excludes, no-background-color, and page
selection). schematicPath is required and validated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:58:50 -04:00
Gavin Colonese
7210e0b59c feat: add export_sch_bom tool (kicad-cli, schematic BOM)
New MCP tool `export_sch_bom` wrapping `kicad-cli sch export bom`: exposes
the full BOM option set (presets, field/label lists, group-by, sort
field/direction, filter, exclude-DNP, include-excluded-from-BOM, and the
field/string/ref/ref-range delimiters, plus keep-tabs/keep-line-breaks).
schematicPath is required and validated. Self-contained interface handler.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:58:25 -04:00
Gavin Colonese
f23f1f658a feat: add export_3d_cli tool (kicad-cli, format-dispatched 3D export)
New MCP tool `export_3d_cli` wrapping the kicad-cli 3D subcommands. A
`format` enum {step,glb,stl,ply,brep,xao,vrml} selects the subcommand and
the handler forwards only flags valid for that subcommand: the shared
geometry/include set for step/glb/stl/ply/brep/xao, no-optimize-step for
STEP only, and units + models-dir/models-relative for VRML. Rich CLI
sibling of the existing export_3d / export_vrml (left untouched). Reads
the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:58:01 -04:00
Gavin Colonese
0faef07650 feat: add export_gerber_single tool (kicad-cli, single-file Gerber)
New MCP tool `export_gerber_single` wrapping `kicad-cli pcb export gerber`:
plots the selected layers to a SINGLE Gerber file (singular sibling of
export_gerbers). Exposes the full single-file Plot option set (X2, netlist
attributes, the four DNP fab-layer modes, soldermask subtraction, aperture
macros, drill-file origin, precision, Protel extension). Reads the saved
.kicad_pcb. Note: this subcommand is deprecated in KiCad 9.0 (still exits 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:57:35 -04:00
Gavin Colonese
ce6cff1b39 feat: add export_pcb_dxf tool (kicad-cli, full layer-plot DXF)
New MCP tool `export_pcb_dxf` wrapping `kicad-cli pcb export dxf`: exposes
the full layer-plot surface (layer/common-layer lists, refdes/value
exclusion, soldermask subtraction, use-contours, use-drill-origin,
border+title, output-units, the four DNP fab-layer modes, drill-shape, and
the single/multi output modes). Reads the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:57:09 -04:00
Gavin Colonese
494c2e1ffb feat: add export_pcb_svg tool (kicad-cli, full layer-plot SVG)
New MCP tool `export_pcb_svg` wrapping `kicad-cli pcb export svg`: exposes
the full layer-plot surface (layer/common-layer lists, mirror, soldermask
subtraction, negative, B&W, theme, the four DNP fab-layer modes,
page-size-mode, fit-page-to-board, exclude-drawing-sheet, drill-shape, and
the single/multi output modes). Rich CLI sibling of the existing SWIG
export_svg (left untouched). Reads the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:56:41 -04:00
Gavin Colonese
f491df4eea feat: add export_pcb_pdf tool (kicad-cli, full layer-plot PDF)
New MCP tool `export_pcb_pdf` wrapping `kicad-cli pcb export pdf`: exposes
the full layer-plot surface (layer/common-layer lists, mirror,
refdes/value exclusion, border+title, soldermask subtraction, the four
DNP fab-layer modes, negative, B&W, theme, drill-shape, and the
single/separate/multipage output modes). Rich CLI sibling of the existing
SWIG export_pdf (left untouched). Reads the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:56:17 -04:00
Gavin Colonese
f6d38f1942 feat: add export_pos tool (kicad-cli, full placement-file option set)
New MCP tool `export_pos` wrapping `kicad-cli pcb export pos`: exposes
side, format, units, bottom-negate-X, drill-file origin, SMD-only,
exclude-through-hole, exclude-DNP and gerber-board-edge. Rich CLI sibling
of the existing SWIG-limited export_position_file (left untouched).
Self-contained interface handler reading the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:55:50 -04:00
Gavin Colonese
ab7a2101af feat: add export_gencad tool (kicad-cli, GenCAD assembly/test format)
New MCP tool `export_gencad` wrapping `kicad-cli pcb export gencad`:
exposes define-var plus the flip-bottom-pads, unique-pins,
unique-footprints, use-drill-origin and store-origin-coord flags.
Self-contained interface handler reading the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:55:26 -04:00
Gavin Colonese
cdaa1eb635 feat: add export_ipcd356 tool (kicad-cli, IPC-D-356 netlist)
New MCP tool `export_ipcd356` wrapping `kicad-cli pcb export ipcd356`:
generates an IPC-D-356 bare-board electrical-test netlist for flying-probe
and bed-of-nails testers. Minimal option set (outputPath + boardPath only,
matching the subcommand's --help). Self-contained interface handler reading
the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:55:02 -04:00
Gavin Colonese
0f07ccd593 feat: add export_odb tool (kicad-cli, ODB++ archive)
New MCP tool `export_odb` wrapping `kicad-cli pcb export odb`: precision,
compression mode (zip/tgz/none), and units. Single ODB++ job archive for
CAM/MES/assembly. Self-contained interface handler reading the saved
.kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:54:37 -04:00
Gavin Colonese
88226d567b feat: add export_ipc2581 tool (kicad-cli, with BOM column mapping)
New MCP tool `export_ipc2581` wrapping `kicad-cli pcb export ipc2581`:
precision, compression, standard version, units, and the five
--bom-col-* field mappings (internal id, mfg P/N, mfg, distributor P/N,
distributor) that control the BOM part data embedded in the IPC-2581
file — directly useful for MES/assembly imports that need a real
internal P/N rather than the description. Self-contained interface
handler reading the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:54:11 -04:00
Gavin Colonese
b37ca6a225 feat: add export_drill tool (kicad-cli, full drill option set)
New MCP tool `export_drill` wrapping `kicad-cli pcb export drill`:
format (excellon/gerber), drill origin, Excellon zero-suppression and
oval formats, units, mirror-Y, minimal header, separate PTH/NPTH files,
drill map generation + map format, and Gerber precision. Self-contained
interface handler reading the saved .kicad_pcb.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:53:45 -04:00
Gavin Colonese
82e5de7aa9 feat: add export_gerbers tool (kicad-cli, full Plot option set)
New MCP tool `export_gerbers` wrapping `kicad-cli pcb export gerbers`,
exposing the complete Plot-dialog option surface that the existing
SWIG-based export_gerber lacks: layer + common-layer lists, X2 on/off,
netlist attributes, soldermask subtraction, aperture macros, the three
DNP fab-layer modes, refdes/value exclusion, border+title, drill-file
origin, coordinate precision (5/6), Protel vs KiCad extensions, and
--board-plot-params to reuse the board's stored plot settings.

Implemented as a self-contained interface handler (_handle_export_gerbers)
that resolves the board path (param or current board) and shells out to
kicad-cli, mirroring the _handle_export_netlist pattern — no dependence
on a live SWIG board, since the KiCad IPC API exposes no plot/export
endpoints. Reads the saved .kicad_pcb on disk.

Wiring: dispatch map + src/tools/export.ts registration + export
category in registry.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:53:19 -04:00
Ravi
4a251b7343 feat(schematic): batch component authoring + connection tools
Adds batch authoring tools that collapse the dozens of round-trips needed to
stand up a schematic into a handful: batch_add_components,
batch_edit_schematic_components, replace_schematic_component,
batch_add_no_connects, batch_connect, and batch_add_and_connect (place a set of
parts and wire them in one call). These reuse the existing single-item handlers
internally, so behavior matches exactly.

- python/commands/schematic_batch.py: SchematicBatchCommands(iface)
- python/commands/schematic_text_utils.py: shared .kicad_sch text helpers
- src/tools/schematic-batch.ts + registry 'schematic_batch' category
- python/kicad_interface.py: import + instantiate + dispatch routes
- tests/test_schematic_batch.py: 13 unit tests

Note: schematic_text_utils.py is also shipped by the field-placement and
hierarchy PRs; if those merge first, drop the duplicate copy on rebase.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:49:04 -04:00
Ravi
7c385b993f feat(schematic): reposition and auto-place Ref/Value field labels
Adds field-placement tools:
- set_schematic_property_position / batch_set_schematic_property_positions:
  move a symbol's Reference/Value field labels
- autoplace_schematic_fields: place every symbol's fields clear of its body
  and nearby net labels (the #1 readability problem in generated schematics)
- check_schematic_layout: audit out-of-bounds / fields-in-body / duplicate
  labels (note: overlaps upstream find_overlapping_elements etc. — reuse or
  drop on request)

Generic .kicad_sch text helpers are factored into commands/schematic_text_utils.py
so the batch/hierarchy modules don't import from one another.

- python/commands/schematic_text_utils.py: shared text/S-expr helpers
- python/commands/schematic_field_layout.py: SchematicFieldLayoutCommands
- src/tools/schematic-layout.ts + registry 'schematic_layout' category
- python/kicad_interface.py: import + instantiate + dispatch routes
- tests/test_schematic_field_layout.py: 23 unit tests incl. end-to-end on real .kicad_sch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:45:17 -04:00
Ravi
a9d7af5edf feat(schematic): hierarchical sheet insertion and subsheet scaffolding
Adds add_hierarchical_sheet (insert a sheet symbol referencing a child
.kicad_sch, with sheet_instances + fixed component instance paths) and
create_hierarchical_subsheet (create the child file + wire it into the parent
in one call). Upstream has add_sheet_pin / add_schematic_hierarchical_label
but no way to create a sheet or stand up a child sheet, so hierarchical
designs can't be built through the MCP server today.

- python/commands/schematic_hierarchy.py: SchematicHierarchyCommands
- src/tools/schematic-hierarchy.ts + registry 'schematic_hierarchy' category
- python/kicad_interface.py: import + instantiate + dispatch routes
- tests/test_schematic_hierarchy.py: 5 unit/integration tests on real .kicad_sch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:41:12 -04:00
mixelpixx
df82ff5c56 Merge pull request #226 from ravishivt/feat/list-symbol-pins
feat(schematic): add list_symbol_pins to read pins from symbol libraries
2026-06-03 17:38:30 -04:00
mixelpixx
02095088e1 Merge pull request #213 from mixelpixx/fix/issue-84-derive-pythonpath
fix(windows): derive PYTHONPATH from the detected KiCAD python (#84)
2026-06-03 17:31:47 -04:00
mixelpixx
f60f21aad2 Merge pull request #216 from stefanobaldo/fix/stdin-close-cleanup
fix(server): close Python backend and exit gracefully when stdin closes
2026-06-03 17:29:05 -04:00
Ravi
f5d7c0aaf4 feat(schematic): add list_symbol_pins to read pins from symbol libraries
Adds list_symbol_pins and batch_list_symbol_pins: read a symbol's pin
number/name/type/local-position straight from the .kicad_sym libraries,
without placing it on a schematic. Fills a gap in library_symbol.py
(which can search/list symbols and read properties but not pins) and
complements get_schematic_pin_locations (placed-symbol coords only).

- python/commands/symbol_pins.py: SymbolPinCommands (stateless)
- src/tools/library-symbol.ts: tool wrappers; registry 'symbol_pins' category
- python/kicad_interface.py: import + instantiate + dispatch routes
- tests/test_symbol_pins.py: 12 unit tests (MagicMock loader; no system KiCad)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 14:36:22 -07:00
Chaitanya Malhotra
3ecf5f38fd fix: connect MCP transport before warm-up to prevent client timeout
Swap Phase 3 (transport connect) before Phase 4 (warm-up) in
server.ts startup. Previously the transport was connected only after
warm-up completed (55-125s), which exceeded the MCP client's 30s
connection timeout on macOS cold-start.

PR #210 fixed the Python-side by daemon-threading warm_cache, but
the Node-side still blocked transport connect on warm-up completion.

Complements #210. Replaces the server.ts portion of #206.
2026-06-01 18:42:50 +05:30
mixelpixx
3b4ffef724 Merge pull request #196 from gcolonese/feat-schematic-justify
feat(schematic): expose justify directive on field labels via MCP
2026-05-30 12:15:28 -04:00
Stefano Baldo
c6395ed0b1 fix(server): close Python backend and exit when stdin closes 2026-05-30 13:08:24 -03:00
mixelpixx
f95de32cdd fix(logging): bound log size + honor LOG_LEVEL + mute kicad-skip (#181)
The server wrote gigabytes to ~/.kicad-mcp/logs and ignored LOG_LEVEL. Three
root causes, all fixed here (the logging carve-out of #182):

- Python (kicad_interface.py): replace the unbounded FileHandler with a
  RotatingFileHandler (10 MB x 3 backups, env-tunable via KICAD_MCP_LOG_MAX_BYTES
  / KICAD_MCP_LOG_BACKUP_COUNT); read the level from KICAD_MCP_LOG_LEVEL or
  LOG_LEVEL (default INFO) instead of hardcoding DEBUG; mute the noisy
  skip / skip.sexp.* loggers to WARNING unless KICAD_MCP_DEBUG_SKIP is set.
- TypeScript (config.ts): honor KICAD_MCP_LOG_LEVEL / LOG_LEVEL for the TS logger.
- TypeScript (logger.ts): size-cap the per-day log files with the same env knobs.
- Docs + a no-network test for the env helpers, skip muting, and that no
  unbounded handler targets kicad_interface.log.

Verified: LOG_LEVEL is now applied, skip is muted, and the file rotates instead
of growing forever. Full suite unchanged from baseline.

The hierarchical-sheet rewrite from #182 is intentionally left out (stays as a
separate PR pending the #169/#170 design discussion).

Co-Authored-By: angelorodem <angelorodem@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 12:07:03 -04:00
mixelpixx
df02627134 fix(windows): derive PYTHONPATH from the detected KiCAD python (#84)
findPythonExecutable already auto-detects KiCAD's bundled python across versions
(10.0/9.0/…) and both Program Files and per-user %LOCALAPPDATA% installs, but the
spawn still defaulted PYTHONPATH to a hardcoded "C:/Program Files/KiCad/9.0/..."
path. So a KiCad 10 or per-user user whose python.exe was found correctly still
got the wrong PYTHONPATH unless they set it by hand — the exact manual fixup
people keep posting in #84.

Add deriveKiCadSitePackages(pythonExe): for a detected KiCAD python at
<root>/<version>/bin/python.exe it returns the matching site-packages
(<...>/bin/Lib/site-packages, or the older lib/python3/dist-packages layout),
verified to exist. Use it as the PYTHONPATH default. Precedence is unchanged at
the top: explicit PYTHONPATH env override still wins; the legacy 9.0 string
remains only as a last-resort fallback, so this can't regress below current
behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 09:47:25 -04:00
mixelpixx
74a153aab4 Merge pull request #207 from karu2003/feat/schematic-mirrorY
feat(schematic): add angle and mirrorY to add_schematic_component
2026-05-30 09:34:45 -04:00
mixelpixx
034ffb79bd Merge pull request #204 from mixelpixx/fix/issue-199-jlcpcb-prebuilt-download
fix(jlcpcb): download prebuilt catalog instead of broken JLCSearch offset loop (#199)
2026-05-30 09:29:48 -04:00
Gavin Colonese
7313037dc3 feat(schematic): expose justify directive on field labels via MCP
Add optional `justify` property to `fieldPositions` entries in
`edit_schematic_component` and to `set_schematic_component_property`.

Changes:
- `python/kicad_interface.py`: new `_set_justify_on_property()` helper
  that adds/replaces/removes the `(justify ...)` token inside a property's
  `(effects ...)` block. Passing "center" (the KiCad default) removes the
  directive entirely. Integrated into `_set_property_in_block()` (for the
  `properties` dict path) and into the `field_positions` loop in
  `_handle_edit_schematic_component()`. `_handle_set_schematic_component_property()`
  now forwards `justify` from params through to the spec dict.
  Also fixes pre-existing mypy type-ignore on `circle.radius` (kipy stub).
- `src/tools/schematic.ts`: extend the `fieldPositions` Zod schema to accept
  `justify?: string | string[]` (array form normalised to a space-separated
  string before the Python call). Add `justify?: string` to
  `set_schematic_component_property`.
- `python/commands/routing.py`: fix pre-existing mypy error — annotate `ex`
  and `ey` as `float` in `_point_to_segment_distance_nm`.
- `python/commands/pin_locator.py`: fix pre-existing mypy error — use explicit
  `str()` cast on `pin_data["number"]` before `dict.get()` call.
- `tests/test_schematic_field_justify.py`: 14 unit + integration tests
  covering add/replace/remove of the justify directive and backward
  compatibility (calls without justify leave existing directives untouched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 15:29:00 -04:00
Jeff Laflamme
6c44273d55 fix(board-view): rebase onto main — fold responseMode + restore jpg
Rebased onto current main (d765bfe). Merged changes:

Board view (kicad-cli, cffi-free):
- Replace pcbnew/PLOT_CONTROLLER + cairosvg with kicad-cli SVG export
  and cffi-free PNG conversion: pymupdf → inkscape → imagemagick chain
- pcbPath optional param with fallback to loaded board
- Restore jpg output via PIL post-processing on PNG bytes
- responseMode inline/file from main's #161: inline returns imageData,
  file writes <board>_2d_view.<ext> and returns filePath
- shutil.which for cross-platform kicad-cli lookup
- Explicit TimeoutExpired catch; errorDetails on all error returns
- Validate fmt strictly; TypeScript returns image type for inline png/jpg

Schematic view (kicad_interface.py):
- Same cffi-free _svg_to_png helper for get_schematic_view and
  get_schematic_view_region (pymupdf → inkscape → imagemagick)

Tests:
- Update test_get_board_2d_view_save_to_file.py to mock subprocess/
  kicad-cli and _svg_to_png instead of PLOT_CONTROLLER/cairosvg
- All 5 responseMode tests pass
2026-05-27 10:50:21 +07:00
ka ru
5a9790301e feat: add angle rotation and mirrorY horizontal flipping for components
- New angle parameter for component rotation (degrees, KiCad CCW-in-screen)
- New mirrorY parameter for horizontal symbol flipping (transistor orientation)
- Property positions (Reference, Value, Footprint) automatically rotated
- Property text effects preserved from library definitions
- Updates to add_schematic_component tool with new parameters
- Implements proper S-expression formatting for (mirror y) attribute
- Text angles and offsets derived from library properties for correct placement
2026-05-26 19:50:54 +02:00
mixelpixx
030d008843 feat(jlcpcb): make the FULL ~10GB catalog reachable + resumable downloads (#199)
People want to be able to pull the whole catalog, and prior downloads stalled or
repeated the same parts. Two fixes:

- Full catalog correctness: yaqwsx's cache.sqlite3 (the full ~10GB set) stores
  category/manufacturer as IDs with no v_components view, so the convert left
  those fields blank. Build an equivalent v_components join for yaqwsx-style
  sources so the full catalog converts with category/subcategory/manufacturer
  populated. Clarify in the tool schema that source="yaqwsx" = full catalog
  (needs 7z) vs cdfer = in-stock subset.
- Resumable downloads: CDFER stream download now uses a read timeout and resumes
  from the partial file via HTTP Range on interruption (up to 5 retries); the
  yaqwsx curl calls use -C - / --retry. Addresses the stall/partial-download
  complaints.

Adds a yaqwsx-schema conversion test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:57:07 -04:00
mixelpixx
cb5f744754 feat(jlcpcb): warn when downloaded catalog is stale (#199)
Per review feedback: CDFER's upstream scraper pipeline has stalled for weeks
(broken cart API + a bug in their scraper), so a "fresh download" can still be
old data. Compute the catalog age from the source Last-Modified header, expose
catalog_age_days, and emit a stale=True + warning when older than 14 days that
points users to source='yaqwsx' (fresh, needs 7z) or source='official'. Surface
the warning in the MCP tool output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:49:40 -04:00
mixelpixx
b21b1410eb fix(jlcpcb): download prebuilt catalog instead of broken JLCSearch offset loop (#199)
The download_jlcpcb_database tool paged the community JLCSearch API with an
offset parameter, but that endpoint is a search front-end that ignores offset
and returns the same first 100 parts on every page, so a full catalog download
was impossible.

Add commands/jlcpcb_downloader.py with a layered strategy that reuses prebuilt
catalogs the whole ecosystem already trusts:
  - CDFER single-file SQLite (primary; no 7z/zip, reliable on Windows)
  - yaqwsx/jlcparts split 7z (fallback; only if a 7z CLI is present)
  - official JLCPCB API (optional; cursor pagination, if credentials set)

Conversion reads CDFER's v_components view (or sniffs the largest table for
yaqwsx), C-prefixes integer lcsc, derives library_type from basic/preferred,
maps mfr->mfr_part, and normalizes price JSON to the manager's [{qty,price}]
shape. Rewire _handle_download_jlcpcb_database to use it (closing/reopening the
manager connection so the on-disk db can be rewritten on Windows). Remove the
broken offset loop from jlcsearch.py (client kept for interactive lookups).
Reduce download_jlcpcb.py to a thin CLI wrapper and update the TS tool schema.

Verified end-to-end against live CDFER: 616k parts downloaded + converted in
~40s, FTS search and price-break parsing correct. New unit tests cover the
conversion and source fall-through; no network in tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:53:12 -04:00
mixelpixx
d765bfec78 Merge pull request #202 from jbjardine/cojbdev/kicad-mcp-backend-state
Add backend state MCP tool
2026-05-24 08:51:34 -04:00
jbjardine
21fc0702ac Add backend state MCP tool 2026-05-23 23:08:23 +02:00
jbjardine
aac80b57b8 Fix MCP error wrapping for DRC payloads 2026-05-23 22:32:15 +02:00
mixelpixx
df558295aa Merge pull request #198 from bhoot1234567890/fix/issue-195-macos-wxapp-warmup
fix: macOS wxApp warm-up + symbol cache eager loading (#195)
2026-05-23 10:04:20 -04:00
Chaitanya Malhotra
ef6c135cda fix: macOS wxApp warm-up + symbol cache eager loading (#195)
Three-phase MCP server startup: wait for Python READY handshake, send
_warmup command (pcbnew.BOARD() triggers wxApp init on macOS), connect
to MCP transport only after warm-up completes.

Also pre-populate symbol library cache during SymbolLibraryManager
init so the first search_symbols call doesn't parse 241 .kicad_sym
files from disk (30-120s). Both warm-up and cache population happen
before tools are registered with the MCP client.

Fixes #195
2026-05-23 05:39:24 +05:30
scorp508
6113e0c27a fix: reconnect IPC backend after KiCad starts (#140)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 17:41:47 -04:00
Gavin Colonese
fa6cdcc0cd feat: add mil unit support across position/coordinate commands (#162)
* feat(units): add mil unit support across all position/coordinate commands

KiCad natively supports mils, so the MCP server should too. Added "mil"
as a valid unit option in tool schemas and updated all unit-to-nanometer
scale conversions across component, routing, outline, view, and IPC
handler code paths. 1 mil = 25400 nm (0.0254 mm).

Also fixes a pre-existing mypy overload error in pin_locator.py (str cast
on dict.get key) that was blocking pre-commit on any Python file change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(units): add mil to TypeScript tool schemas

The Python-side mil support was added but the actual input validation
happens in the TypeScript/Zod schemas. Updated all z.enum(["mm", "inch"])
to include "mil" across board, component, routing, design-rules, and
export tool definitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): replace CP-1252 mojibake with correct Unicode in board.ts

Replace U+00C3 U+00D7 (×) with U+00D7 (×) in add_logo size output string.
Character was mangled when file was saved as CP-1252 instead of UTF-8.

* fix: restore em-dash and fix pre-commit mypy in component/routing

component.py: replace CP-1252 mojibake (â€") with correct Unicode
em-dash (—) in the 'Add to board first' comment. Addresses
maintainer review on PR #162.

routing.py: annotate ex/ey as float at first assignment site in
_point_to_segment_distance_nm so mypy pre-commit hook passes
cleanly on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 17:29:34 -04:00
NiNjA-CodE
76e644e4ef feat(routing): add add_gnd_stitching_vias MCP tool with all-layer collision (#191)
Drop GND stitching vias across the board with collision checking
against every non-GND segment, via, and pad on every copper layer.
PTH vias penetrate the full stackup, so an F.Cu-only check (the most
common shortcut) silently creates shorts on inner / B.Cu copper —
this implementation explicitly walks all layers.

  grid          Regular grid across the board interior. Default
                spacing 5mm.

  around_refs   Densify around specified footprints (e.g. MCUs,
                switching regulators, RF parts). Configurable
                density via densifyRadius.

  in_zones      Restrict placements to candidates inside the filled
                polygons of GND copper zones, so each new via lands
                on copper that's already a GND equipotential.
                Recommended on boards where the GND zone is fragmented:
                these vias actually stitch real polygons rather than
                floating on silkscreen.

All three strategies use the same collision check + intra-call
clump-prevention, so passing `["grid", "around_refs", "in_zones"]`
is a safe kitchen-sink configuration.

  - Auto-detect GND net (tries GND / GROUND / VSS / /GND in order)
    OR explicit `gndNet` parameter.
  - Per-via geometry control: viaSize, viaDrill, clearance.
  - edgeMargin: keep-out distance from board edge.
  - maxVias: cap on total placements (useful for incremental work).
  - dryRun: return placements without modifying the board — for
    previewing before committing.
  - Validates viaDrill < viaSize, rejects unknown strategy names,
    surfaces clear errors when GND net can't be resolved or the
    board outline is missing.

Approach ported from morningfire-pcb-automation
(https://github.com/NiNjA-CodE/morningfire-pcb-automation,
scripts/ground/add_gnd_vias.py). The original parses the PCB text
with regex and writes vias by string concatenation; this port reads
obstacles via the pcbnew API (handles rotated footprints, integrates
with the live in-memory board so two sequential calls see each
other's placements, picks up net codes from the loaded board) and
adds the in_zones strategy, the maxVias cap, and dry-run mode.

Credit is in the docstring, the TypeScript wrapper comment, the MCP
tool description (visible to clients), and the CHANGELOG entry.

tests/test_add_gnd_stitching_vias.py — 18 cases, all passing.
Uses mocked pcbnew objects so the suite runs under both the conftest
stub and a real pcbnew install.

  - grid strategy fills empty board with correct count
  - collision blocks via near a signal track (with extent assertion)
  - GND-net obstacles are correctly ignored
  - around_refs densifies near footprints with bounded extent
  - in_zones rejects candidates outside HitTestFilledArea
  - dryRun does NOT call board.Add
  - actual run calls board.Add per placement
  - maxVias caps total placements
  - intra-call clump prevention (asserts pairwise distance)
  - viaDrill >= viaSize is rejected
  - unknown strategy name is rejected
  - missing GND net returns clear error payload
  - no board loaded returns clear error
  - named GND net (e.g. VSS) is honoured even when GND also exists
  - direct unit tests for _point_to_segment_distance_nm helper

Real-board smoke test on TuneForge_TF001 (4-layer, 44 footprints):
  - GND net auto-detected
  - grid spacing 4mm: 141 placements, 129 blocked by collision
  - grid + in_zones: 140 placed, 15 rejected by zone membership,
    115 blocked by collision

  python/commands/routing.py         (+impl, ~370 LOC)
  python/kicad_interface.py          (+handler registration)
  python/schemas/tool_schemas.py     (+MCP schema)
  src/tools/routing.ts               (+TypeScript surface, builds clean)
  tests/test_add_gnd_stitching_vias.py (+18 tests)
  CHANGELOG.md                       (+Unreleased -> New MCP Tools)
2026-05-19 21:17:25 -04:00
NiNjA-CodE
e2941631c1 feat(autoroute): add best-of-N support (attempts, targetNets, passSchedule) (#190)
The existing single-shot autoroute leaves 1-7 nets unrouted on dense
boards in my testing. Best-of-N drives that to 0 most of the time by
running Freerouting a few times with varied --max-passes and keeping
the SES with the best routing score.

New optional parameters (all backward-compatible):

  attempts:     int, default 1 (unchanged behaviour). When > 1, run
                Freerouting N times and pick the highest-scoring SES.
  targetNets:   list of critical net names. An attempt that routes all
                of them earns a 50,000-point scoring bonus.
  passSchedule: list of --max-passes values to cycle through across
                attempts. Default: [50, 60, 65, 70, 75, 80, 85, 90, 55,
                95] (wraps if attempts > len). Ignored when attempts=1
                (legacy maxPasses still used).

Scoring contract (pinned by tests):
  score = nets_routed * 1000 + segments
  if targetNets and all routed: score += 50_000

  - +1 net always beats any segment-count delta (1000 pt step).
  - Segments break ties at equal net count.
  - Target bonus dominates net-count gains from unrelated nets.

## Implementation notes

  - When attempts > 1, each attempt runs with `-mt 1` (single-thread
    optimisation). Freerouting 2.x's multi-threaded optimiser is
    documented to introduce clearance violations, so forcing
    single-thread during scoring keeps the comparison apples-to-apples.
  - One failed attempt does not abort the whole best-of-N run. The
    failure is recorded in the response under attempts[] with ok=False,
    and the remaining attempts compete for best. If every attempt fails
    the response surfaces a clear error.
  - The winning SES is preserved as <stem>_best.ses next to the
    canonical <stem>.ses so the caller can inspect it after the run.
  - Response shape:
      attempts == 1:  unchanged (no attempts/best_attempt fields)
      attempts > 1:   adds attempts[], best_attempt, best_score,
                      best_ses_path

## Attribution

Scoring approach and default pass schedule ported from
morningfire-pcb-automation
(https://github.com/NiNjA-CodE/morningfire-pcb-automation,
scripts/routing/freeroute_runner.py). Credited in the function
docstring, the TypeScript wrapper comment, the tool description (visible
to MCP clients), and the CHANGELOG entry.

The MCP version adds: cleaner per-attempt result reporting, automatic
single-thread optimisation, graceful degradation on partial failure,
and explicit validation that surfaces clean error payloads for invalid
attempts values.

## Tests

  tests/test_autoroute_score.py             8 cases, scoring contract
  tests/test_autoroute_best_of_n.py         6 cases, orchestration logic

All 14 passing. Tests are pure-Python: subprocess is mocked so the
suite runs in any environment (no Java / Freerouting / KiCad required).

  - Single-attempt response shape unchanged
  - Best-of-three picks the highest-scoring SES
  - One nonzero exit attempt doesn't abort the run
  - passSchedule wraps when attempts exceeds len
  - targetNets bonus wins over higher raw net count
  - attempts=0 rejected with clean error before DSN export
  - +1 net (1000 pts) dominates any segment delta
  - Segments tiebreak at equal net count
  - Quoted net names in SES are normalised vs unquoted targets

TypeScript builds clean.
2026-05-18 23:03:38 -04:00
NiNjA-CodE
983ffc3793 feat(component): add check_courtyard_overlaps MCP tool (#189)
Detects courtyard overlaps between footprints and flags courtyards that
extend past the board outline. Returns overlap pairs with intersection
extents (mm), per-component boundary violations, and a placement summary.

The killer feature for AI-driven workflows is the `positions` parameter,
which accepts hypothetical placements `{ref: [x, y]}` or
`{ref: [x, y, rotation_degrees]}`. The tool evaluates the proposed
placement WITHOUT writing to the board file — so an AI agent can validate
a move_component / place_component before committing it, instead of the
current loop of write -> run DRC -> parse violations -> revert.

## Implementation

- Uses the real courtyard polygons from pcbnew (`fp.GetCourtyard(F_CrtYd)`
  or B_CrtYd) for accurate AABBs even on custom and rotated footprints.
- Falls back to `fp.GetBoundingBox()` when no F/B.Courtyard polygon is
  present.
- For virtual rotation, rotates the four AABB corners and re-axis-aligns.
  Conservative: the rotated-AABB is always >= the rotated-polygon, so
  overlap reports are never false-negatives (may be marginally
  over-cautious on diagonal rectangles, which is the right error bias
  for a placement validator).
- Optional `margin` parameter expands every courtyard by N mm — useful
  for enforcing a manufacturing keepout wider than the symbol's
  declared courtyard.

## Attribution

The approach is ported from morningfire-pcb-automation
(https://github.com/NiNjA-CodE/morningfire-pcb-automation), specifically
`scripts/placement/check_overlaps.py`. The upstream uses a static
per-footprint-type courtyard lookup table; this implementation reads
the real polygons from pcbnew so it works on any footprint without
maintaining a table. Attribution is in the function docstring, the
TypeScript wrapper, the tool's description (visible to MCP clients),
and the CHANGELOG entry.

## Tests

12 pytest cases in tests/test_check_courtyard_overlaps.py, all passing:

  - No overlaps when spaced; overlap detected on intersect
  - Margin pushes borderline pairs into overlap
  - `refs` filter restricts the check
  - Boundary violations are flagged; `include_boundary=false` suppresses
  - Virtual position does not mutate the footprint (asserts
    `SetPosition` is never called)
  - Virtual rotation swaps a tall-narrow courtyard's x/y extents
  - No-board-loaded returns clean error payload
  - Bad position spec (wrong arity) returns clean error payload
  - GetCourtyard() OutlineCount=0 -> fallback to GetBoundingBox()
  - `board_outline` override replaces the Edge.Cuts bbox

Tests use mocked pcbnew objects so they run under both the conftest stub
and a real pcbnew install. Real-board smoke test on a 44-footprint
production board succeeds: 1 known overlap detected (SW1<->SW2), 0
boundary violations, virtual placement test reports 6 expected overlaps.

## Files touched

- python/commands/component.py            (impl + helpers)
- python/kicad_interface.py               (tool registration)
- python/schemas/tool_schemas.py          (MCP schema entry)
- src/tools/component.ts                  (TypeScript surface, builds clean)
- tests/test_check_courtyard_overlaps.py  (12 cases)
- CHANGELOG.md                            (Unreleased -> New MCP Tools)
2026-05-18 23:03:34 -04:00
kevargaso
40d6d6bba1 Add query_zones tool for auditing copper pours (#174)
query_traces silently omits PCB_ZONE_T objects, so layer-usage audits
miss power planes and GND pours entirely. query_zones complements it by
iterating board.Zones() and returning each zone's net, layers, priority,
fill state, min thickness, bounding box, and filled area, with the same
net/layer/boundingBox filter surface as query_traces.
2026-05-18 14:40:18 -04:00
karu2003
5c6b55453e feat(mcp): add route_arc_trace for true PCB arc primitives (SWIG + IPC) (#165) 2026-05-18 14:40:09 -04:00
Gavin Colonese
b69a4eb88b feat(view): save 2D board view to file instead of base64 (#161)
* Feat: save 2D board view to file instead of returning base64

The 2D view was returning base64-encoded image data in JSON, which
often exceeded token/message size limits. Now saves the rendered
image (PNG/JPG/SVG) next to the PCB file and returns the file path.
This makes the output usable by tools that can read image files
directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(board): add opt-in responseMode param to get_board_2d_view

Add a responseMode string parameter (enum: inline | file, default inline)
so callers can choose how the rendered image is delivered.

- inline (default, pre-PR behavior): image bytes are base64-encoded
  and returned in the imageData response field -- backward-compatible.
- file: image is written next to the .kicad_pcb as
  <board>_2d_view.<ext> and filePath is returned -- resolves the
  MCP message-size limit problem on large boards.

Rendering logic is shared between both modes; only response packaging
differs. Updated tool schema (Python + TypeScript) and replaced the
existing test file with 5 focused unit tests covering inline/file modes
for PNG and SVG formats plus the default-is-inline contract.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-18 14:29:00 -04:00
Eugene Mikhantyev
3ea2d03637 style: apply prettier formatting to kicad-server.ts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 20:45:11 +01:00
Eugene Mikhantyev
eda28dc9bf Merge pull request #155 from rek/fix/linux-script-path
fix: resolve kicad_interface.py path using import.meta.url (Linux/macOS)
2026-05-13 20:43:57 +01:00
Eugene Mikhantyev
7cbf5a1e49 Merge pull request #149 from Kulitorum/fix/project-sym-lib-table-scope
Make project-scope sym-lib-table visible to symbol-discovery tools
2026-05-13 20:30:11 +01:00