kicad-mcp/CLAUDE.md
Ryan Malloy 458583ee08
Some checks are pending
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
CI / Lint and Format (push) Waiting to run
CI / Test Python 3.11 on macos-latest (push) Waiting to run
CI / Test Python 3.12 on macos-latest (push) Waiting to run
CI / Test Python 3.13 on macos-latest (push) Waiting to run
CI / Test Python 3.10 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.11 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.12 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.13 on ubuntu-latest (push) Waiting to run
Add autowire documentation and update project structure
Autowire guide covers the decision tree, threshold tuning, workflow
patterns, and troubleshooting. CLAUDE.md updated to reflect the new
autowire/ package and tools/autowire.py.
2026-03-09 00:35:15 -06:00

141 lines
5.6 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code when working with the mckicad codebase.
## Development Commands
- `make install` - Install dependencies with uv (creates .venv)
- `make run` - Start the MCP server (`uv run python main.py`)
- `make test` - Run all tests (`uv run pytest tests/ -v`)
- `make test <file>` - Run a specific test file
- `make lint` - Lint with ruff + mypy (`src/mckicad/` and `tests/`)
- `make format` - Auto-format with ruff
- `make build` - Build package
- `make clean` - Remove build artifacts and caches
Python 3.10+ required. Uses `uv` for everything. Configure via `.env` (copy `.env.example`).
## Architecture
mckicad is a FastMCP 3 server for KiCad electronic design automation. It uses src-layout packaging with `hatchling` as the build backend.
### Project Structure
```
src/mckicad/
__init__.py # __version__ only
server.py # FastMCP 3 server + lifespan + module imports
config.py # Lazy config functions (no module-level env reads)
autowire/
__init__.py # Package init
strategy.py # Wiring decision tree: classify_net, crossing estimation
planner.py # NetPlan → apply_batch JSON conversion
tools/
autowire.py # autowire_schematic MCP tool (1 tool)
schematic.py # kicad-sch-api: create/edit schematics (9 tools)
project.py # Project discovery and structure (3 tools)
drc.py # DRC checking + manufacturing constraints (4 tools)
bom.py # BOM generation and export (2 tools)
export.py # Gerber, drill, PDF, SVG via kicad-cli (4 tools)
routing.py # FreeRouting autorouter integration (3 tools)
analysis.py # Board validation + real-time analysis (3 tools)
pcb.py # IPC-based PCB manipulation via kipy (5 tools)
resources/
projects.py # kicad://projects resource
files.py # kicad://project/{path} resource
prompts/
templates.py # debug_pcb, analyze_bom, design_circuit, debug_schematic
utils/
kicad_cli.py # KiCad CLI detection and execution
path_validator.py # Path security / directory traversal prevention
secure_subprocess.py # Safe subprocess execution with timeouts
ipc_client.py # kipy IPC wrapper for live KiCad connection
freerouting.py # FreeRouting JAR engine
file_utils.py # Project file discovery
kicad_utils.py # KiCad path detection, project search
tests/
conftest.py # Shared fixtures (tmp dirs, project paths)
test_*.py # Per-module test files
main.py # Entry point: .env loader + server start
```
### Key Design Decisions
**Lazy config** (`config.py`): All environment-dependent values are accessed via functions (`get_search_paths()`, `get_kicad_user_dir()`) called at runtime, not at import time. Static constants (`KICAD_EXTENSIONS`, `TIMEOUT_CONSTANTS`, `COMMON_LIBRARIES`) remain as module-level dicts since they don't read env vars. This eliminates the .env load-order race condition.
**Decorator-based tool registration**: Each tool module imports `mcp` from `server.py` and decorates functions with `@mcp.tool()` at module level. `server.py` imports the modules to trigger registration. No `register_*_tools()` boilerplate.
**Schematic abstraction point**: `tools/schematic.py` uses `kicad-sch-api` for file-level schematic manipulation. The `_get_schematic_engine()` helper exists as a swap point for when kipy adds schematic IPC support.
**Dual-mode operation**: PCB tools work via IPC (kipy, requires running KiCad) or CLI (kicad-cli, batch mode). Tools degrade gracefully when KiCad isn't running.
### Tool Registration Pattern
```python
# tools/example.py
from mckicad.server import mcp
@mcp.tool()
def my_tool(param: str) -> dict:
"""Tool description for the calling LLM."""
return {"success": True, "data": "..."}
```
### Tool Return Convention
All tools return dicts with at least `success: bool`. On failure, include `error: str`. On success, include relevant data fields.
## Adding New Features
1. Choose the right module (or create one in `tools/`)
2. Import `mcp` from `mckicad.server`
3. Decorate with `@mcp.tool()` and add a clear docstring
4. If new module: add import in `server.py`
5. Write tests in `tests/test_<module>.py`
## Security
- All file paths validated via `utils/path_validator.py` before access
- External commands run through `utils/secure_subprocess.py` with timeouts
- KiCad CLI commands sanitized — no shell injection
- `main.py` inline .env loader runs before any mckicad imports
## Environment Variables
- `KICAD_USER_DIR` - KiCad user config directory
- `KICAD_SEARCH_PATHS` - Comma-separated project search paths
- `KICAD_CLI_PATH` - Explicit kicad-cli path
- `FREEROUTING_JAR_PATH` - Path to FreeRouting JAR
- `LOG_LEVEL` - Logging level (default: INFO)
## Testing
Markers: `unit`, `integration`, `requires_kicad`, `slow`, `performance`
```bash
make test # all tests
make test tests/test_schematic.py # one file
uv run pytest -m "unit" # by marker
```
## Entry Point
```toml
[project.scripts]
mckicad = "mckicad.server:main"
```
Run via `uvx mckicad`, `uv run mckicad`, or `uv run python main.py`.
## FreeRouting Setup
1. Download JAR from https://freerouting.app/
2. Place at `~/freerouting.jar`, `/usr/local/bin/freerouting.jar`, or `/opt/freerouting/freerouting.jar`
3. Install Java runtime
4. Verify with `check_routing_capability()` tool
5. Or set `FREEROUTING_JAR_PATH` in `.env`
## Logging
Logs go to `mckicad.log` in project root, overwritten each start. Never use `print()` — MCP uses stdin/stdout for JSON-RPC transport.