Skip to main content
Glama
Copper

Copper

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
COPPER_NO_UPDATE_CHECKNoSet 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:

  1. project_create(name, template, board_size=[w,h])

  2. 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.

  3. board_build(width, height): sync, outline, placement, routing, pour and DRC in one call, one report. If it returns "pending", call board_build() again.

  4. 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

CapabilityDetails
tools
{
  "listChanged": false
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
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, session (what this session's Copper calls cost so far) and next: the single most useful next call. detail='credits' returns the per-tool table instead: calls, result bytes, images, estimated tokens.

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 argument on any other tool affects only that call; project_open is what changes the active project. Example: project_open(path='C:/Users/me/Documents/KiCad/robot/robot.kicad_pro')

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 (config), last ERC/DRC counts (and whether they are stale), and next steps. Read-only and cheap; call it when you (re)start work on a design.

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 tool to list every tool with a one-line summary. Example: copper_help(tool='sch_apply')

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 refs maps id to reference. Parts without 'at' are laid out automatically, and a sheet only Copper has arranged is re-drawn as titled functional blocks (layout='keep' never does that; nothing is redrawn unless the netlist is proven identical). ops (objects with "op"):

  • place: lib_id | lcsc "C25804" | part (+provider) | spec "10k 0402" (resolved by the parts providers); id, ref, value, footprint, fields {LCSC:..}, near (ref/id), group mcu|power|connector|passive, at [x,y], rot, mirror, unit, dnp

  • connect: pin | pins [..], net (power names like GND, +3V3, VBUS become power symbols; kind:'global' between sheets)

  • power: pin(s), net · no_connect: pin | pins · pwr_flag: net · wire: from, to | points · label: net + pin | at

  • set_field: ref, field, value | fields · move: ref, at | by, rot, mirror · swap: ref + lib_id | lcsc | part | footprint

  • delete: ref | label | wire_at [x,y] | dangling:true · disconnect: pin(s) · rename_net: from, to · text: text, at The result has what changed, the netlist check of every requested connection and KiCad's ERC with a ready fix per issue, so a separate verify call is not needed. Example: ops=[{"op":"place","lib_id":"Regulator_Linear:AMS1117-3.3","id":"ldo"},{"op":"place","spec":"10uF 0805","id":"cin"}, {"op":"connect","pins":["ldo.VI","cin.1"],"net":"VBUS"},{"op":"connect","pins":["ldo.GND","cin.2"],"net":"GND"}]

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 ref with number, name, type and its net) | nets (net -> ['REF.PIN', ...], KiCad's full net names) | labels | unconnected (pins ERC will flag, each with a ready fix). filter = case-insensitive substring. Example: sch_read(query='pins', ref='U1') · sch_read(query='nets', filter='USB')

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 (style_used: 'kept') unless force=true; sheets with sheet symbols, buses or several instances are refused. style 'pro2' (default); when the checks refuse it a simpler style is drawn: style_used says which, fallback says why. Example: sch_tidy() · sch_tidy(sheet='power.kicad_sch', dry_run=true) · sch_tidy(blocks=[{"id":"U2","title":"MCU"}])

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 (provider in the result says which; 'local' = the user's own catalogue file, 'kicad' = generic library parts). Results are ranked by spec match, then by the provider's part class (parts without a setup fee first), stock and price. query: a value spec ('10k 0603', '100nF 16V 0402 X7R', 'LED red 0603', '12MHz crystal', 'LDO 3.3V SOT-223'), an MPN ('AMS1117-3.3') or a category phrase ('USB-C 16P', 'Header 1x4 2.54mm'). Each result: provider, id (the provider's part number), mpn, package, stock, price, match ('partial' = not every term confirmed) and next = the exact sch_apply op. Passives by spec and known part numbers need no search: sch_apply place resolves and imports them itself. Example: parts_search(query='USB-C 16P', limit=5)

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 provider. lcsc: JLC assembly stock + Basic/Preferred/Extended class + price ladder + attrition (authoritative for JLC PCBA), LCSC retail stock (a different pool — labelled), parameters, datasheet URL, whether EasyEDA has a symbol/footprint/3D model, and whether parts_import would use a KiCad stock symbol+footprint or import from EasyEDA. Other providers: the catalogue record (mpn, manufacturer, value, package, stock, symbol/footprint, extra supplier fields). Use before committing to an unfamiliar part, or to check stock/class of a part already on the schematic. Example: parts_get(part='C2040')

parts_importA

Make a provider's part usable in this project: returns the lib_id + footprint for sch_apply place. sch_apply {"op":"place","part":...} calls this by itself; use it directly to inspect a part first or to force a source. prefer='auto' (default) uses KiCad's stock symbol + footprint when it verifiably fits, else converts the provider's symbol, footprint and 3D model into project libraries (registered in the project's lib tables); 'easyeda' forces the provider's drawing, 'stock' fails if there is no stock fit. Footprint pads are checked against symbol pins and KiCad itself loads the result. preview=true returns pictures of the symbol and footprint. Example: parts_import(part='C165948')

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), lines (decided), manual (could not decide: reason + options) and stock_risks. Example: parts_source_bom(write=false) to preview, then parts_source_bom()

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 next gives the op). Example: parts_alternatives(target='C15401')

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 sheet for sub-sheets) and fix = the exact next tool call, already pointed at the right sub-sheet. Repeats of one problem are merged into one entry with count + all refs. next is the single most useful call to make now; new / fixed compare with the previous run. Checks run on the SAVED files. Example: verify(what=['erc','drc'])

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 fix. checks: subset of the enum (default all but schematic_style). Example: design_review(checks=['decoupling','usb_c_cc'])

