""" 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"] INLINE_RESULT_THRESHOLD = 20 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"}, }, } POWER_SYMBOL_DEFAULTS = { "stub_length": 5.08, "grid_snap": 2.54, "fine_grid": 1.27, } DECOUPLING_DEFAULTS = { "cols": 6, "h_spacing": 12.7, "v_spacing": 15.0, "offset_below_ic": 20.0, } BATCH_LIMITS = { "max_components": 500, "max_wires": 1000, "max_labels": 500, "max_total_operations": 2000, } LABEL_DEFAULTS = { "stub_length": 7.62, # 3 grid units (2.54mm each), clears component bodies } AUTOWIRE_DEFAULTS = { "direct_wire_max_distance": 50.0, "crossing_threshold": 2, "high_fanout_threshold": 5, "label_min_distance": 10.0, } 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"], }