kicad-mcp/src/mckicad/config.py
Ryan Malloy 4ae38fed59 Rebuild on FastMCP 3 with src-layout and kicad-sch-api integration
Migrate from FastMCP 2.14.5 to 3.1.0 with complete architectural
overhaul. Adopt src-layout packaging, lazy config functions to
eliminate .env race condition, and decorator-based tool registration.

Consolidate 14 tool modules into 8 focused modules (33 tools total).
Add 9 new schematic tools via kicad-sch-api for creating and
manipulating .kicad_sch files. Drop pandas dependency (BOM uses
stdlib csv). Remove ~17k lines of stubs, over-engineering, and
dead code.

All checks pass: ruff clean, mypy 0 errors, 17/17 tests green.
2026-03-03 18:26:54 -07:00

129 lines
3.7 KiB
Python

"""
Configuration for the mckicad MCP server.
All config is accessed via functions to avoid module-level os.environ.get()
race conditions with .env loading.
"""
import os
import platform
def get_system() -> str:
"""Get the current operating system name."""
return platform.system()
def get_kicad_user_dir() -> str:
"""Get KiCad user documents directory, respecting env override."""
env_val = os.environ.get("KICAD_USER_DIR")
if env_val:
return os.path.expanduser(env_val)
system = get_system()
if system == "Darwin" or system == "Windows":
return os.path.expanduser("~/Documents/KiCad")
elif system == "Linux":
return os.path.expanduser("~/KiCad")
return os.path.expanduser("~/Documents/KiCad")
def get_kicad_app_path() -> str:
"""Get KiCad application installation path, respecting env override."""
env_val = os.environ.get("KICAD_APP_PATH")
if env_val:
return env_val
_app_paths = {
"Darwin": "/Applications/KiCad/KiCad.app",
"Windows": r"C:\Program Files\KiCad",
"Linux": "/usr/share/kicad",
}
return _app_paths.get(get_system(), "/Applications/KiCad/KiCad.app")
def get_search_paths() -> list[str]:
"""Read KICAD_SEARCH_PATHS from env, expand ~, filter to existing dirs."""
paths: list[str] = []
env_val = os.environ.get("KICAD_SEARCH_PATHS", "")
if env_val:
for p in env_val.split(","):
expanded = os.path.expanduser(p.strip())
if os.path.isdir(expanded) and expanded not in paths:
paths.append(expanded)
# Auto-detect common project locations
default_locations = [
"~/Documents/PCB",
"~/PCB",
"~/Electronics",
"~/Projects/Electronics",
"~/Projects/PCB",
"~/Projects/KiCad",
]
for loc in default_locations:
expanded = os.path.expanduser(loc)
if os.path.isdir(expanded) and expanded not in paths:
paths.append(expanded)
return paths
# --- Static configuration (no env dependency) ---
KICAD_EXTENSIONS = {
"project": ".kicad_pro",
"pcb": ".kicad_pcb",
"schematic": ".kicad_sch",
"design_rules": ".kicad_dru",
"worksheet": ".kicad_wks",
"footprint": ".kicad_mod",
"netlist": "_netlist.net",
"kibot_config": ".kibot.yaml",
}
DATA_EXTENSIONS = [".csv", ".pos", ".net", ".zip", ".drl"]
TIMEOUT_CONSTANTS = {
"kicad_cli_version_check": 10.0,
"kicad_cli_export": 30.0,
"application_open": 10.0,
"subprocess_default": 30.0,
}
COMMON_LIBRARIES = {
"basic": {
"resistor": {"library": "Device", "symbol": "R"},
"capacitor": {"library": "Device", "symbol": "C"},
"inductor": {"library": "Device", "symbol": "L"},
"led": {"library": "Device", "symbol": "LED"},
"diode": {"library": "Device", "symbol": "D"},
},
"power": {
"vcc": {"library": "power", "symbol": "VCC"},
"gnd": {"library": "power", "symbol": "GND"},
"+5v": {"library": "power", "symbol": "+5V"},
"+3v3": {"library": "power", "symbol": "+3V3"},
},
"connectors": {
"conn_2pin": {"library": "Connector", "symbol": "Conn_01x02_Male"},
"conn_4pin": {"library": "Connector_Generic", "symbol": "Conn_01x04"},
},
}
DEFAULT_FOOTPRINTS = {
"R": [
"Resistor_SMD:R_0805_2012Metric",
"Resistor_SMD:R_0603_1608Metric",
"Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal",
],
"C": [
"Capacitor_SMD:C_0805_2012Metric",
"Capacitor_SMD:C_0603_1608Metric",
"Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm",
],
"LED": ["LED_SMD:LED_0805_2012Metric", "LED_THT:LED_D5.0mm"],
"D": ["Diode_SMD:D_SOD-123", "Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal"],
}