Skip to main content
Glama
mpeyal

kicad-forge

by mpeyal
README.md
# kicad-forge

[![tests](https://github.com/mpeyal/kicad-forge/actions/workflows/tests.yml/badge.svg)](https://github.com/mpeyal/kicad-forge/actions/workflows/tests.yml)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)

One MCP server for KiCad **analysis + authoring + simulation**. Reads and writes
KiCad files directly as S-expressions, so nothing here needs KiCad installed
except the two things that genuinely do (cross-checking netlists with
`kicad-cli`, and reading the stock symbol libraries).

Built and tested in one session. **141/141 tests pass** — 113 unit, 28 over the
live MCP protocol. Read the *Honest status* section before you trust it with a
board you are paying to fabricate.

---

## Install

```bash
git clone https://github.com/mpeyal/kicad-forge.git
cd kicad-forge
pip install mcp
```

That is the only dependency. Everything else is the standard library.

Register with Claude Desktop — `%APPDATA%\Claude\claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "kicad-forge": {
      "command": "C:\\Program Files\\KiCad\\10.0\\bin\\python.exe",
      "args": ["C:\\path\\to\\kicad-forge\\run_server.py"]
    }
  }
}
```

Using **KiCad's bundled Python** is recommended but not required — it is not
needed for parsing (this server never imports `pcbnew`), but it guarantees
`kicad-cli` is on PATH for `verify_netlist`. Any Python 3.10+ works.

Claude Code:

```bash
claude mcp add kicad-forge -s user -- python -m kicad_forge.server
```

> [!IMPORTANT]
> **If Claude Desktop came from the Microsoft Store**, `%APPDATA%` is
> virtualised and the config is NOT at `AppData\Roaming\Claude\`. It lives at:
>
> ```
> %LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude\claude_desktop_config.json
> ```
>
> Editing the non-packaged path appears to work and silently does nothing —
> no server, and no log file to tell you why. If you add an entry and no
> `mcp-server-<name>.log` ever appears in `%LOCALAPPDATA%\Claude\Logs`, you
> are editing the wrong file.

Verify: ask *"what are kicad-forge's capabilities?"* — the `capabilities` tool
reports the version, whether KiCad's symbol libraries were found, whether a
SPICE engine was found, and the full coverage manifest.

---

## The 18 tools

**Analysis**

| Tool | Does |
|---|---|
| `analyze_project` | Full review: components, nets, analog/digital checks, PCB routing, DFM |
| `list_components` | Components with prefix/value filters |
| `list_nets` | Netlist derived geometrically from the files |
| `trace_connections` | Every connection from a component or one pin |
| `verify_netlist` | **Diffs our netlist against `kicad-cli`'s own export** |

**PCB**

| Tool | Does |
|---|---|
| `pcb_statistics` | Layers, dimensions, footprints, tracks, vias, zones |
| `pcb_connectivity` | Union-find copper graph: routed / unrouted / islanded nets |
| `list_footprints` | Footprints, filterable by board side |

**Authoring** (atomic writes)

| Tool | Does |
|---|---|
| `create_kicad_project` | `.kicad_pro` + `.kicad_sch` + `.kicad_pcb`, optional outline |
| `add_component` | Place a symbol from KiCad's libraries or the built-in set |
| `add_generic_ic` | Rectangular IC from a pin list, for parts in no library |
| `add_wire` / `add_label` | Wires and local/global/hierarchical labels |
| `component_pin_positions` | Exact pin coordinates, so wires land on pins |

**Simulation**

| Tool | Does |
|---|---|
| `export_spice_netlist` | SPICE netlist; active devices are *skipped*, never guessed |
| `simulate_subcircuits` | Auto-generates testbenches, runs them, compares to analytics |
| `run_spice` | Run an arbitrary deck |

---

## Write safety

You asked for no safety layer, and there is none: no dry-run gating, no `.bak`
files, no confirmation prompts. Writes go straight through.

What is kept, because it is the absence of a bug rather than a safety feature:

1. **Atomic replace.** Serialise to a temp file in the same directory → `fsync`
   → `os.replace()` (atomic on Windows and POSIX). A crash mid-write leaves the
   original file untouched instead of truncated.
2. **Verify before replace.** The serialised bytes are re-parsed and compared
   to the source tree. A mismatch raises and writes nothing.
3. **Atom text is preserved verbatim.** `1.270000` stays `1.270000`; `0.1` stays
   `0.1`. Numbers are never round-tripped through `float()` and reformatted.

That last point is why the round-trip is **byte-identical** on real KiCad files —
your git diffs show only what actually changed.

**Still: keep your projects in git.** Nothing above protects you from a logic bug.

---

## Honest status

### Verified

- **Verified against real LTspice 24.1.9.** Decks generated by this tool were
  executed by LTspice on Windows:

  | Testbench | Analytical | LTspice | Error |
  |---|---|---|---|
  | RC low-pass 10k/100n | 159.154943092 Hz | 159.154943622 Hz | 3.3e-7 % |
  | Divider 100k/100k | 0.500000000 V | 0.500000000 V | 0 % |

  The LTspice `.log` files are checked in under `tests/fixtures/ltspice/` and
  asserted on every run.
- **100% agreement with KiCad's own netlist exporter.** A 24-component ESP32
  board was generated by this tool, opened in **Eeschema 10.0.5**, and its
  netlist exported by KiCad. All **18 nets match exactly** — same pin
  membership, same net names, zero missing, zero invented. KiCad's `.net` file
  is checked in as `tests/fixtures/esp32_sensor_kicad_reference.net` and is
  asserted against on every test run.
- **Byte-identical round trip** on real KiCad 9 `.kicad_sch` and `.kicad_pcb`.
- **Known-answer detector maths**, hand-computed and asserted exactly:
  regulator Vout 5.1956 V, divider ratio 0.153976, RC cutoff 159.1549 Hz,
  I²C rise time 398.2 ns.
- **55 component-value spellings** (`4k7`, `1R0`, `2u2`, `100nF/50V`,
  `1uF 0805`, `4.7 kOhm`, …).
- **Pin geometry** against hand-computed transforms at 0/90/180/270° and both
  mirrors; all pins land on KiCad's 1.27 mm grid.
- **Cross-validated with `kiutils`**, an independent third-party parser: it
  reads the schematics this tool writes and agrees on the component set.
- **28 checks over the real MCP protocol** — subprocess, stdio, handshake,
  `tools/list`, `tools/call`.

### Not verified

- **No large-corpus validation.** kicad-happy is regression-tested against
  5,800+ real projects. This has been checked against one real KiCad-exported
  netlist, two real KiCad files, and fixtures it generated itself. Expect
  format edge cases on real-world boards — run `verify_netlist` on your own
  designs, which is exactly what it is for.
- **PCB authoring is not implemented.** Only project scaffolding and board
  outline. No footprint placement, no routing, no Gerber export.

### Deliberately not implemented

`detectors.COVERAGE` lists this in the tool output too, so the model using the
server sees it rather than reading an empty findings list as a clean board:

EMC / radiated emissions · thermal / junction temperature · component lifecycle
& EOL · MPN sourcing · voltage derating · signal integrity & impedance ·
differential-pair skew · datasheet extraction · power sequencing · Gerber
verification.

**An empty findings list is not a clean bill of health.** For those domains use
kicad-happy, which implements them properly.

### Known limitations

- I²C detection is name-based; a bus with unconventional net names is missed.
- Divider detection is topological — it reports voltage-sensing dividers too.
- RC filter detection requires the cap's far side to be ground.
- PCB pad/track contact uses bounding boxes; rotated non-rectangular pads are
  approximated.
- Arc tracks are measured by their chord, so arc-heavy boards under-report
  total track length.
- The `VREF_TABLE` covers 28 regulators. Unknown parts report `needs-data`
  rather than guessing a Vref.

---

## Where this fits

It does not replace kicad-happy — that has 61 detectors and a 5,800-project
regression corpus, and this does not. What this adds is the combination you
asked for in one server: **authoring and simulation and analysis**, with no
`pcbnew`/SWIG dependency and no KiCad required for the read path.

A reasonable split: this for authoring and netlist/SPICE work, kicad-happy for
the review pass before you order boards.

---

## Tests

```bash
python kicad_forge/tests/test_suite.py         # 113 unit tests
python kicad_forge/tests/test_mcp_protocol.py  # 28 protocol tests
python kicad_forge/tests/make_fixture.py       # regenerate the known-answer fixture
```

MIT.

TDQS

B3.2/5.0

Scored across 18 tools

Disambiguation4/5

Tools map to distinct actions and resources, so an agent can usually tell list_components from list_nets or pcb_statistics. The slight overlap comes from the SPICE-related tools and analyze_project, which can approach similar territory but are differentiated by purpose and output.

Naming Consistency4/5

Most tools follow a clear snake_case verb_noun or area_noun pattern: list_*, add_*, pcb_*, simulate_*, run_*. A few names like capabilities and component_pin_positions are noun phrases rather than verb-first actions, but the overall style is consistent enough to navigate.

Tool Count4/5

With 18 tools the server is slightly above the typical well-scoped 3-15 range, but the tool set spans schematic analysis, PCB analysis, project creation, and simulation. Each tool has a distinct job, so the larger count is justified rather than bloated.

Completeness3/5

The server covers project creation, additive schematic editing, net tracing, PCB statistics/connectivity, and SPICE workflows well. However, it lacks update, remove, or move operations for components, wires, and labels, leaving the editing lifecycle incomplete for a design tool.

Maintenance

ActivityMaintained
ResponsivenessNo issues