Copper
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| COPPER_NO_UPDATE_CHECK | No | Set to 1 to turn off the check for a newer KiCad (gitlab.com) that Copper performs during `setup` and `doctor`. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Copper for KiCad: schematic, PCB, parts, checks and the fabrication pack, on KiCad's own files.
New board, short path:
project_create(name, template, board_size=[w,h])
sch_apply(ops=[...]) with the WHOLE design in one batch. Place parts by spec ("10k 0402"), part number or lib_id; connect pins to nets by NAME: {"op":"connect","pins":["U1.PA9","J1.D+"],"net":"USB_DP"}. Never compute coordinates.
board_build(width, height): sync, outline, placement, routing, pour and DRC in one call, one report. If it returns "pending", call board_build() again.
fab_export() writes the fabrication pack. Existing project: project_open(path), then project_summary.
Results already carry KiCad's ERC/DRC counts, a fix per finding and next: do not call verify or render
after each step. Pictures are attached only when asked (render, or preview/render=true). Single-step tools
(pcb_place, pcb_route, pcb_pour ...) are for fixing what board_build reports.
Examples for a tool: copper_help(tool=...). Trouble: copper_status.
Assembled parts need the part number the fab profile asks for (default: LCSC); parts placed by spec get it.
If a file is open in KiCad, save and close it first.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| copper_statusA | Start here. Shows Copper + KiCad versions and installs, what works on this machine (kicad-cli, KiCad's pcbnew Python for
board edits, live IPC socket, Freerouting/Java for autorouting), the active project (+ whether it's open in KiCad, last ERC/DRC counts),
recent projects, tool modules that failed to load, |
| project_createA | Create a new KiCad project (.kicad_pro, .kicad_sch, .kicad_pcb) and make it active. Refuses to overwrite an existing project. template: '2layer' (default) | '4layer' | 'blank' (KiCad defaults, no fab rules). fab: the fab profile whose design rules the project gets (default: the configured one). board_size: [width_mm, height_mm] draws a rectangular outline. dir: the parent folder (the project goes in dir/), or the project folder itself when it ends in ; default Documents/Copper/. KiCad re-loads the files (ERC + DRC) before success is reported. Example: project_create(name='usb-sensor', board_size=[50,30]) |
| project_openA | Make an existing KiCad project the active one (all other tools then default to it). path: project folder,
.kicad_pro, .kicad_sch or .kicad_pcb. Returns a short overview + next steps (project_summary has the full digest).
A |
| project_summaryA | Compact digest of the active (or given) project: sheets, components by kind, ICs, nets, power rails, % of parts with a
part number (and which are missing one), missing footprints, board size/layers/tracks/vias/zones, the active parts providers + fab
profile ( |
| checkpointA | Snapshot / list / diff / restore the project's design files (.kicad_sch, .kicad_pcb, .kicad_pro, rules, lib tables) in .copper/snapshots. action: 'create' (label optional) · 'list' · 'diff' (id optional, default newest: what changed since) · 'restore' (id required; snapshots the current state first, refuses if the file is open in KiCad). Mutating tools snapshot by themselves before they write. Example: checkpoint(action='diff') |
| copper_helpA | Worked examples, op catalogues and argument details for one Copper tool: the long-form help that is kept out of
the tool list. Call it before your first non-trivial use of a tool, or when a call came back BAD_ARGUMENT / BAD_OP.
Omit |
| sch_applyA | Edit the schematic with ONE atomic batch of ops (all succeed or nothing is written). Put a whole sub-circuit, or the
whole design, in a single call. Creates the .kicad_sch if missing. One call edits one sheet.
Pins are 'REF.PIN', PIN = number or name ('U1.PA9', 'J1.VBUS', 'R1.2'); a name on several pins connects them all.
Never compute coordinates: connect by NET NAME and Copper anchors the label or power symbol on the pin. New parts
take a temporary 'id' (one word) used in pins ('led.A'); the result's
|
| sch_readA | Read the schematic compactly: before editing an existing design, and to find exact pin names.
query: summary (default, counts) | components (ref, value, lib_id, footprint, LCSC, position) | pins (every pin of |
| sch_tidyA | Re-draw a schematic sheet so it reads like a hand-drawn professional sheet WITHOUT changing the circuit: titled
functional blocks in signal-flow columns, canonical drawings per idiom (LDO, op-amp loop, crystal, LED chain), support
parts wired at the pin they serve, supplies up and grounds down, design notes, smallest paper that fits. Nothing is
written unless KiCad's netlist of the WHOLE project is identical before and after and ERC reports nothing new. A sheet
wired by hand is left alone ( |
| libraryA | Search and inspect the KiCad symbol and footprint libraries this machine has (global + project tables): find a valid lib_id / footprint before sch_apply place, and read exact pin names. action: search_symbols (query = part number, name or keywords) | search_footprints (query; filters = footprint globs) | symbol_info (lib_id -> pins with number, name, type, unit; default footprint) | footprint_info (fp_id -> pads, courtyard, 3D model) | list (libraries in the active tables) | repair (fix "library not found": rebuilds missing or stale global tables; dry_run defaults to TRUE). Examples: library(action='search_symbols', query='USB C receptacle') · library(action='symbol_info', lib_id='Device:R') |
| parts_searchA | Find parts. The project's parts providers are asked in order and the first with results answers ( |
| parts_getA | Full detail for one part by its provider part number. The provider is found from the id (a C-number is LCSC;
'name:id' names one) or |
| parts_importA | Make a provider's part usable in this project: returns the |
| parts_source_bomA | BOM sourcing pass: give every schematic component (all sheets) a part from the project's parts providers. Run it once
the schematic has values and footprints, before the fab pack. Per component it decides from an existing MPN field,
then value + footprint package, then an MPN in the footprint name; existing part numbers are kept (overwrite=true
re-picks) and checked against stock and package. Writes the provider's part-number field plus MPN / Manufacturer
(write=false reports only). Returns summary (sourced %, parts by class, setup-fee and cost estimates), |
| parts_alternativesA | In-stock substitutes for a part — use when a part is out of stock, in a dear assembly class (JLC Extended:
~$3 setup fee), or too expensive. target: a part number (→ same package, same value / same MPN family; for lcsc Basic > Preferred >
Extended, then stock) or a free-text spec ('100nF 0402'). For ICs the alternatives are the same MPN family from other
makers/packagings — check the datasheet pinout before swapping. Apply with sch_apply set_field (the result's |
| verifyA | Run KiCad's own checks and get actionable findings. Use after EVERY batch of edits (sch_apply, pcb_*), and before fab_export. what: list of 'erc' (schematic electrical rules), 'drc' (board rules + unrouted nets + schematic↔board parity; zones are refilled in memory,
the file is not modified) and 'review' (engineering heuristics, see design_review). Default: ['erc'] plus 'drc' when a board exists.
Each finding has severity, type, message, refs (pins like 'U1.3' / footprints), at [x,y] mm (on |
| design_reviewA | Engineering review of the schematic netlist (and the board, if any): catches what passes ERC but kills boards.
Decoupling, I2C pull-ups, crystal load caps, USB-C CC resistors, USB ESD, regulator caps and feedback-divider voltage, floating EN/RESET/BOOT pins,
LED resistors, undriven rails, part sourcing (+ the fab's assembly-class fee), missing footprints, label typos, thin power-path tracks, thermal pads without vias.
Read-only. Each finding has a |
| renderA | Look at the design: returns PNG image(s) the model can see. Use after edits to check layout, and with target: 'schematic' (every sheet, or |
| pcb_readA | Read the PCB. With no arguments: a digest (footprint, track, via and unrouted counts, outline size, layers). what: any of summary | footprints (ref, value, position mm, rotation, side) | nets | pads | tracks (every segment with its uuid; combine with net) | widths (per-net copper summary) | zones | drawings. refs: only these footprints. net: only that net. drc=true also runs KiCad DRC. source='live' reads the board open in the running KiCad editor. Example: pcb_read(what=["footprints"], refs=["U1"]) · pcb_read(what=["nets"], net="GND") |
| pcb_syncA | Update the PCB from the schematic (KiCad's "Update PCB from Schematic"); creates the .kicad_pcb if missing. Adds missing footprints beside the outline, updates fields, swaps changed footprints in place, assigns every pad's net and keeps existing positions. Copper follows renamed nets; copper left from deleted parts is reported. remove_extra=true deletes footprints (and their orphaned copper) that are no longer in the schematic. Refuses if the schematic has unannotated or duplicate references. board_build() runs this and the following steps. Example: pcb_sync() |
| pcb_setupA | Board setup: outline, corner mounting holes, copper layer count, fab design rules + net classes, silkscreen text size. Only what you pass is changed. Outline, one of: width x height mm rectangle (optional radius; REPLACES the existing outline), polygon, auto_size=true (smallest comfortable rectangle for the parts, unrouted boards only), or outline_file='case.step' (STEP, DXF, SVG, STL; 'auto' = the one file in mechanical/): the board body or an enclosure's cavity with its arcs, cutouts and mounting holes; an uncertain pick lists candidates (outline_pick), outline_dry_run=true only analyses. First setup applies the design rules of the project's fab profile (rules='fab'). power_nets adds nets / patterns to the Power net class. Examples: pcb_setup(width=40, height=30) · pcb_setup(width=50, height=50, radius=2, holes='M3', layers=4) · pcb_setup(outline_file='mechanical/case.step', outline_dry_run=true) · pcb_setup(power_nets=['VBAT','SW'], power_track=0.8) |
| pcb_placeA | Place footprints. auto=true: connectivity-driven placement inside the outline (connectors on the edges, decoupling caps at their IC pin, crystal at the MCU, RF antennas on an edge), courtyard-legal on a 0.5 mm grid; several candidates are scored for elegance and the best is written. Locked parts and keep=[..] stay put; refs=[..] places only those around what is there. On a routed board, placing everything is refused unless clear_tracks=true. overhang={"J1": 1.2} lets a side-entry connector stick out past the edge. moves=[{ref, x, y, rot, side, lock}]: exact positions in mm (board coordinates, as pcb_read), applied after auto placement. Returns the ratsnest length before/after and the elegance score. Examples: pcb_place(auto=true) · pcb_place(moves=[{"ref":"J1","x":101,"y":115,"rot":90}]) |
| pcb_routeA | Route the board. mode='auto' (default without tracks): autoroute every unrouted connection with Freerouting (Java and the router are downloaded once into $COPPER_HOME/tools on first use). Existing tracks and vias are kept; a result that is worse than before is reverted; the new tracks are then polished (polish=false skips that). Can take minutes on dense boards. 2 layers: pcb_route(ignore_nets=["GND"]) then pcb_pour(net="GND", stitch=true). 4 layers: pour the inner planes first. tracks=[{"from":"U1.3","to":"C3.1","width":0.3,"layer":"F.Cu","waypoints":[[x,y]]}]: explicit connections; refuses to join different nets or to cross other nets' copper. pcb_route_guided is the built-in deterministic router (no download); board_build() picks for you. Examples: pcb_route() · pcb_route(tracks=[{"from":"J1.1","to":"U1.3","width":0.5}]) |
| pcb_pourA | Copper pour: a zone for one net (default GND) over the whole outline on the chosen layers (default F.Cu + B.Cu), then fill every zone and re-run DRC. Pads the fill cannot reach get a solid connection or a via; what still cannot join is reported with its position. stitch=true adds stitching vias. A pour on an inner layer is a plane. Re-running replaces the net's zone; remove=true deletes it; fill_only=true just refills after moving or routing. Examples: pcb_pour() · pcb_pour(net="GND", stitch=true) · pcb_pour(net="+3V3", layers=["In2.Cu"]) · pcb_pour(fill_only=true) |
| pcb_editA | Low-level PCB escape hatch — a batch of ops applied in one transaction (all or nothing), then DRC: {"op":"add_track","points":[[x,y],[x,y],...],"width":0.25,"layer":"F.Cu","net":"GND"} {"op":"add_via","at":[x,y],"net":"GND","size":0.6,"drill":0.3} (or "points":[[x,y],...]) {"op":"delete","refs":["R7"]} | {"uuids":[...]} | {"nets":["SDA"]} (tracks+vias of those nets) | {"all_tracks":true} | {"zone_nets":["GND"]} {"op":"add_text","text":"v1.0","at":[x,y],"layer":"F.SilkS","size":1.0,"rot":0} {"op":"set_field","ref":"U1","field":"LCSC","value":"C12345","visible":false} {"op":"move","ref":"R1","x":110,"y":105,"rot":90,"side":"top"} (locked part: add "unlock":true) {"op":"lock"|"unlock","refs":["J1"]} (or "all":true) Coordinates are mm (board coordinates as in pcb_read; track/via uuids from pcb_read(what=["tracks"])). Deleting a footprint that is in the schematic only lasts until the next pcb_sync — delete the symbol instead. Prefer pcb_place / pcb_route / pcb_pour when they fit. |
| pcb_route_planA | Analyse the board for routing (read-only) and return what you need to write a plan for pcb_route_guided: board and
rules, a net table (kind, pads, unrouted, class), pours, keep-outs, a congestion map with hot spots, and
|
| pcb_route_guidedA | Built-in deterministic router (no download): same plan + same board gives the same copper; KiCad's DRC judges the
result and copper it rejects is removed again. No plan = built-in heuristics.
plan (all optional, mm, net globs ok): order [nets], global {grid, via_cost, style, layers, time_budget_s,
engine: "v2" (default) | "guided"},
nets {"": {priority, width, layers, avoid:[[x0,y0,x1,y1]], corridor:[[x,y],..], max_vias, skip}},
diff_pairs [{p, n, gap, width}], regions [{rect, cost | forbid:true, layers, nets}]. Unknown fields are rejected.
ripup: none (default, keep existing tracks) | nets (re-route |
| focusA | What the user is pointing at. Call it when they say "this part", "here", "that corner" instead of guessing. action: get (default; the latest click / box in the Copper Canvas or selection in KiCad's PCB editor; wait=N waits up to N s for their next one) | resolve (no human: what is inside box=[x0,y0,x1,y1] or nearest near=[x,y] mm on document='schematic' | 'pcb') | open (start the Copper Canvas viewer and return its URL) | clear. Returns focus {document, mode, box_mm | point_mm, refs, pins, nets, age_s}, items with value / footprint / position, and a cropped PNG with the focus outlined. Empty refs with a box or point means the user means that AREA. Use refs / pins in sch_apply, pcb_place, pcb_route; box_mm in render(crop=...). Examples: focus() · focus(wait=60) · focus(action='resolve', document='pcb', box=[100,100,120,115]) |
| fab_exportA | Produce the order pack for a PCB fab: Gerbers + Excellon drill zipped, a BOM CSV and a placement (centroid/CPL) CSV, all in the format of a fab PROFILE. Writes to /copper-out/fab/. profile: omit to use the project's configured profile (fab.profile in the Copper config; 'jlcpcb' out of the box). Built in: 'jlcpcb' (Protel gerbers; BOM 'Comment, Designator, Footprint, LCSC Part #'; CPL 'Designator, Mid X, Mid Y, Layer, Rotation' with JLC rotation corrections), 'pcbway', 'oshpark' (bare boards) and 'generic' (Gerber X2 + Excellon + KiCad position file + plain BOM with MPN/Manufacturer — for any other fab). Users add their own as JSON in $COPPER_HOME/fabs/ or /.copper/fabs/. profile='list' returns every available profile with its rules and notes (nothing is exported). It first runs ERC + DRC (zones refilled, schematic parity) and REFUSES (error FAB_BLOCKED, listing blockers with fixes) for whatever
the profile says blocks an order: ERC errors, DRC errors, unrouted nets, parity issues, no board outline, an empty board and — for
profiles that need one per part, like jlcpcb — assembled parts without the profile's part number.
Fix the blockers (each comes with the next tool call); pass waive=[codes] ONLY for things the user consciously accepts:
'erc','drc','unrouted','parity','parts','outline','empty' or 'all'. assembly=false skips BOM + placement (bare PCB order).
With assembly it also asks the parts provider for live stock of every BOM part (for |
| exportA | Export any other manufacturing/documentation output with kicad-cli; the files are verified to exist and be non-empty. kind: pdf_schematic | svg (schematic, one per sheet) | pcb_pdf | step | glb | vrml | ipc2581 (BOM columns from the part-number/MPN fields) | odb | netlist (KiCad s-expr) | bom (CSV grouped by Value/Footprint/part number) | pos (CSV, mm, both sides, DNP excluded) | drill | gerbers | render3d (PNG) | stats (board statistics JSON) | ipcd356. out: file (or folder for svg/drill/gerbers); default /copper-out/export/. For a fab's order pack use fab_export instead. Examples: export(kind='step') · export(kind='pdf_schematic') · export(kind='pcb_pdf', layers=['F.Cu','Edge.Cuts']) |
| board_buildA | The whole board in ONE call: update the PCB from the schematic, outline, placement, routing, ground pour and
KiCad's DRC + ERC, with one compact report (a line per stage, error groups with fixes, |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| design_board | Full loop: requirements → sourced schematic → verified PCB → the fab's order pack |
| source_bom | Give every part a part number from the project's parts providers and report cost/stock risks |
| review_design | ERC, DRC and engineering review, then fix what is safe to fix and report the rest |
| release_to_fab | Final checks and the Gerber/BOM/placement pack for the project's fab profile |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| status | KiCad installs, capabilities, active project, parts providers + fab profile and next step (JSON) |
| guide | Workflow, conventions, parts providers and fab profiles for driving KiCad through Copper |
| help | Every tool with a one-line summary; read copper://help/<tool> for its long examples (JSON) |
| session-credits | What this session's Copper calls cost: calls, result bytes, images and estimated tokens per tool (JSON) |
| project-summary | Sheets, components, nets, rails, sourced %, board, config, last ERC/DRC (JSON) |
| project-bom | Grouped bill of materials with part numbers, MPN and manufacturer (JSON) |
| project-netlist | Compact netlist: one line per net, REF.PIN(name) members (text) |
| project-erc | Latest ERC counts and findings with fix hints (JSON) |
TDQS
Scored across 31 tools
Most tools target clearly distinct phases (project, schematic, PCB, parts, export), and descriptions explicitly steer between abstraction levels such as board_build vs individual pcb_* steps or pcb_edit vs pcb_place. A few boundaries still blur: design_review overlaps verify(what='review'), and multiple routing entry points require judgment.
Names are consistently snake_case with domain prefixes (sch_, pcb_, parts_, project_, copper_, fab_) and mostly action suffixes. Minor deviations are single-word tools (checkpoint, library, render, verify, focus, export) and the noun_noun parts_alternatives.
31 tools is heavy for a single MCP server and exceeds the typical 15-tool comfort zone, increasing context and selection overhead. The domain is broad and some tools are justified, but board_build already bundles several pcb_* workflows, so consolidation is possible.
The surface covers the full KiCad lifecycle: project setup, schematic read/edit, library and part sourcing, PCB sync/place/route/pour/edit, ERC/DRC/review, checkpoints, rendering, and fab/export. No obvious operational gaps appear for PCB design automation.