Skip to content
Conduit

mcp server

kicad-jlcpcb

Claude Code plugin that turns a PCB idea into a wired .kicad_pcb ready for EasyEDA auto-routing and one-click JLCPCB ordering — LCSC part sourcing, EasyEDA pin-map auto-fetch, and session-persisted wo

BeckhamLabsLLC/kicad-jlcpcbcommunitylistedunreviewednot running here yet

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "kicad-jlcpcb": {
      "command": "uvx",
      "args": [
        "kicad-jlcpcb"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 14, 2026

kicad-jlcpcb

License: MIT Python 3.10+ KiCad 8+ tests upstream contract MCP

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.

┌─ 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 queries the JLCPCB catalog directly, caches the answer, 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.
  • 14 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

ComponentVersionNotes
Python3.10 – 3.13Tested on all four in CI
KiCad8.0 – 10.xkicad-cli on PATH or in a standard install location; pcbnew Python bindings for pcb_generate. Only the 8.0 floor is enforced — KiCad 11 removed the SWIG bindings and will pass detection, then fail at pcb_generate. See TROUBLESHOOTING
EasyEDA accountfreeOnly needed for the final routing + ordering step
NetworkrequiredPart data is fetched live — see below

Data dependencies

Part sourcing depends on two third-party, unofficial services. Neither is run by JLCPCB, and neither is run by us. Both are overridable, so a fork can point at a mirror without touching code:

ServiceUsed forOverride
jlcsearch.tscircuit.comCatalog search, JLCPCB stock, basic/extended tierKJLC_JLCSEARCH_BASE
easyeda.comExact C-number lookup, symbols, pin mapsKJLC_EASYEDA_BASE

Resolved parts are cached in ~/.cache/kicad-jlcpcb/lcsc_parts.sqlite for 24 hours. There is no bulk catalog download.

Why the overrides exist. v0.1.0 hardcoded a single third-party URL. Upstream retired that data layout, the URL started returning 404, and part sourcing broke silently for months before anyone noticed (#1). A weekly CI job now tests both services directly, and you can repoint either one yourself.

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

Install

/plugin marketplace add BeckhamLabsLLC/claude-plugins
/plugin install kicad-jlcpcb@beckhamlabs-plugins

Restart Claude Code, then run /mcp and look for kicad-jlcpcb.

That is the whole install. You do not need to clone the repo, and you do not need to install anything with pip — the plugin resolves its own two dependencies (mcp, httpx) on first launch via uv, which most Python toolchains already have:

curl -LsSf https://astral.sh/uv/install.sh | sh    # if you do not have it

If you would rather not use uv, install the dependencies into whichever Python python3 resolves to and the plugin will use them directly:

python3 -m pip install mcp httpx

KiCad itself is a separate install — see Requirements.

git clone https://github.com/BeckhamLabsLLC/kicad-jlcpcb.git
cd kicad-jlcpcb
pip install -e ".[dev]"

Then register the clone as a local marketplace, which is what makes Claude Code expand ${CLAUDE_PLUGIN_ROOT} for the MCP server:

/plugin marketplace add /abs/path/to/kicad-jlcpcb
/plugin install kicad-jlcpcb@beckhamlabs

Note the marketplace name differs from the published one: the repo's own .claude-plugin/marketplace.json declares beckhamlabs, while the aggregator repo declares beckhamlabs-plugins.

Do not register the checkout by enabling its .mcp.json as a project server. ${CLAUDE_PLUGIN_ROOT} is only substituted for plugin-provided MCP configs; in project scope it stays a literal string and the server cannot be found. See TROUBLESHOOTING.

Sanity check

/mcp in Claude Code should list kicad-jlcpcb with its 14 tools. If it reports the server failed, see TROUBLESHOOTING — the first step there prints the actual reason, which Claude Code does not show you.

From a clone, you can also check the whole path end to end — that the server starts, registers its handlers, and answers a real tool call:

python3 bin/launch.py --version     # prints the version, exits 0
python3 scripts/check_protocol.py   # initialize -> tools/list -> tools/call

Run the launcher with no arguments and it will appear to hang — that is correct. An MCP server speaks JSON-RPC on stdio and is waiting for a client.


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 (live catalog query, cached locally for 24 h)
  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 each, only for parts whose nets reference pins by name, 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.

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 through the Claude Code marketplace. There is no pip install kicad-jlcpcb.

MCP tool surface

StageToolPurpose
Setupdetect_kicadProbe kicad-cli version, return install hint if missing
Setupcreate_projectScaffold .kicad_pro + subdirs + session file
Setupload_projectValidate existing .kicad_pro; surfaces resumable session state
Resumesession_resumeReport where a prior workflow left off for a project dir
Resumesession_confirm_bomRecord the user's BOM approval so a restart doesn't re-ask
Sourcinglcsc_searchFree-text part search, basic-only by default
Sourcinglcsc_resolve_bomBatch BOM resolution with cost-impact warnings
Sourcingfetch_part_librarySymbol + footprint from EasyEDA's real geometry into project libs/
Pin mapspart_pin_mapFetch pin-name → pad-number map from EasyEDA
Schematicsch_generateEmit .kicad_sch from a netlist spec
Schematicsch_run_ercRun kicad-cli sch erc and parse the report
PCBpcb_generateMain tool. Auto-fetches pin maps, places footprints, wires every net, saves .kicad_pcb
Terminaleasyeda_handoffRecommended terminal tool. Produces EasyEDA import instructions
Legacypackage_for_jlcpcbFor users routing in KiCad: export Gerbers + package a JLCPCB upload zip

Full input-schema definitions are in src/kicad_jlcpcb_mcp/server.py under _tool_definitions().


PCB spec format

pcb_generate consumes a JSON-serializable dict:

{
  "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. The plugin finds KiCad's footprint directory automatically on Linux, macOS, Windows and Flatpak; override with KJLC_FOOTPRINT_DIR or pcb_generate's lib_dir.

Full worked spec: examples/soilnode-esp32/spec.json.


Architecture

src/kicad_jlcpcb_mcp/
  server.py         ← MCP server + 14 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–10)
  lcsc_client.py    ← jlcsearch + EasyEDA lookup, 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    ← 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

pytest tests/       # offline suite; nothing here touches the network

Two suites are gated behind environment variables because they need something the default run can't assume:

KICAD_INSTALLED=1 pytest tests/ -v                          # needs pcbnew
KJLC_NETWORK_TESTS=1 pytest tests/test_network_contract.py  # hits live APIs

The offline suite covers subprocess wrapping, HTTP mocking, the 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.

test_network_contract.py earns its own paragraph. Every other test is mocked, which is why the whole suite stayed green for four months while part sourcing was completely broken in the field. The contract tests assert the shape of live upstream responses — never a specific price or stock figure — and run weekly in CI. If that badge goes red, part sourcing is broken for everyone; please open an issue.

Lint:

ruff check .
ruff format --check .

Troubleshooting

Full guide: TROUBLESHOOTING.md. Most common issues:

SymptomFix
MCP server shows as failed / CONNECTION_CLOSEDRun python3 bin/launch.py --version by hand — it prints what is missing. Usually: install uv, or python3 -m pip install mcp httpx.
ImportError: No module named pcbnewInstall KiCad; don't try to pip install pcbnew (it ships with KiCad)
First run stalls ~12 s per partExpected — EasyEDA rate limit, and only for parts whose nets use pin names. Cached forever after the first fetch.
Footprint not foundThe error names how many libraries were found and suggests near matches. If none were found, set KJLC_FOOTPRINT_DIR.
/pcb-new offers to resume when you wanted a clean startDelete .kicad_jlcpcb_session.json or pick a new project name

What you get, and what to check

The plugin produces a .kicad_pcb with real footprints and every net wired, plus a JLCPCB upload zip. Two things are worth checking before you order:

Part geometry comes from EasyEDA. Symbols carry the real pin names and numbers, and footprints the real pad positions, sizes and drills — EasyEDA, LCSC and JLCPCB share a parent company, so this is the same data JLCPCB assembles against. When EasyEDA has no geometry for a part, a placeholder is written instead and fetch_part_library says so in warnings. A placeholder footprint will not match the real part — replace it from KiCad's standard libraries before ordering.

CPL rotations are passed through, not corrected. JLCPCB's expected orientation differs from KiCad's for some packages. Footprints generated from EasyEDA already share JLCPCB's convention; footprints you take from KiCad's standard libraries may not. The plugin says so rather than applying a guess that silently rotates parts — check JLCPCB's assembly preview after upload and fix anything that looks wrong there.


Design rationale: why the handoff

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.

So the plugin takes the pragmatic win: it 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

  • Next — auto-placement that respects functional groupings (power domain, RF block, analog front-end), DRC integration, differential-pair awareness, and a per-package CPL rotation table so JLCPCB orientations need no manual correction.
  • Later — vision-based schematic extraction: drop in a photo of a hand-drawn schematic, out comes a wired .kicad_pcb.

Contributing

See CONTRIBUTING.md for dev setup, test running, and PR conventions. All contributors follow the Code of Conduct. Bug reports: open an issue.


License

MIT — see LICENSE.

↑↓ navigate openesc close