From 3d9497ebe593405c7b7a9626b168637960152f45 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 11 Mar 2026 11:04:24 +0100 Subject: [PATCH 1/2] fix: snapshot saves logs+prompt to logs/ subdir; routing via_x under start pad; router disabled --- python/commands/export.py | 5 +++- python/commands/routing.py | 18 ++++++++++---- python/kicad_interface.py | 49 +++++++++++++++++++++++++++++++++++--- src/server.ts | 4 +++- src/tools/project.ts | 3 ++- 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/python/commands/export.py b/python/commands/export.py index 2307506..d15e533 100644 --- a/python/commands/export.py +++ b/python/commands/export.py @@ -695,7 +695,10 @@ class ExportCommands: session_lines = all_lines[session_start:] timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - dest = os.path.join(project_dir, f"mcp_log_{timestamp}.txt") + from pathlib import Path + logs_dir = Path(project_dir) / "logs" + logs_dir.mkdir(exist_ok=True) + dest = str(logs_dir / f"mcp_log_{timestamp}.txt") with open(dest, "w", encoding="utf-8") as f: f.writelines(session_lines) diff --git a/python/commands/routing.py b/python/commands/routing.py index d630943..5dd0101 100644 --- a/python/commands/routing.py +++ b/python/commands/routing.py @@ -144,9 +144,14 @@ class RoutingCommands: if not net: net = start_pad.GetNetname() or end_pad.GetNetname() or "" - # Detect if pads are on different copper layers → need via - start_layer = start_pad.GetLayerName() - end_layer = end_pad.GetLayerName() + # Detect if pads are on different copper layers → need via. + # SMD pad.GetLayer() reports F.Cu even on flipped B.Cu footprints in + # KiCAD 9 SWIG. Use footprint.GetLayer() instead — it always reflects + # the actual placed layer after Flip(). + fp_start = footprints[from_ref] + fp_end = footprints[to_ref] + start_layer = self.board.GetLayerName(fp_start.GetLayer()) + end_layer = self.board.GetLayerName(fp_end.GetLayer()) copper_layers = {"F.Cu", "B.Cu"} needs_via = ( start_layer in copper_layers @@ -155,8 +160,11 @@ class RoutingCommands: ) if needs_via: - # Place via at midpoint between the two pads - via_x = (start_pos.x + end_pos.x) / 2 / scale + # Place via directly below the start pad (same X). + # Using the geometric midpoint X causes all vias to stack at + # the same X when pads are back-to-back mirrored (e.g. J1/J2 + # on F.Cu/B.Cu): midpoint is always the board center. + via_x = start_pos.x / scale via_y = (start_pos.y + end_pos.y) / 2 / scale # Trace on start layer: start_pad → via diff --git a/python/kicad_interface.py b/python/kicad_interface.py index 3435d07..40ca581 100644 --- a/python/kicad_interface.py +++ b/python/kicad_interface.py @@ -1679,9 +1679,11 @@ class KiCADInterface: """Copy the entire project folder to a snapshot directory for checkpoint/resume.""" import shutil from datetime import datetime + from pathlib import Path try: step = params.get("step", "") label = params.get("label", "") + prompt_text = params.get("prompt", "") # Determine project directory from loaded board or explicit path project_dir = None if self.board: @@ -1693,19 +1695,60 @@ class KiCADInterface: if not project_dir or not os.path.isdir(project_dir): return {"success": False, "message": "Could not determine project directory for snapshot"} - base_name = Path(project_dir).name ts = datetime.now().strftime("%Y%m%d_%H%M%S") + + # Save prompt + log into logs/ subdirectory before snapshotting + logs_dir = Path(project_dir) / "logs" + logs_dir.mkdir(exist_ok=True) + + prompt_file = None + if prompt_text: + prompt_filename = f"PROMPT_step{step}_{ts}.md" if step else f"PROMPT_{ts}.md" + prompt_file = logs_dir / prompt_filename + prompt_file.write_text(prompt_text, encoding="utf-8") + logger.info(f"Prompt saved: {prompt_file}") + + # Copy current MCP session log into logs/ before snapshotting + import platform + system = platform.system() + if system == "Windows": + mcp_log_dir = os.path.join(os.environ.get("APPDATA", ""), "Claude", "logs") + elif system == "Darwin": + mcp_log_dir = os.path.expanduser("~/Library/Logs/Claude") + else: + mcp_log_dir = os.path.expanduser("~/.config/Claude/logs") + mcp_log_src = os.path.join(mcp_log_dir, "mcp-server-kicad.log") + mcp_log_dest = None + if os.path.exists(mcp_log_src): + with open(mcp_log_src, "r", encoding="utf-8", errors="replace") as f: + all_lines = f.readlines() + session_start = 0 + for i, line in enumerate(all_lines): + if "Initializing server" in line: + session_start = i + session_lines = all_lines[session_start:] + log_filename = f"mcp_log_step{step}_{ts}.txt" if step else f"mcp_log_{ts}.txt" + mcp_log_dest = logs_dir / log_filename + with open(mcp_log_dest, "w", encoding="utf-8") as f: + f.writelines(session_lines) + logger.info(f"MCP session log saved: {mcp_log_dest} ({len(session_lines)} lines)") + + base_name = Path(project_dir).name suffix_parts = [p for p in [f"step{step}" if step else "", label, ts] if p] snapshot_name = base_name + "_snapshot_" + "_".join(suffix_parts) - snapshot_dir = str(Path(project_dir).parent / snapshot_name) + snapshots_base = Path(project_dir) / "snapshots" + snapshots_base.mkdir(exist_ok=True) + snapshot_dir = str(snapshots_base / snapshot_name) - shutil.copytree(project_dir, snapshot_dir) + shutil.copytree(project_dir, snapshot_dir, ignore=shutil.ignore_patterns("snapshots")) logger.info(f"Project snapshot saved: {snapshot_dir}") return { "success": True, "message": f"Snapshot saved: {snapshot_name}", "snapshotPath": snapshot_dir, "sourceDir": project_dir, + "promptSaved": str(prompt_file) if prompt_file else None, + "mcpLogSaved": str(mcp_log_dest) if mcp_log_dest else None, } except Exception as e: logger.error(f"snapshot_project error: {e}") diff --git a/src/server.ts b/src/server.ts index 4f93cbd..4596693 100644 --- a/src/server.ts +++ b/src/server.ts @@ -232,7 +232,9 @@ export class KiCADMcpServer { logger.info("Registering KiCAD tools, resources, and prompts..."); // Register router tools FIRST (for tool discovery and execution) - registerRouterTools(this.server, this.callKicadScript.bind(this)); + // NOTE: Router disabled — causes Claude to hallucinate tool schemas via search_tools/execute_tool. + // All tools are registered directly below and are immediately visible to Claude. + // registerRouterTools(this.server, this.callKicadScript.bind(this)); // Register all tools registerProjectTools(this.server, this.callKicadScript.bind(this)); diff --git a/src/tools/project.ts b/src/tools/project.ts index 4a1b739..1e59179 100644 --- a/src/tools/project.ts +++ b/src/tools/project.ts @@ -84,8 +84,9 @@ export function registerProjectTools(server: McpServer, callKicadScript: Functio { step: z.string().describe("Step number or identifier, e.g. '1' or '2'"), label: z.string().describe("Short label for this checkpoint, e.g. 'schematic_ok' or 'layout_ok'"), + prompt: z.string().optional().describe("Full prompt text to save as PROMPT_step{step}_{timestamp}.md alongside the snapshot"), }, - async (args: { step: string; label: string }) => { + async (args: { step: string; label: string; prompt?: string }) => { const result = await callKicadScript("snapshot_project", args); return { content: [{ From e6deaa2408c511abf8dfc1c82970f68d824ae7e8 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 11 Mar 2026 11:19:52 +0100 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20add=20v2.2.3=20changelog=20+=20READ?= =?UTF-8?q?ME=20=E2=80=94=20developer=20mode,=20snapshot=20logging,=20priv?= =?UTF-8?q?acy=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 44 ++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ca4ad..cc382a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,102 @@ All notable changes to the KiCAD MCP Server project are documented here. +## [2.2.3] - 2026-03-11 + +### Merged: PR #57 (Kletternaut/demo/rpiCSI-videotest → main) + +This release incorporates 28 commits developed and live-tested during a full +Raspberry Pi CSI adapter PCB design session. All tools listed below were validated +end-to-end using Claude Desktop + KiCAD 9 on Windows. + +### New MCP Tools + +- `connect_passthrough` — Schematic-only tool that wires all pins of one connector + directly to the matching pins of another (e.g. J1 pin N → J2 pin N). Creates nets + named with a configurable prefix (`netPrefix`). Designed for FFC/ribbon cable + passthrough adapters. **Schematic only — do not call for PCB routing.** + +- `sync_schematic_to_board` — Imports all net/pad assignments from the schematic + into the open PCB file. Required after `connect_passthrough` before routing can + start. Returns `pads_assigned` count for verification. + +- `snapshot_project` — Saves a named checkpoint of the entire project folder into a + `snapshots/` subdirectory inside the project. Allows resuming from a known-good + state without redoing earlier steps. Accepts `step`, `label`, and optional `prompt` + parameters. + +- `run_erc` — Runs KiCAD's Electrical Rules Check on the schematic and returns + violations as structured JSON. + +- `import_svg_logo` — Converts an SVG file to PCB silkscreen polygons and places + them on a specified layer. + +### Bug Fixes + +- `route_pad_to_pad`: **Critical fix for B.Cu footprints in KiCAD 9.** `pad.GetLayerName()` + always returned `F.Cu` for SMD pads on flipped footprints (KiCAD 9 SWIG bug). + Fix: use `footprint.GetLayer()` instead, which correctly reflects the placed layer + after `Flip()`. Without this fix, no vias were inserted for back-to-back connectors. + +- `route_pad_to_pad`: Via was placed at the geometric midpoint between the two pads. + For back-to-back mirrored connectors (J1 F.Cu / J2 B.Cu) this caused all 15 vias + to stack at the same X coordinate (board center). Fix: via is now placed at the + X coordinate of the start pad (`via_x = start_pos.x`), producing 15 parallel + vertical traces. + +- `place_component` (B.Cu footprints): `Flip()` was called before `board.Add()`, + causing KiCAD 9 to hang for ~30 seconds. Fix: `board.Add()` first, then `Flip()`. + +- `add_board_outline`: Three separate bugs fixed — incorrect cornerRadius fallback, + wrong top-left origin default, and broken arc delegation for IPC rounded rectangles. + +- `snapshot_project`: Snapshots were saved one level above the project directory, + cluttering the parent folder. Fix: snapshots now go into `/snapshots/`. + +- MCP server log timestamp was always UTC/ISO. Fix: now uses local system time. + +- `search_tools` (router pattern): direct tools like `snapshot_project` were invisible + to the router. Fix: direct tool names added to the router's known-tool list. + +### Developer Mode (`KICAD_MCP_DEV=1`) + +Set the environment variable `KICAD_MCP_DEV=1` in your Claude Desktop config to +enable developer features: + +```json +"env": { + "KICAD_MCP_DEV": "1" +} +``` + +**What it does:** +- `export_gerber` automatically copies the current MCP session log into the project's + `logs/` subdirectory as `mcp_log_.txt`. +- `snapshot_project` copies the MCP session log into `logs/` at every checkpoint as + `mcp_log_step_.txt`. +- If a `prompt` parameter is passed to `snapshot_project`, it is saved as + `PROMPT_step_.md` alongside the log. + +**Purpose:** Makes it easy to include the full tool call history when filing a bug +report or GitHub issue — just attach the log file from the project's `logs/` folder. + +> ⚠️ **Privacy warning:** The MCP session log contains the **complete conversation +> history** between Claude and the MCP server, including all tool parameters and +> responses. When sharing a project directory (e.g. as a ZIP attachment in a GitHub +> issue), **review or delete the `logs/` folder first** to avoid accidentally +> disclosing sensitive file paths, component names, or design details. + +### Snapshot Logging (always active) + +Regardless of dev mode, `snapshot_project` now always saves a copy of the current +MCP session log into `/logs/` at each checkpoint. This means every project +automatically retains a traceable record of which tools were called and in what order. + +> ⚠️ **Same privacy note applies:** the `logs/` directory inside your project folder +> contains tool call history. Do not share it publicly without reviewing its contents. + +--- + ## [2.2.2-alpha] - 2026-03-01 ### New MCP Tools diff --git a/README.md b/README.md index 8542b79..7d9e161 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,50 @@ https://github.com/mixelpixx/arduino-ide +## What's New in v2.2.3 + +### New Tools: FFC/Ribbon Cable Passthrough Workflow + +A complete workflow for designing passthrough adapter boards (e.g. Raspberry Pi CSI +cable adapters) is now supported: + +1. `connect_passthrough` — wires all pins of one connector to the matching pins of + another in the schematic (J1 pin N → J2 pin N, auto-named nets). +2. `sync_schematic_to_board` — imports the net assignments into the PCB. +3. `route_pad_to_pad` — routes each connection with automatic via insertion when + pads are on opposite copper layers. +4. `snapshot_project` — saves a named checkpoint into `/snapshots/`. + +### Bug Fixes (KiCAD 9 / Windows) + +- **Via insertion for B.Cu footprints** — `route_pad_to_pad` now correctly detects + when a footprint is on B.Cu and inserts the required via. (KiCAD 9 SWIG returned + `F.Cu` for all SMD pads regardless of layer — fixed.) +- **Board outline rounded corners** — `add_board_outline` now correctly applies + `cornerRadius` when `shape="rounded_rectangle"`. +- **B.Cu placement hang** — placing a footprint on B.Cu no longer causes a ~30s + freeze in KiCAD 9. + +### Developer Mode + +Set `KICAD_MCP_DEV=1` in your Claude Desktop MCP environment to automatically save +the MCP session log into the project's `logs/` folder on every `export_gerber` and +`snapshot_project` call. Useful for debugging and for attaching to GitHub issues. + +```json +"env": { + "KICAD_MCP_DEV": "1" +} +``` + +> ⚠️ **Privacy warning:** The session log contains your full tool call history +> (including file paths and design details). **Review or delete `logs/` before +> sharing a project directory publicly.** + +See [CHANGELOG](CHANGELOG.md) for the full list of changes in this release. + +--- + ## What's New in v2.1.0 ### Critical Schematic Workflow Fix + Complete Wiring System (Issue #26)