kicad-jlcpcb
README.md
# kicad-jlcpcb
[](LICENSE)
[](#requirements)
[](https://www.kicad.org/)
[](#testing)
[](https://modelcontextprotocol.io/)
> From "I want a board that does X" to a wired `.kicad_pcb` EasyEDA can auto-route and JLCPCB can build — in a single Claude Code conversation.
`kicad-jlcpcb` is a Claude Code plugin + MCP server that automates the tedious half of going from idea to fab. It sources LCSC parts with a hard preference for JLCPCB basic-library stock, auto-fetches pin maps from EasyEDA, places KiCad-stdlib footprints, wires every net by pin **name** (not pad number), and hands off a `.kicad_pcb` that EasyEDA can route and order in two clicks.
```text
┌─ you ──────────────────────────────────┐
│ /pcb-new An ESP32-C3 soil-moisture │
│ sensor, USB-C, 3.3V LDO... │
└────────────────┬───────────────────────┘
│
┌─────────────▼─────────────┐
│ kicad-jlcpcb MCP server │
│ • source parts (LCSC) │
│ • fetch pin maps │
│ • place + wire footprints│
│ • save .kicad_pcb │
└─────────────┬─────────────┘
│
drag into easyeda.com
│
Auto Route
│
Order via JLCPCB
```
---
## Why this plugin
Three recurring friction points in small-batch PCB work, automated:
1. **"Is this part basic or extended on JLCPCB?"** — You stop needing to cross-reference LCSC's UI. The plugin's SQLite-cached jlcparts mirror answers in milliseconds and always prefers basic-tier (no $3/part assembly setup fee).
2. **"What's the right pad number for this IC's `GPIO10`?"** — You stop reading datasheets to build netlists. The plugin queries EasyEDA by LCSC C-number, caches the pin-name → pad-number map, and lets you reference pins by their functional names.
3. **"Why is my auto-router failing?"** — You stop fighting Freerouting on RF boards. The plugin stops at "wired `.kicad_pcb`" and hands off to EasyEDA's cloud auto-router, which works on real designs.
---
## Quick tour
- **Two slash commands**: `/pcb-new` (from a description) and `/pcb-from-bom` (from a CSV).
- **One agent**: `part-sourcer` — finds the best JLCPCB-stocked part for a generic spec.
- **One skill**: `kicad-jlcpcb-workflow` — the full reference the LLM consults while driving the workflow.
- **13 MCP tools** covering setup, sourcing, schematic, PCB generation, EasyEDA handoff, and session resume.
- **Session persistence** — each project writes a `.kicad_jlcpcb_session.json` so `/pcb-new` can resume mid-flow after a Claude Code restart.
---
## Requirements
| Component | Version | Notes |
|---|---|---|
| Python | 3.10 – 3.13 | Tested on all four |
| KiCad | 8.0+ | `kicad-cli` on `PATH`, `pcbnew` Python bindings for `pcb_generate` |
| EasyEDA account | free | Only needed for the final routing + ordering step |
Install KiCad:
- **Fedora 40+:** `sudo dnf install kicad`
- **Ubuntu 22.04+:** `sudo add-apt-repository ppa:kicad/kicad-9.0-releases && sudo apt install kicad`
- **Arch:** `sudo pacman -Syu kicad`
- **macOS:** [kicad.org/download](https://www.kicad.org/download/)
---
## Install
Distributed via GitHub only — no PyPI, no marketplace. Clone and install locally.
### 1. Clone + install (virtualenv recommended)
```bash
git clone https://github.com/BeckhamLabsLLC/kicad-jlcpcb.git
cd kicad-jlcpcb
python -m venv .venv
source .venv/bin/activate # Windows: .\.venv\Scripts\activate
pip install -e ".[dev]"
```
The editable install puts a `kicad-jlcpcb` entry-point script in the venv's `bin/`. `.mcp.json` calls that script directly; if the venv isn't active when Claude Code launches, point `.mcp.json` at the absolute path:
```json
{
"mcpServers": {
"kicad-jlcpcb": {
"command": "/abs/path/to/kicad-jlcpcb/.venv/bin/kicad-jlcpcb",
"args": []
}
}
}
```
If you'd rather install into your user Python without a venv, substitute `pip install -e .` after `cd kicad-jlcpcb` and skip the venv lines. The entry-point lands in `~/.local/bin` instead.
### 2. Register with Claude Code
```
/plugin marketplace add /abs/path/to/kicad-jlcpcb
/plugin install kicad-jlcpcb@local
```
Restart Claude Code so the MCP server registers.
### 3. Sanity check
```bash
kicad-jlcpcb --help # should print nothing (MCP servers speak JSON-RPC on stdio), exit 0
```
---
## Five-minute quick-start
Pick a small idea — an ESP32-C3 board with one sensor, a USB-C port, and an LDO works well. Run:
```
/pcb-new An ESP32-C3 soil-moisture sensor with two capacitive probes,
USB-C 5V in, a 3.3V LDO, status LED, and JST-PH battery header.
Place it on an 80x60 mm board.
```
Claude walks you through:
1. `detect_kicad` — verify the toolchain (< 1 s)
2. `create_project` — scaffold `.kicad_pro` + `.kicad_sch` + session file
3. Decomposes the description into ~12 generic part specs
4. `lcsc_search` per spec (first run populates the 17 MB jlcparts cache)
5. **BOM checkpoint** — shows every resolved part, flags extended-tier ones with cost warnings, and waits for your confirmation
6. `pcb_generate` — fetches EasyEDA pin maps (~12 s per unique IC, first run only), places footprints, wires nets, saves `.kicad_pcb`
7. `easyeda_handoff` — prints the import instructions
The full trace with real timings and tool outputs: **[`examples/soilnode-esp32/walkthrough.md`](examples/soilnode-esp32/walkthrough.md)**.
First run: ~90 s. Subsequent runs on similar designs: under 10 s.
---
## What the plugin does **not** do
Set expectations honestly before you start:
- ❌ **Auto-route traces.** That's why the `.kicad_pcb` gets handed to EasyEDA. Freerouting 2.1.0's CLI is buggy and can't handle RF matching networks; nothing else works headlessly well enough to ship.
- ❌ **Beautiful placement.** The three-band grid (connectors on top, ICs in the middle, passives below) is functional, not pretty. You rearrange in EasyEDA before routing.
- ❌ **Design review.** There's no DRC integration (yet — see Roadmap). The plugin trusts your spec and relies on KiCad / EasyEDA to catch rule violations.
- ❌ **PyPI distribution.** Install from the clone. No `pip install kicad-jlcpcb`.
---
## MCP tool surface
| Stage | Tool | Purpose |
|---|---|---|
| Setup | `detect_kicad` | Probe `kicad-cli` version, return install hint if missing |
| Setup | `create_project` | Scaffold `.kicad_pro` + subdirs + session file |
| Setup | `load_project` | Validate existing `.kicad_pro`; surfaces resumable session state |
| Resume | `session_resume` | Report where a prior workflow left off for a project dir |
| Sourcing | `lcsc_search` | Free-text part search, basic-only by default |
| Sourcing | `lcsc_resolve_bom` | Batch BOM resolution with cost-impact warnings |
| Sourcing | `fetch_part_library` | Placeholder symbol/footprint fetch into project `libs/` |
| Pin maps | `part_pin_map` | Fetch pin-name → pad-number map from EasyEDA |
| Schematic | `sch_generate` | Emit `.kicad_sch` from a netlist spec |
| Schematic | `sch_run_erc` | Run `kicad-cli sch erc` and parse the report |
| PCB | **`pcb_generate`** | **Main tool.** Auto-fetches pin maps, places footprints, wires every net, saves `.kicad_pcb` |
| Terminal | **`easyeda_handoff`** | **Recommended terminal tool.** Produces EasyEDA import instructions |
| Legacy | `package_for_jlcpcb` | For users routing in KiCad: export Gerbers + package a JLCPCB upload zip |
Full input-schema definitions are in [`src/kicad_jlcpcb_mcp/server.py`](src/kicad_jlcpcb_mcp/server.py) under `_tool_definitions()`.
---
## PCB spec format
`pcb_generate` consumes a JSON-serializable dict:
```json
{
"name": "demo",
"board": {"width_mm": 80, "height_mm": 60, "layer_count": 2},
"components": [
{
"ref": "U1",
"value": "ESP32-C3-WROOM-02",
"lcsc": "C2934560",
"lib": "RF_Module",
"fp": "ESP32-C3-WROOM-02"
}
],
"nets": {
"3V3": [["U1", "3V3"], ["C1", "1"]],
"GND": [["U1", "GND"], ["C1", "2"]],
"SPI_SCK": [["U1", "GPIO10"], ["U2", "SCK"]]
}
}
```
Key rules:
- Reference IC pins by their **functional name** (`3V3`, `GPIO10`, `SCK`). The plugin resolves them via EasyEDA's pinmap.
- For passives (R, C, L, D), use bare pad numbers: `"1"`, `"2"`.
- `lib` and `fp` are KiCad-stdlib library + footprint names. See `/usr/share/kicad/footprints/` for the catalog.
Full worked spec: **[`examples/soilnode-esp32/spec.json`](examples/soilnode-esp32/spec.json)**.
---
## Architecture
```
src/kicad_jlcpcb_mcp/
server.py ← MCP server + 13 tool definitions
session.py ← per-project state (.kicad_jlcpcb_session.json)
project.py ← .kicad_pro create / load / validate
kicad_cli.py ← async wrapper for kicad-cli (KiCad 8/9)
lcsc_client.py ← jlcparts mirror + SQLite cache + basic-tier filter
part_library.py ← EasyEDA client (EasyEdaRateLimiter + pin-map cache)
pcb.py ← pcbnew-based .kicad_pcb generator
schematic.py ← netlist spec → .kicad_sch
gerber_pack.py ← KiCad 8/9 Protel extension normalizer + JLCPCB zip
sexpr.py ← s-expression reader/writer
config.py ← module-level constants
```
All HTTP goes through `lcsc_client` and `part_library`. All KiCad CLI invocations go through `kicad_cli`. `pcbnew` is lazy-imported inside `pcb.py` so the rest of the plugin runs fine when KiCad isn't installed (most tools don't need it).
---
## Testing
```bash
PYTHONPATH=src pytest tests/ # 212 tests (6 skipped without KiCad)
```
With KiCad's `pcbnew` bindings available:
```bash
KICAD_INSTALLED=1 PYTHONPATH=src pytest tests/ -v # runs integration suite too
```
Coverage spans subprocess wrapping, HTTP mocking, SQLite cache, s-expression round-trip, schematic emission, Gerber renaming, EasyEDA pin-map parsing, rate-limit / retry, session persistence, MCP tool routing, and real `pcbnew` board generation.
Lint:
```bash
ruff check .
ruff format --check .
```
---
## Troubleshooting
Full guide: **[`TROUBLESHOOTING.md`](TROUBLESHOOTING.md)**. Most common issues:
| Symptom | Fix |
|---|---|
| `kicad-jlcpcb` command not found | `pip install -e .` from the clone, restart Claude Code |
| `ImportError: No module named pcbnew` | Install KiCad; don't try to `pip install pcbnew` (it ships with KiCad) |
| First run stalls ~12 s per IC | Expected — EasyEDA rate limit. Cached forever after first fetch. |
| `Footprint not found` | Check `/usr/share/kicad/footprints/<lib>.pretty/` for the exact name |
| `/pcb-new` offers to resume when you wanted a clean start | Delete `.kicad_jlcpcb_session.json` or pick a new project name |
---
## Design rationale (Phase 1.6)
Earlier releases tried to route the board headlessly with Freerouting and produce a JLCPCB Gerber zip directly. That didn't work for real boards — Freerouting 2.1.0 has CLI bugs, can't route RF matching networks, and won't save partial results.
Phase 1.6 takes the pragmatic win: **the plugin wires everything up, EasyEDA routes and orders.** The tradeoff is opening a browser tab and clicking two buttons; in exchange you get reliability the open-source tooling can't match and a one-click path to a JLCPCB order.
---
## Roadmap
- **Phase 2** — auto-placement that respects functional groupings (power domain, RF block, analog front-end), DRC integration, differential-pair awareness.
- **Phase 3** — vision-based schematic extraction: drop in a photo of a hand-drawn schematic, out comes a wired `.kicad_pcb`.
---
## Contributing
See **[`CONTRIBUTING.md`](CONTRIBUTING.md)** for dev setup, test running, and PR conventions. All contributors follow the [Code of Conduct](CODE_OF_CONDUCT.md). Bug reports: [open an issue](https://github.com/BeckhamLabsLLC/kicad-jlcpcb/issues).
---
## License
MIT — see **[`LICENSE`](LICENSE)**.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessUnresponsive