Fix drc issues - swap out pbnew for kicad cli and ipc

This commit is contained in:
Lama 2025-03-21 15:27:36 -04:00
parent b1cb48ecf7
commit a3613f273a
9 changed files with 548 additions and 262 deletions

View File

@ -25,6 +25,7 @@ This guide will help you set up a Model Context Protocol (MCP) server for KiCad.
- macOS, Windows, or Linux with KiCad installed
- Python 3.10 or higher
- KiCad 9.0 or higher
- Claude Desktop (or another MCP client)
- Basic familiarity with the terminal
@ -154,9 +155,10 @@ The KiCad MCP Server provides several key features, each with detailed documenta
- **BOM Management**: Analyze and export Bills of Materials
- *Example:* "Generate a BOM for my smart watch project" → Creates a detailed bill of materials
- **Design Rule Checking**: Run DRC checks and track your progress over time
- **Design Rule Checking**: Run DRC checks and track your progress over time
- *Example:* "Run DRC on my power supply board and compare to last week" → Shows progress in fixing violations
- *KiCad 9.0+ Compatible:* Uses the new KiCad CLI or IPC API automatically
- **PCB Visualization**: Generate visual representations of your PCB layouts
- *Example:* "Show me a thumbnail of my audio amplifier PCB" → Displays a visual render of the board

View File

@ -11,6 +11,24 @@ The Design Rule Check (DRC) functionality allows you to:
3. Track your progress over time as you fix issues
4. Compare current results with previous checks
## KiCad 9.0+ Compatibility
**Important Update**: With KiCad 9.0+, the DRC functionality has been reimplemented to work with the new KiCad APIs. The server now supports two methods for running DRC:
1. **KiCad CLI Method** (Recommended) - Uses the `kicad-cli` command-line tool to run DRC checks without requiring a running instance of KiCad.
2. **IPC API Method** - Connects to a running instance of KiCad through the new IPC API using the `kicad-python` package.
The server automatically selects the best available method based on your KiCad installation.
## Prerequisites
For optimal DRC functionality with KiCad 9.0+, you should have:
- KiCad 9.0 or newer installed
- `kicad-cli` available in your system PATH (included with KiCad 9.0+)
- For IPC API functionality: the `kicad-python` package installed (`pip install kicad-python`)
## Using DRC Features
### Running a DRC Check
@ -25,6 +43,7 @@ Please run a DRC check on my project at /Users/username/Documents/KiCad/my_proje
```
The tool will:
- Automatically select the best available method (CLI or IPC API)
- Analyze your PCB design for rule violations
- Generate a comprehensive report
- Save the results to your DRC history
@ -122,5 +141,19 @@ If the DRC check fails to run:
1. Ensure your KiCad project exists at the specified path
2. Verify that the project contains a PCB file (.kicad_pcb)
3. Check that the KiCad installation is detected correctly
3. Check your KiCad installation:
- For CLI method: Verify `kicad-cli` is in your PATH or in a standard installation location
- For IPC API method: Make sure KiCad is running with the API server enabled in Preferences > Plugins
4. Try using the full absolute path to your project file
### Method Selection Issues
If you want to force a specific DRC method:
1. **CLI Method**: Ensure `kicad-cli` is available in your PATH
2. **IPC API Method**:
- Install the `kicad-python` package
- Launch KiCad before running the DRC check
- Enable the API server in KiCad preferences
If you continue to experience issues, check the server logs for more detailed error information.

View File

@ -10,7 +10,7 @@ from mcp.server.fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files
from kicad_mcp.utils.logger import Logger
from kicad_mcp.utils.drc_history import get_drc_history
from kicad_mcp.tools.drc_tools import run_drc_via_cli
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli
# Create logger for this module
logger = Logger()

View File

@ -0,0 +1,3 @@
"""
DRC implementations for different KiCad API approaches.
"""

View File

@ -0,0 +1,169 @@
"""
Design Rule Check (DRC) implementation using KiCad command-line interface.
"""
import os
import json
import subprocess
import tempfile
from typing import Dict, Any, Optional
from mcp.server.fastmcp import Context
from kicad_mcp.utils.logger import Logger
from kicad_mcp.config import system
# Create logger for this module
logger = Logger()
async def run_drc_via_cli(pcb_file: str, ctx: Context) -> Dict[str, Any]:
"""Run DRC using KiCad command line tools.
Args:
pcb_file: Path to the PCB file (.kicad_pcb)
ctx: MCP context for progress reporting
Returns:
Dictionary with DRC results
"""
results = {
"success": False,
"method": "cli",
"pcb_file": pcb_file
}
try:
# Create a temporary directory for the output
with tempfile.TemporaryDirectory() as temp_dir:
# Output file for DRC report
output_file = os.path.join(temp_dir, "drc_report.json")
# Find kicad-cli executable
kicad_cli = find_kicad_cli()
if not kicad_cli:
logger.error("kicad-cli not found in PATH or common installation locations")
results["error"] = "kicad-cli not found. Please ensure KiCad 9.0+ is installed and kicad-cli is available."
return results
# Report progress
await ctx.report_progress(50, 100)
ctx.info("Running DRC using KiCad CLI...")
# Build the DRC command
cmd = [
kicad_cli,
"pcb",
"drc",
"--format", "json",
"--output", output_file,
pcb_file
]
logger.info(f"Running command: {' '.join(cmd)}")
process = subprocess.run(cmd, capture_output=True, text=True)
# Check if the command was successful
if process.returncode != 0:
logger.error(f"DRC command failed with code {process.returncode}")
logger.error(f"Error output: {process.stderr}")
results["error"] = f"DRC command failed: {process.stderr}"
return results
# Check if the output file was created
if not os.path.exists(output_file):
logger.error("DRC report file not created")
results["error"] = "DRC report file not created"
return results
# Read the DRC report
with open(output_file, 'r') as f:
try:
drc_report = json.load(f)
except json.JSONDecodeError:
logger.error("Failed to parse DRC report JSON")
results["error"] = "Failed to parse DRC report JSON"
return results
# Process the DRC report
violations = drc_report.get("violations", [])
violation_count = len(violations)
logger.info(f"DRC completed with {violation_count} violations")
await ctx.report_progress(70, 100)
ctx.info(f"DRC completed with {violation_count} violations")
# Categorize violations by type
error_types = {}
for violation in violations:
error_type = violation.get("message", "Unknown")
if error_type not in error_types:
error_types[error_type] = 0
error_types[error_type] += 1
# Create success response
results = {
"success": True,
"method": "cli",
"pcb_file": pcb_file,
"total_violations": violation_count,
"violation_categories": error_types,
"violations": violations
}
await ctx.report_progress(90, 100)
return results
except Exception as e:
logger.error(f"Error in CLI DRC: {str(e)}", exc_info=True)
results["error"] = f"Error in CLI DRC: {str(e)}"
return results
def find_kicad_cli() -> Optional[str]:
"""Find the kicad-cli executable in the system PATH.
Returns:
Path to kicad-cli if found, None otherwise
"""
# Check if kicad-cli is in PATH
try:
if system == "Windows":
# On Windows, check for kicad-cli.exe
result = subprocess.run(["where", "kicad-cli.exe"], capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip().split("\n")[0]
else:
# On Unix-like systems, use which
result = subprocess.run(["which", "kicad-cli"], capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip()
except Exception as e:
logger.error(f"Error finding kicad-cli: {str(e)}")
# If we get here, kicad-cli is not in PATH
# Try common installation locations
if system == "Windows":
# Common Windows installation path
potential_paths = [
r"C:\Program Files\KiCad\bin\kicad-cli.exe",
r"C:\Program Files (x86)\KiCad\bin\kicad-cli.exe"
]
elif system == "Darwin": # macOS
# Common macOS installation paths
potential_paths = [
"/Applications/KiCad/KiCad.app/Contents/MacOS/kicad-cli",
"/Applications/KiCad/kicad-cli"
]
else: # Linux and other Unix-like systems
# Common Linux installation paths
potential_paths = [
"/usr/bin/kicad-cli",
"/usr/local/bin/kicad-cli",
"/opt/kicad/bin/kicad-cli"
]
# Check each potential path
for path in potential_paths:
if os.path.exists(path) and os.access(path, os.X_OK):
return path
# If still not found, return None
return None

View File

@ -0,0 +1,163 @@
"""
Design Rule Check (DRC) implementation using the KiCad IPC API.
"""
import os
from typing import Dict, Any
from mcp.server.fastmcp import Context
from kicad_mcp.utils.logger import Logger
from kicad_mcp.utils.kicad_api_detection import check_ipc_api_environment
# Create logger for this module
logger = Logger()
async def run_drc_with_ipc_api(pcb_file: str, ctx: Context) -> Dict[str, Any]:
"""Run DRC using the KiCad IPC API (kicad-python).
This requires a running instance of KiCad with the IPC API enabled.
Args:
pcb_file: Path to the PCB file (.kicad_pcb)
ctx: MCP context for progress reporting
Returns:
Dictionary with DRC results
"""
try:
# Import the kicad-python modules
import kipy
from kipy.board_types import DrcExclusion, DrcSeverity
logger.info("Successfully imported kipy modules")
# Check if we're running in a KiCad IPC plugin environment
is_plugin, socket_path = check_ipc_api_environment()
# Connect to KiCad
await ctx.report_progress(20, 100)
ctx.info("Connecting to KiCad...")
if is_plugin:
# When running as a plugin, let kipy use environment variables
kicad = kipy.KiCad()
else:
# When running standalone, try to connect to KiCad
if socket_path:
kicad = kipy.KiCad(socket_path=socket_path)
else:
# Try with default socket path
kicad = kipy.KiCad()
# Get the currently open board
await ctx.report_progress(30, 100)
ctx.info("Getting board...")
# Check which board to use
current_boards = await kicad.get_open_documents("board")
# If we have an open board, check if it's the one we want
use_current_board = False
board_doc = None
if current_boards:
for doc in current_boards:
if doc.file_path and os.path.normpath(doc.file_path) == os.path.normpath(pcb_file):
board_doc = doc
use_current_board = True
break
# If the board isn't open, see if we can open it
if not use_current_board:
ctx.info(f"Opening board file: {pcb_file}")
try:
# Try to open the board
doc = await kicad.open_document(pcb_file)
board_doc = doc
except Exception as e:
logger.error(f"Error opening board: {str(e)}")
return {
"success": False,
"method": "ipc",
"error": f"Failed to open board file: {str(e)}"
}
# Get the board
board = await kicad.board.get_board(board_doc.uuid)
# Run DRC
await ctx.report_progress(50, 100)
ctx.info("Running DRC check...")
# Define which severities to include
severity_filter = DrcSeverity.ERROR | DrcSeverity.WARNING
# Run DRC on the board
drc_report = await kicad.board.run_drc(
board.uuid,
severity_filter=severity_filter,
exclusions=DrcExclusion.NONE # Include all violations
)
# Process results
await ctx.report_progress(70, 100)
ctx.info("Processing DRC results...")
# Get all violations
violations = drc_report.violations
violation_count = len(violations)
logger.info(f"DRC completed with {violation_count} violations")
ctx.info(f"DRC completed with {violation_count} violations")
# Process the violations
drc_errors = []
error_types = {}
for violation in violations:
# Get violation details
severity = str(violation.severity)
message = violation.message
# Extract location
location = {
"x": violation.location.x if hasattr(violation, 'location') and violation.location else 0,
"y": violation.location.y if hasattr(violation, 'location') and violation.location else 0
}
error_info = {
"severity": severity,
"message": message,
"location": location
}
drc_errors.append(error_info)
# Count by type
if message not in error_types:
error_types[message] = 0
error_types[message] += 1
# Create result
results = {
"success": True,
"method": "ipc",
"pcb_file": pcb_file,
"total_violations": violation_count,
"violation_categories": error_types,
"violations": drc_errors
}
return results
except ImportError as e:
logger.error(f"Failed to import kipy modules: {str(e)}")
return {
"success": False,
"method": "ipc",
"error": f"Failed to import kipy modules: {str(e)}"
}
except Exception as e:
logger.error(f"Error in IPC API DRC: {str(e)}", exc_info=True)
return {
"success": False,
"method": "ipc",
"error": f"Error in IPC API DRC: {str(e)}"
}

View File

@ -2,26 +2,26 @@
Design Rule Check (DRC) tools for KiCad PCB files.
"""
import os
import json
import subprocess
import tempfile
from typing import Dict, Any, List, Optional, Tuple
from typing import Dict, Any
from mcp.server.fastmcp import FastMCP, Context
from kicad_mcp.utils.file_utils import get_project_files
from kicad_mcp.utils.logger import Logger
from kicad_mcp.utils.drc_history import save_drc_result, get_drc_history, compare_with_previous
from kicad_mcp.config import KICAD_APP_PATH, system
from kicad_mcp.utils.kicad_api_detection import get_best_api_approach
# Import implementations
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli
from kicad_mcp.tools.drc_impl.ipc_drc import run_drc_with_ipc_api
# Create logger for this module
logger = Logger()
def register_drc_tools(mcp: FastMCP, kicad_modules_available: bool = False) -> None:
def register_drc_tools(mcp: FastMCP) -> None:
"""Register DRC tools with the MCP server.
Args:
mcp: The FastMCP server instance
kicad_modules_available: Whether KiCad Python modules are available
"""
@mcp.tool()
@ -73,6 +73,7 @@ def register_drc_tools(mcp: FastMCP, kicad_modules_available: bool = False) -> N
Args:
project_path: Path to the KiCad project file (.kicad_pro)
ctx: MCP context for progress reporting
Returns:
Dictionary with DRC results and statistics
@ -96,38 +97,35 @@ def register_drc_tools(mcp: FastMCP, kicad_modules_available: bool = False) -> N
await ctx.report_progress(10, 100)
ctx.info(f"Starting DRC check on {os.path.basename(pcb_file)}")
# Try to use pcbnew if available
if kicad_modules_available:
try:
drc_results = await run_drc_with_pcbnew(pcb_file, ctx)
if drc_results["success"]:
# Save results to history
save_drc_result(project_path, drc_results)
# Add comparison with previous run
comparison = compare_with_previous(project_path, drc_results)
if comparison:
drc_results["comparison"] = comparison
if comparison["change"] < 0:
ctx.info(f"Great progress! You've fixed {abs(comparison['change'])} DRC violations since the last check.")
elif comparison["change"] > 0:
ctx.info(f"Found {comparison['change']} new DRC violations since the last check.")
else:
ctx.info(f"No change in the number of DRC violations since the last check.")
return drc_results
except Exception as e:
logger.error(f"Error running DRC with pcbnew: {str(e)}", exc_info=True)
ctx.info(f"Error running DRC with pcbnew: {str(e)}")
# Fall back to CLI method if pcbnew fails
# Get app context and determine which approach to use
app_context = ctx.request_context.lifespan_context
api_approach = getattr(app_context, 'api_approach', get_best_api_approach())
# Fall back to command line DRC check
logger.info("Attempting DRC check via command line")
await ctx.report_progress(30, 100)
drc_results = run_drc_via_cli(pcb_file)
# Run DRC using the appropriate approach
drc_results = None
if drc_results["success"]:
if api_approach == "cli":
# Use CLI approach (kicad-cli)
logger.info("Using kicad-cli for DRC")
ctx.info("Using KiCad CLI for DRC check...")
drc_results = await run_drc_via_cli(pcb_file, ctx)
elif api_approach == "ipc":
# Use IPC API approach (kicad-python)
logger.info("Using IPC API for DRC")
ctx.info("Using KiCad IPC API for DRC check...")
drc_results = await run_drc_with_ipc_api(pcb_file, ctx)
else:
# No API available
logger.error("No KiCad API available for DRC")
return {
"success": False,
"error": "No KiCad API available for DRC. Please install KiCad 9.0 or later."
}
# Process and save results if successful
if drc_results and drc_results.get("success", False):
# Save results to history
save_drc_result(project_path, drc_results)
@ -146,225 +144,7 @@ def register_drc_tools(mcp: FastMCP, kicad_modules_available: bool = False) -> N
# Complete progress
await ctx.report_progress(100, 100)
return drc_results
async def run_drc_with_pcbnew(pcb_file: str, ctx: Context) -> Dict[str, Any]:
"""Run DRC using the pcbnew Python module.
Args:
pcb_file: Path to the PCB file (.kicad_pcb)
ctx: MCP context for progress reporting
Returns:
Dictionary with DRC results
"""
try:
import pcbnew
logger.info("Successfully imported pcbnew module")
# Load the board
board = pcbnew.LoadBoard(pcb_file)
if not board:
logger.error("Failed to load PCB file")
return {"success": False, "error": "Failed to load PCB file"}
await ctx.report_progress(40, 100)
ctx.info("PCB file loaded, running DRC checks...")
# Create a DRC runner
drc = pcbnew.DRC(board)
drc.SetViolationHandler(pcbnew.DRC_ITEM_LIST())
# Run the DRC
drc.Run()
await ctx.report_progress(70, 100)
# Get the violations
violations = drc.GetViolations()
violation_count = violations.GetCount()
logger.info(f"DRC completed with {violation_count} violations")
ctx.info(f"DRC completed with {violation_count} violations")
# Process the violations
drc_errors = []
for i in range(violation_count):
violation = violations.GetItem(i)
error_info = {
"severity": violation.GetSeverity(),
"message": violation.GetErrorMessage(),
"location": {
"x": violation.GetPointA().x / 1000000.0, # Convert to mm
"y": violation.GetPointA().y / 1000000.0
}
}
drc_errors.append(error_info)
await ctx.report_progress(90, 100)
# Categorize violations by type
error_types = {}
for error in drc_errors:
error_type = error["message"]
if error_type not in error_types:
error_types[error_type] = 0
error_types[error_type] += 1
# Create summary
results = {
"success": True,
"method": "pcbnew",
"pcb_file": pcb_file,
"total_violations": violation_count,
"violation_categories": error_types,
"violations": drc_errors
return drc_results or {
"success": False,
"error": "DRC check failed with an unknown error"
}
return results
except ImportError as e:
logger.error(f"Failed to import pcbnew: {str(e)}")
raise
except Exception as e:
logger.error(f"Error in pcbnew DRC: {str(e)}", exc_info=True)
raise
def run_drc_via_cli(pcb_file: str) -> Dict[str, Any]:
"""Run DRC using KiCad command line tools.
This is a fallback method when pcbnew Python module is not available.
Args:
pcb_file: Path to the PCB file (.kicad_pcb)
Returns:
Dictionary with DRC results
"""
results = {
"success": False,
"method": "cli",
"pcb_file": pcb_file
}
try:
# Create a temporary directory for the output
with tempfile.TemporaryDirectory() as temp_dir:
# The command to run DRC depends on the operating system
if system == "Darwin": # macOS
# Path to KiCad command line tools on macOS
pcbnew_cli = os.path.join(KICAD_APP_PATH, "Contents/MacOS/pcbnew_cli")
# Check if the CLI tool exists
if not os.path.exists(pcbnew_cli):
logger.error(f"pcbnew_cli not found at {pcbnew_cli}")
results["error"] = f"pcbnew_cli not found at {pcbnew_cli}"
return results
# Output file for DRC report
output_file = os.path.join(temp_dir, "drc_report.json")
# Run the DRC command
cmd = [
pcbnew_cli,
"--run-drc",
"--output-json", output_file,
pcb_file
]
elif system == "Windows":
# Path to KiCad command line tools on Windows
pcbnew_cli = os.path.join(KICAD_APP_PATH, "bin", "pcbnew_cli.exe")
# Check if the CLI tool exists
if not os.path.exists(pcbnew_cli):
logger.error(f"pcbnew_cli not found at {pcbnew_cli}")
results["error"] = f"pcbnew_cli not found at {pcbnew_cli}"
return results
# Output file for DRC report
output_file = os.path.join(temp_dir, "drc_report.json")
# Run the DRC command
cmd = [
pcbnew_cli,
"--run-drc",
"--output-json", output_file,
pcb_file
]
elif system == "Linux":
# Path to KiCad command line tools on Linux
pcbnew_cli = "pcbnew_cli" # Assume it's in the PATH
# Output file for DRC report
output_file = os.path.join(temp_dir, "drc_report.json")
# Run the DRC command
cmd = [
pcbnew_cli,
"--run-drc",
"--output-json", output_file,
pcb_file
]
else:
results["error"] = f"Unsupported operating system: {system}"
return results
logger.info(f"Running command: {' '.join(cmd)}")
process = subprocess.run(cmd, capture_output=True, text=True)
# Check if the command was successful
if process.returncode != 0:
logger.error(f"DRC command failed with code {process.returncode}")
logger.error(f"Error output: {process.stderr}")
results["error"] = f"DRC command failed: {process.stderr}"
return results
# Check if the output file was created
if not os.path.exists(output_file):
logger.error("DRC report file not created")
results["error"] = "DRC report file not created"
return results
# Read the DRC report
with open(output_file, 'r') as f:
try:
drc_report = json.load(f)
except json.JSONDecodeError:
logger.error("Failed to parse DRC report JSON")
results["error"] = "Failed to parse DRC report JSON"
return results
# Process the DRC report
violation_count = len(drc_report.get("violations", []))
logger.info(f"DRC completed with {violation_count} violations")
# Extract violations
violations = drc_report.get("violations", [])
# Categorize violations by type
error_types = {}
for violation in violations:
error_type = violation.get("message", "Unknown")
if error_type not in error_types:
error_types[error_type] = 0
error_types[error_type] += 1
# Create success response
results = {
"success": True,
"method": "cli",
"pcb_file": pcb_file,
"total_violations": violation_count,
"violation_categories": error_types,
"violations": violations
}
return results
except Exception as e:
logger.error(f"Error in CLI DRC: {str(e)}", exc_info=True)
results["error"] = f"Error in CLI DRC: {str(e)}"
return results

View File

@ -0,0 +1,135 @@
"""
Utility functions for detecting and selecting available KiCad API approaches.
"""
import os
import subprocess
import shutil
from typing import Tuple, Optional, Literal
from kicad_mcp.utils.logger import Logger
from kicad_mcp.config import system
# Create logger for this module
logger = Logger()
def check_for_cli_api() -> bool:
"""Check if KiCad CLI API is available.
Returns:
True if KiCad CLI is available, False otherwise
"""
try:
# Check if kicad-cli is in PATH
if system == "Windows":
# On Windows, check for kicad-cli.exe
kicad_cli = shutil.which("kicad-cli.exe")
else:
# On Unix-like systems
kicad_cli = shutil.which("kicad-cli")
if kicad_cli:
# Verify it's a working kicad-cli
if system == "Windows":
cmd = [kicad_cli, "--version"]
else:
cmd = [kicad_cli, "--version"]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
logger.info(f"Found working kicad-cli: {kicad_cli}")
return True
# Check common installation locations if not found in PATH
if system == "Windows":
# Common Windows installation paths
potential_paths = [
r"C:\Program Files\KiCad\bin\kicad-cli.exe",
r"C:\Program Files (x86)\KiCad\bin\kicad-cli.exe"
]
elif system == "Darwin": # macOS
# Common macOS installation paths
potential_paths = [
"/Applications/KiCad/KiCad.app/Contents/MacOS/kicad-cli",
"/Applications/KiCad/kicad-cli"
]
else: # Linux
# Common Linux installation paths
potential_paths = [
"/usr/bin/kicad-cli",
"/usr/local/bin/kicad-cli",
"/opt/kicad/bin/kicad-cli"
]
# Check each potential path
for path in potential_paths:
if os.path.exists(path) and os.access(path, os.X_OK):
logger.info(f"Found kicad-cli at common location: {path}")
return True
logger.info("KiCad CLI API is not available")
return False
except Exception as e:
logger.error(f"Error checking for KiCad CLI API: {str(e)}")
return False
def check_for_ipc_api() -> bool:
"""Check if KiCad IPC API (kicad-python) is available.
Returns:
True if KiCad IPC API is available, False otherwise
"""
try:
# Try to import the kipy module
import kipy
logger.info("KiCad IPC API (kicad-python) is available")
return True
except ImportError:
logger.info("KiCad IPC API (kicad-python) is not available")
return False
except Exception as e:
logger.error(f"Error checking for KiCad IPC API: {str(e)}")
return False
def check_ipc_api_environment() -> Tuple[bool, Optional[str]]:
"""Check if we're running in a KiCad IPC plugin environment.
Returns:
Tuple of (is_plugin, socket_path)
"""
# Check for environment variables that would indicate we're a plugin
is_plugin = os.environ.get("KICAD_PLUGIN_ENV") is not None
# Check for socket path in environment
socket_path = os.environ.get("KICAD_SOCKET_PATH")
if is_plugin:
logger.info("Running as a KiCad plugin")
elif socket_path:
logger.info(f"KiCad IPC socket path found: {socket_path}")
return (is_plugin, socket_path)
def get_best_api_approach() -> Literal["cli", "ipc", "none"]:
"""Determine the best available KiCad API approach.
Returns:
String indicating which API approach to use:
- "cli": Use KiCad command-line interface
- "ipc": Use KiCad IPC API (kicad-python)
- "none": No API available
"""
# Check for IPC API first (preferred if available)
if check_for_ipc_api():
return "ipc"
# Check for CLI API next
if check_for_cli_api():
return "cli"
# No API available
logger.warning("No KiCad API available")
return "none"

View File

@ -2,3 +2,4 @@ mcp[cli]
httpx
pytest
pandas
kicad-python