renderA

Look at the design: returns PNG image(s) the model can see. Use after edits to check layout, and with crop to zoom on a finding's at.

target: 'schematic' (every sheet, or sheet='name' for one), 'pcb' (2D layers), 'pcb3d' (raytraced 3D). layers (pcb): default all copper + silkscreen + front mask + outline (no F.Fab: its value texts sit on top of the reference designators); pass e.g. ['F.Cu','Edge.Cuts'] to choose. view: pcb → top|bottom; pcb3d → top|bottom|iso|iso_bottom|front. crop: [x0,y0,x1,y1] in mm document coordinates (schematic / pcb top view). width: pixels, ≤ 2400 (default 1400). Example: render(target='schematic', crop=[90,40,180,110])

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 suggested_plan (the built-in heuristics as an editable plan) with plan_schema. Skip it to route with defaults. heatmap=true attaches the congestion picture. ripup='all' analyses as if all unlocked tracks were removed. Example: pcb_route_plan() -> edit suggested_plan -> pcb_route_guided(plan=...)

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 nets) | all. dry_run / compare never write. Returns routed/total, failed [{net, from, to, reason, where, blocked_by}], per-net length and vias, DRC counts, findings with fixes. A net with a pour only gets the pads the pour misses connected; ground without one is left for pcb_pour. Routing nothing leaves the board untouched. Examples: pcb_route_guided() · pcb_route_guided(ripup="all", plan={"order":["/USB_D?","*"]})

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 boards boards, default 5): parts with zero / too little stock or not listed come back in warnings (code 'stock': affected refs + a ready parts_alternatives fix per part). That never blocks; waive=['stock'] acknowledges it. Offline (or with check_stock=false), stock.not_checked and the first order note say stock was NOT checked. Every output is checked (non-empty, complete layer set, zip readable). Returns board size, layers, min track/drill, part counts, summary.assembly_fees (Basic / Preferred / Extended counts, the fee total, extended_refs = which parts cause it) when the profile has part classes, and order notes. Examples: fab_export() · fab_export(profile='generic') · fab_export(waive=['parts']) · fab_export(assembly=false)

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, fab_ready, next). Use it instead of pcb_sync, pcb_setup, pcb_place, pcb_route, pcb_pour and verify one after another. width/height (mm) apply when the board has no outline (omit both to size it from the parts) or with outline='replace'. An existing outline, existing tracks and placed parts are kept: on a board that already has copper only new footprints are placed and only missing connections are routed. If the result has pending, the time box was reached: call board_build() again to continue. Example: board_build(width=50, height=32)

Prompts

Interactive templates invoked by user choice

NameDescription
design_boardFull loop: requirements → sourced schematic → verified PCB → the fab's order pack
source_bomGive every part a part number from the project's parts providers and report cost/stock risks
review_designERC, DRC and engineering review, then fix what is safe to fix and report the rest
release_to_fabFinal checks and the Gerber/BOM/placement pack for the project's fab profile

Resources

Contextual data attached and managed by the client

NameDescription
statusKiCad installs, capabilities, active project, parts providers + fab profile and next step (JSON)
guideWorkflow, conventions, parts providers and fab profiles for driving KiCad through Copper
helpEvery tool with a one-line summary; read copper://help/<tool> for its long examples (JSON)
session-creditsWhat this session's Copper calls cost: calls, result bytes, images and estimated tokens per tool (JSON)
project-summarySheets, components, nets, rails, sourced %, board, config, last ERC/DRC (JSON)
project-bomGrouped bill of materials with part numbers, MPN and manufacturer (JSON)
project-netlistCompact netlist: one line per net, REF.PIN(name) members (text)
project-ercLatest ERC counts and findings with fix hints (JSON)

TDQS

A4/5.0

Scored across 31 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count2/5

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.

Completeness5/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues