Ryan Malloy bc0f3db97c
Some checks are pending
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
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
Implement comprehensive AI/LLM integration for KiCad MCP server
Add intelligent analysis and recommendation tools for KiCad designs:

## New AI Tools (kicad_mcp/tools/ai_tools.py)
- suggest_components_for_circuit: Smart component suggestions based on circuit analysis
- recommend_design_rules: Automated design rule recommendations for different technologies
- optimize_pcb_layout: PCB layout optimization for signal integrity, thermal, and cost
- analyze_design_completeness: Comprehensive design completeness analysis

## Enhanced Utilities
- component_utils.py: Add ComponentType enum and component classification functions
- pattern_recognition.py: Enhanced circuit pattern analysis and recommendations
- netlist_parser.py: Implement missing parse_netlist_file function for AI tools

## Key Features
- Circuit pattern recognition for power supplies, amplifiers, microcontrollers
- Technology-specific design rules (standard, HDI, RF, automotive)
- Layout optimization suggestions with implementation steps
- Component suggestion system with standard values and examples
- Design completeness scoring with actionable recommendations

## Server Integration
- Register AI tools in FastMCP server
- Integrate with existing KiCad utilities and file parsers
- Error handling and graceful fallbacks for missing data

Fixes ImportError that prevented server startup and enables advanced
AI-powered design assistance for KiCad projects.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-11 16:15:58 -06:00

125 lines
3.9 KiB
Python

"""
Environment variable handling for KiCad MCP Server.
"""
import logging
import os
def load_dotenv(env_file: str = ".env") -> dict[str, str]:
"""Load environment variables from .env file.
Args:
env_file: Path to the .env file
Returns:
Dictionary of loaded environment variables
"""
env_vars = {}
logging.info(f"load_dotenv called for file: {env_file}")
# Try to find .env file in the current directory or parent directories
env_path = find_env_file(env_file)
if not env_path:
logging.warning(f"No .env file found matching: {env_file}")
return env_vars
logging.info(f"Found .env file at: {env_path}")
try:
with open(env_path) as f:
logging.info(f"Successfully opened {env_path} for reading.")
line_num = 0
for line in f:
line_num += 1
line = line.strip()
# Skip empty lines and comments
if not line or line.startswith("#"):
logging.debug(f"Skipping line {line_num} (comment/empty): {line}")
continue
# Parse key-value pairs
if "=" in line:
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
logging.debug(f"Parsed line {line_num}: Key='{key}', RawValue='{value}'")
# Remove quotes if present
if value.startswith('"') and value.endswith('"') or value.startswith("'") and value.endswith("'"):
value = value[1:-1]
# Expand ~ to user's home directory
original_value = value
if "~" in value:
value = os.path.expanduser(value)
if value != original_value:
logging.debug(
f"Expanded ~ in value for key '{key}': '{original_value}' -> '{value}'"
)
# Set environment variable
logging.info(f"Setting os.environ['{key}'] = '{value}'")
os.environ[key] = value
env_vars[key] = value
else:
logging.warning(f"Skipping line {line_num} (no '=' found): {line}")
logging.info(f"Finished processing {env_path}")
except Exception:
# Use logging.exception to include traceback
logging.exception(f"Error loading .env file '{env_path}'")
logging.info(f"load_dotenv returning: {env_vars}")
return env_vars
def find_env_file(filename: str = ".env") -> str | None:
"""Find a .env file in the current directory or parent directories.
Args:
filename: Name of the env file to find
Returns:
Path to the env file if found, None otherwise
"""
current_dir = os.getcwd()
logging.info(f"find_env_file starting search from: {current_dir}")
max_levels = 3 # Limit how far up to search
for _ in range(max_levels):
env_path = os.path.join(current_dir, filename)
if os.path.exists(env_path):
return env_path
# Move up one directory
parent_dir = os.path.dirname(current_dir)
if parent_dir == current_dir: # We've reached the root
break
current_dir = parent_dir
return None
def get_env_list(env_var: str, default: str = "") -> list:
"""Get a list from a comma-separated environment variable.
Args:
env_var: Name of the environment variable
default: Default value if environment variable is not set
Returns:
List of values
"""
value = os.environ.get(env_var, default)
if not value:
return []
# Split by comma and strip whitespace
items = [item.strip() for item in value.split(",")]
# Filter out empty items
return [item for item in items if item]