* 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>
`BoardOutlineCommands.add_mounting_hole` produced footprints with an empty
library:name id (`(footprint "" ...)` in the .kicad_pcb), which KiCad's GUI
Move tool refuses to select — users couldn't drag the resulting MHs in the
editor. It also emitted the pad with the default `*.Cu` + `*.Mask` LSET on
NPTHs; with `padDiameter > diameter` that creates phantom copper annular
rings on every Cu layer and trips clearance DRC against neighbouring nets.
Repro: call `add_mounting_hole` with `position={x:117,y:84.5,unit:"mm"},
diameter:3.2, padDiameter:3.5`. The resulting MH is unselectable and DRC
reports phantom F.Cu pad shorts to neighbouring component pads.
Changes:
- Set a real FPID via `module.SetFPID(pcbnew.LIB_ID(lib, name))`. Default
is `MountingHole:MountingHole_<diameter:g>mm` (e.g. 3.2 → 3.2mm); a new
optional `footprintLibId` parameter overrides.
- For NPTH (the default `plated:false`), restrict the pad's LSET to
`F.Mask` + `B.Mask` only. PTH path is unchanged — the default Cu+Mask
LSET is correct there.
- Update the schema in `tool_schemas.py`: previously advertised
`x`/`y`/`diameter` at the top level, but the impl reads
`position={x,y,unit}`, `padDiameter`, `plated`. Schema now matches the
implementation and exposes the new `footprintLibId` param.
- New `tests/test_add_mounting_hole.py` regression suite (7 tests) asserting
SetFPID is invoked with non-empty lib:name (default + override forms),
NPTH SetLayerSet excludes any Cu layer, and PTH does not call
SetLayerSet (preserves default Cu+Mask).
PWR_FLAG instances use a #FLG reference prefix, not #PWR, so their
pin positions were never registered as virtual connection anchors in
_parse_virtual_connections. As a result, find_orphaned_wires reported
wire ends terminating on a PWR_FLAG as dangling. Other call sites
(schematic_analysis.py:127, kicad_interface.py:3814) already recognize
#FLG as a power symbol; align this site with them.
Also coerce a previously-validated Optional[int] to int in board/layers.py
so the file passes mypy (required by the pre-commit hook); behavior is
unchanged because the value is already None-checked above.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
board.GetLayerStack() was removed in KiCad 9. Call SetLayerName and
SetLayerType directly on the board instead, and grow the copper layer
count via SetCopperLayerCount when adding inner layers. Without this,
add_layer raises AttributeError on any KiCad 9 installation.
Mechanical application of the `.gitattributes` rules from the prior commit.
All 50 files differ only in line endings — verified by
`git diff --cached --ignore-all-space` being empty.
Before: main had 42 CRLF + 27 LF Python files plus mixed-ending in YAML,
templates, and shell scripts. After: every text file is LF (except the
Windows-native *.ps1, *.bat scripts which remain CRLF per gitattributes).
This eliminates the noisy-diff failure mode seen in PR #102, where a
small logic change produced a 918-line diff due to whole-file CRLF→LF
conversion.
Add isort configuration (profile=black, line_length=100) to pyproject.toml,
add isort pre-commit hook, and auto-sort imports across all Python source files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add [tool.black] config to pyproject.toml and Black hook to
.pre-commit-config.yaml (rev 26.3.1), then auto-format all Python
source and test files with line-length=100, target-version=py310.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add .pre-commit-config.yaml with pre-commit-hooks v5.0.0 (trailing
whitespace, end-of-file fixer, yaml/json checks, large file guard,
merge conflict detection). Add minimal pyproject.toml. Auto-fix
trailing whitespace and missing end-of-file newlines across the
codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug 1 — Rounded corners missing (src/tools/board.ts schema)
Symptom: Claude sends shape='rectangle' + radius=2.5, but the Zod schema
only listed 'rectangle', 'circle', 'polygon' as valid shapes. The JS
handler never passed cornerRadius through the IPC path, and Python only
generated arcs for shape='rounded_rectangle'. Result: 4 straight lines,
no arcs.
Fix: Added 'rounded_rectangle' and 'cornerRadius' to the Zod schema in
src/tools/board.ts so Claude can send the correct shape directly.
Also added a Python-side auto-upgrade: if shape='rectangle' and
cornerRadius>0, silently promote to 'rounded_rectangle' so legacy
callers still work (python/commands/board/outline.py).
Bug 2 — Board outline placed at (-w/2, -h/2) instead of (0, 0)
Symptom: A 30x30 mm board outline was placed centred at the origin
(start -15 -15) ... (end 15 15) instead of the expected top-left at
(0,0) → (30,30).
Root cause: src/tools/board.ts extracted x/y from params and renamed
them to centerX/centerY before forwarding to Python:
const { x, y, ...otherParams } = params;
callKicadScript('add_board_outline', { centerX: x, centerY: y, ...otherParams })
Python received centerX=0, centerY=0 and used that as the board centre,
placing the outline at (-15,-15)→(+15,+15).
Fix: Pass x/y directly as top-left corner coordinates. Python already
contains the correct logic: center = x + width/2, y + height/2. Removed
the incorrect rename in board.ts:
callKicadScript('add_board_outline', { shape, ...params })
Bug 3 — print() on stdout corrupted JSON-RPC protocol → Timeout
Symptom: Claude Desktop reported 'Timeout' for add_board_outline even
though Python completed the command in <5 ms. KiCAD was falsely launched.
Root cause: Debug print() statements written to stdout (the same channel
used for JSON-RPC responses) injected non-JSON lines into the protocol.
Node.js could not parse the response → timeout → Claude Desktop invented
a 'KiCAD UI required' workaround.
Fix: Removed all print() calls from outline.py. Logger.info/debug writes
to the log file (~/.kicad-mcp/logs/kicad_interface.log) and is safe.
Files changed:
src/tools/board.ts — Zod schema + removed centerX/centerY rename
python/commands/board/outline.py — auto-upgrade rectangle+radius, remove print()
Claude sends {shape:'rectangle', params:{x,y,width,height,...}} but handlers
were reading params.get('width') on the outer dict → None → wrong board size.
outline.py: extract inner = params.get('params', params) at method start,
read all dimensions from inner dict.
kicad_interface.py (_ipc_add_board_outline): delegate 'rectangle' to SWIG
path (same as 'rounded_rectangle') so Claude's shape+dims call is handled
correctly. For polygon IPC path: also unwrap inner params for points/width.
All mounting holes were assigned the same reference "MH", violating
PCB conventions and causing conflicts. Now queries existing footprints
to find the next available MH number.
- Added comprehensive documentation (BUILD_AND_TEST, CLIENT_CONFIG, KNOWN_ISSUES, ROADMAP, etc.)
- Updated core functionality for board outline, size, and utilities
- Added new tools for project, routing, schematic, and UI management
- Included TypeScript SDK with full MCP implementation
- Updated configuration examples for all platforms
- Added changelog and status tracking
- Improved Python utilities with KiCAD process management
- Enhanced resource helpers and server capabilities
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>