This massive feature update transforms the KiCad MCP server into a complete EDA automation platform with real-time design capabilities: ## Major New Features ### KiCad IPC API Integration (`utils/ipc_client.py`) - Real-time KiCad communication via kicad-python library - Component placement and manipulation - Live board analysis and statistics - Real-time routing status monitoring - Transaction-based operations with rollback support ### FreeRouting Integration (`utils/freerouting_engine.py`) - Complete automated PCB routing pipeline - DSN export → FreeRouting processing → SES import workflow - Parameter optimization for different routing strategies - Multi-technology support (standard, HDI, RF, automotive) - Routing quality analysis and reporting ### Automated Routing Tools (`tools/routing_tools.py`) - `route_pcb_automatically()` - Complete automated routing - `optimize_component_placement()` - AI-driven placement optimization - `analyze_routing_quality()` - Comprehensive routing analysis - `interactive_routing_session()` - Guided routing assistance - `route_specific_nets()` - Targeted net routing ### Complete Project Automation (`tools/project_automation.py`) - `automate_complete_design()` - End-to-end project automation - `create_outlet_tester_complete()` - Specialized outlet tester creation - `batch_process_projects()` - Multi-project automation pipeline - Seven-stage automation: validation → AI analysis → placement → routing → validation → manufacturing → final analysis ### Enhanced Analysis Tools (`tools/analysis_tools.py`) - `analyze_board_real_time()` - Live board analysis via IPC API - `get_component_details_live()` - Real-time component information - Enhanced `validate_project()` with IPC integration - Live connectivity and routing completion monitoring ## Technical Implementation ### Dependencies Added - `kicad-python>=0.4.0` - Official KiCad IPC API bindings - `requests>=2.31.0` - HTTP client for FreeRouting integration ### Architecture Enhancements - Real-time KiCad session management with automatic cleanup - Transaction-based operations for safe design manipulation - Context managers for reliable resource handling - Comprehensive error handling and recovery ### Integration Points - Seamless CLI + IPC API hybrid approach - FreeRouting autorouter integration via DSN/SES workflow - AI-driven optimization with real-time feedback - Manufacturing-ready file generation pipeline ## Automation Capabilities ### Complete EDA Workflow 1. **Project Setup & Validation** - File integrity and IPC availability 2. **AI Analysis** - Component suggestions and design rule recommendations 3. **Placement Optimization** - Thermal-aware component positioning 4. **Automated Routing** - FreeRouting integration with optimization 5. **Design Validation** - DRC checking and compliance verification 6. **Manufacturing Prep** - Gerber, drill, and assembly file generation 7. **Final Analysis** - Quality scoring and recommendations ### Real-time Capabilities - Live board statistics and connectivity monitoring - Interactive component placement and routing - Real-time design quality scoring - Live optimization opportunity identification ## Usage Examples ```python # Complete project automation automate_complete_design("/path/to/project.kicad_pro", "rf", ["signal_integrity", "thermal"]) # Automated routing with strategy selection route_pcb_automatically("/path/to/project.kicad_pro", "aggressive") # Real-time board analysis analyze_board_real_time("/path/to/project.kicad_pro") # Outlet tester project creation create_outlet_tester_complete("/path/to/new_project.kicad_pro", "gfci", ["voltage_display", "gfci_test"]) ``` This update establishes the foundation for Claude Code to provide complete EDA project automation, from initial design through production-ready manufacturing files, with real-time KiCad integration and automated routing. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
255 lines
8.1 KiB
Python
255 lines
8.1 KiB
Python
"""
|
|
MCP server creation and configuration.
|
|
"""
|
|
|
|
import atexit
|
|
from collections.abc import Callable
|
|
import functools
|
|
import logging
|
|
import os
|
|
import signal
|
|
|
|
from fastmcp import FastMCP
|
|
|
|
# Import context management
|
|
from kicad_mcp.context import kicad_lifespan
|
|
from kicad_mcp.prompts.bom_prompts import register_bom_prompts
|
|
from kicad_mcp.prompts.drc_prompt import register_drc_prompts
|
|
from kicad_mcp.prompts.pattern_prompts import register_pattern_prompts
|
|
|
|
# Import prompt handlers
|
|
from kicad_mcp.prompts.templates import register_prompts
|
|
from kicad_mcp.resources.bom_resources import register_bom_resources
|
|
from kicad_mcp.resources.drc_resources import register_drc_resources
|
|
from kicad_mcp.resources.files import register_file_resources
|
|
from kicad_mcp.resources.netlist_resources import register_netlist_resources
|
|
from kicad_mcp.resources.pattern_resources import register_pattern_resources
|
|
|
|
# Import resource handlers
|
|
from kicad_mcp.resources.projects import register_project_resources
|
|
from kicad_mcp.tools.advanced_drc_tools import register_advanced_drc_tools
|
|
from kicad_mcp.tools.ai_tools import register_ai_tools
|
|
from kicad_mcp.tools.analysis_tools import register_analysis_tools
|
|
from kicad_mcp.tools.bom_tools import register_bom_tools
|
|
from kicad_mcp.tools.drc_tools import register_drc_tools
|
|
from kicad_mcp.tools.export_tools import register_export_tools
|
|
from kicad_mcp.tools.layer_tools import register_layer_tools
|
|
from kicad_mcp.tools.model3d_tools import register_model3d_tools
|
|
from kicad_mcp.tools.netlist_tools import register_netlist_tools
|
|
from kicad_mcp.tools.pattern_tools import register_pattern_tools
|
|
|
|
# Import tool handlers
|
|
from kicad_mcp.tools.project_tools import register_project_tools
|
|
from kicad_mcp.tools.routing_tools import register_routing_tools
|
|
from kicad_mcp.tools.project_automation import register_project_automation_tools
|
|
from kicad_mcp.tools.symbol_tools import register_symbol_tools
|
|
|
|
# Track cleanup handlers
|
|
cleanup_handlers = []
|
|
|
|
# Flag to track whether we're already in shutdown process
|
|
_shutting_down = False
|
|
|
|
# Store server instance for clean shutdown
|
|
_server_instance = None
|
|
|
|
|
|
def add_cleanup_handler(handler: Callable) -> None:
|
|
"""Register a function to be called during cleanup.
|
|
|
|
Args:
|
|
handler: Function to call during cleanup
|
|
"""
|
|
cleanup_handlers.append(handler)
|
|
|
|
|
|
def run_cleanup_handlers() -> None:
|
|
"""Run all registered cleanup handlers."""
|
|
logging.info("Running cleanup handlers...")
|
|
|
|
global _shutting_down
|
|
|
|
# Prevent running cleanup handlers multiple times
|
|
if _shutting_down:
|
|
return
|
|
|
|
_shutting_down = True
|
|
logging.info("Running cleanup handlers...")
|
|
|
|
for handler in cleanup_handlers:
|
|
try:
|
|
handler()
|
|
logging.info(f"Cleanup handler {handler.__name__} completed successfully")
|
|
except Exception as e:
|
|
logging.error(f"Error in cleanup handler {handler.__name__}: {str(e)}", exc_info=True)
|
|
|
|
|
|
def shutdown_server():
|
|
"""Properly shutdown the server if it exists."""
|
|
global _server_instance
|
|
|
|
if _server_instance:
|
|
try:
|
|
logging.info("Shutting down KiCad MCP server")
|
|
_server_instance = None
|
|
logging.info("KiCad MCP server shutdown complete")
|
|
except Exception as e:
|
|
logging.error(f"Error shutting down server: {str(e)}", exc_info=True)
|
|
|
|
|
|
def register_signal_handlers(server: FastMCP) -> None:
|
|
"""Register handlers for system signals to ensure clean shutdown.
|
|
|
|
Args:
|
|
server: The FastMCP server instance
|
|
"""
|
|
|
|
def handle_exit_signal(signum, frame):
|
|
logging.info(f"Received signal {signum}, initiating shutdown...")
|
|
|
|
# Run cleanup first
|
|
run_cleanup_handlers()
|
|
|
|
# Then shutdown server
|
|
shutdown_server()
|
|
|
|
# Exit without waiting for stdio processes which might be blocking
|
|
os._exit(0)
|
|
|
|
# Register for common termination signals
|
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
try:
|
|
signal.signal(sig, handle_exit_signal)
|
|
logging.info(f"Registered handler for signal {sig}")
|
|
except (ValueError, AttributeError) as e:
|
|
# Some signals may not be available on all platforms
|
|
logging.error(f"Could not register handler for signal {sig}: {str(e)}")
|
|
|
|
|
|
def create_server() -> FastMCP:
|
|
"""Create and configure the KiCad MCP server."""
|
|
logging.info("Initializing KiCad MCP server")
|
|
|
|
# Try to set up KiCad Python path - Removed
|
|
# kicad_modules_available = setup_kicad_python_path()
|
|
kicad_modules_available = False # Set to False as we removed the setup logic
|
|
|
|
# if kicad_modules_available:
|
|
# print("KiCad Python modules successfully configured")
|
|
# else:
|
|
# Always print this now, as we rely on CLI
|
|
logging.info(
|
|
"KiCad Python module setup removed; relying on kicad-cli for external operations."
|
|
)
|
|
|
|
# Build a lifespan callable with the kwarg baked in (FastMCP 2.x dropped lifespan_kwargs)
|
|
lifespan_factory = functools.partial(
|
|
kicad_lifespan, kicad_modules_available=kicad_modules_available
|
|
)
|
|
|
|
# Initialize FastMCP server
|
|
mcp = FastMCP("KiCad", lifespan=lifespan_factory)
|
|
logging.info("Created FastMCP server instance with lifespan management")
|
|
|
|
# Register resources
|
|
logging.info("Registering resources...")
|
|
register_project_resources(mcp)
|
|
register_file_resources(mcp)
|
|
register_drc_resources(mcp)
|
|
register_bom_resources(mcp)
|
|
register_netlist_resources(mcp)
|
|
register_pattern_resources(mcp)
|
|
|
|
# Register tools
|
|
logging.info("Registering tools...")
|
|
register_project_tools(mcp)
|
|
register_analysis_tools(mcp)
|
|
register_export_tools(mcp)
|
|
register_drc_tools(mcp)
|
|
register_bom_tools(mcp)
|
|
register_netlist_tools(mcp)
|
|
register_pattern_tools(mcp)
|
|
register_model3d_tools(mcp)
|
|
register_advanced_drc_tools(mcp)
|
|
register_symbol_tools(mcp)
|
|
register_layer_tools(mcp)
|
|
register_ai_tools(mcp)
|
|
register_routing_tools(mcp)
|
|
register_project_automation_tools(mcp)
|
|
|
|
# Register prompts
|
|
logging.info("Registering prompts...")
|
|
register_prompts(mcp)
|
|
register_drc_prompts(mcp)
|
|
register_bom_prompts(mcp)
|
|
register_pattern_prompts(mcp)
|
|
|
|
# Register signal handlers and cleanup
|
|
register_signal_handlers(mcp)
|
|
atexit.register(run_cleanup_handlers)
|
|
|
|
# Add specific cleanup handlers
|
|
add_cleanup_handler(lambda: logging.info("KiCad MCP server shutdown complete"))
|
|
|
|
# Add temp directory cleanup
|
|
def cleanup_temp_dirs():
|
|
"""Clean up any temporary directories created by the server."""
|
|
import shutil
|
|
|
|
from kicad_mcp.utils.temp_dir_manager import get_temp_dirs
|
|
|
|
temp_dirs = get_temp_dirs()
|
|
logging.info(f"Cleaning up {len(temp_dirs)} temporary directories")
|
|
|
|
for temp_dir in temp_dirs:
|
|
try:
|
|
if os.path.exists(temp_dir):
|
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
logging.info(f"Removed temporary directory: {temp_dir}")
|
|
except Exception as e:
|
|
logging.error(f"Error cleaning up temporary directory {temp_dir}: {str(e)}")
|
|
|
|
add_cleanup_handler(cleanup_temp_dirs)
|
|
|
|
logging.info("Server initialization complete")
|
|
return mcp
|
|
|
|
|
|
def setup_signal_handlers() -> None:
|
|
"""Setup signal handlers for graceful shutdown."""
|
|
# Signal handlers are set up in register_signal_handlers
|
|
pass
|
|
|
|
|
|
def cleanup_handler() -> None:
|
|
"""Handle cleanup during shutdown."""
|
|
run_cleanup_handlers()
|
|
|
|
|
|
def setup_logging() -> None:
|
|
"""Configure logging for the server."""
|
|
logging.basicConfig(
|
|
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
"""Start the KiCad MCP server (blocking)."""
|
|
setup_logging()
|
|
logging.info("Starting KiCad MCP server...")
|
|
|
|
server = create_server()
|
|
|
|
try:
|
|
server.run() # FastMCP manages its own event loop
|
|
except KeyboardInterrupt:
|
|
logging.info("Server interrupted by user")
|
|
except Exception as e:
|
|
logging.error(f"Server error: {e}")
|
|
finally:
|
|
logging.info("Server shutdown complete")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|