kicad-mcp/main.py
Ryan Malloy 4ebf8f08e9 Fix .env loading order so KICAD_SEARCH_PATHS takes effect
config.py evaluates os.environ at import time, but mckicad/__init__.py
eagerly imports config via 'from .config import *'. The old main.py
loaded .env after importing from mckicad, so the search paths were
always empty. Now .env is parsed with stdlib before any mckicad imports.

Also fix start.sh to use 'uv run' instead of stale venv/ path.
2026-03-03 16:53:40 -07:00

70 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""
KiCad MCP Server - A Model Context Protocol server for KiCad on macOS.
This server allows Claude and other MCP clients to interact with KiCad projects.
"""
import os
import logging
# --- Setup Logging ---
log_file = os.path.join(os.path.dirname(__file__), 'mckicad.log')
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - [PID:%(process)d] - %(message)s',
handlers=[
logging.FileHandler(log_file, mode='w'),
]
)
# ---------------------
logging.info("--- Server Starting ---")
# Load .env BEFORE any mckicad imports. Importing mckicad.utils.env would
# trigger mckicad/__init__.py which eagerly imports config.py, evaluating
# os.environ.get("KICAD_SEARCH_PATHS") before .env is loaded. So we
# inline the loading here using only stdlib.
_dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(_dotenv_path):
logging.info(f"Loading .env from: {_dotenv_path}")
with open(_dotenv_path) as _f:
for _line in _f:
_line = _line.strip()
if not _line or _line.startswith("#"):
continue
if "=" in _line:
_key, _val = _line.split("=", 1)
_key, _val = _key.strip(), _val.strip()
if (_val.startswith('"') and _val.endswith('"')) or (
_val.startswith("'") and _val.endswith("'")
):
_val = _val[1:-1]
os.environ[_key] = _val
logging.info(f" {_key}={_val}")
else:
logging.info("No .env file found")
# Now safe to import mckicad — config.py will see the .env values
from mckicad.config import KICAD_USER_DIR, ADDITIONAL_SEARCH_PATHS
from mckicad.server import main as server_main
logging.info(f"KICAD_USER_DIR: {KICAD_USER_DIR}")
logging.info(f"ADDITIONAL_SEARCH_PATHS: {ADDITIONAL_SEARCH_PATHS}")
if __name__ == "__main__":
try:
logging.info(f"Starting KiCad MCP server process")
# Print search paths from config
logging.info(f"Using KiCad user directory: {KICAD_USER_DIR}") # Changed print to logging
if ADDITIONAL_SEARCH_PATHS:
logging.info(f"Additional search paths: {', '.join(ADDITIONAL_SEARCH_PATHS)}") # Changed print to logging
else:
logging.info(f"No additional search paths configured") # Changed print to logging
# Run server
logging.info(f"Running server with stdio transport") # Changed print to logging
server_main()
except Exception as e:
logging.exception(f"Unhandled exception in main") # Log exception details
raise