kicad-mcp/main.py
Lauri Gates 9bea0f4ebf feat: migrate to FastMCP 2.0 framework with modern Python packaging
- Update imports from 'mcp.server.fastmcp' to 'fastmcp' per FastMCP 2.0 migration
- Add pyproject.toml for modern Python packaging with hatchling build backend
- Implement missing server lifecycle functions: main(), setup_logging(), cleanup_handler()
- Add async main() entry point for proper server execution
- Update main.py to use async server execution pattern
- Add fastmcp>=0.1.0 dependency to replace legacy mcp server imports

This establishes the foundation for all subsequent feature additions and ensures
compatibility with modern MCP clients and development workflows.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-17 20:13:01 +03:00

80 lines
3.5 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 sys
import logging # Import logging module
# Must import config BEFORE env potentially overrides it via os.environ
from kicad_mcp.config import KICAD_USER_DIR, ADDITIONAL_SEARCH_PATHS
from kicad_mcp.server import main as server_main
from kicad_mcp.utils.env import load_dotenv
# --- Setup Logging ---
log_file = os.path.join(os.path.dirname(__file__), 'kicad-mcp.log')
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - [PID:%(process)d] - %(message)s',
handlers=[
logging.FileHandler(log_file, mode='w'), # Use 'w' to overwrite log on each start
# logging.StreamHandler() # Optionally keep logging to console if needed
]
)
# ---------------------
logging.info("--- Server Starting --- ")
logging.info(f"Initial KICAD_USER_DIR from config.py: {KICAD_USER_DIR}")
logging.info(f"Initial ADDITIONAL_SEARCH_PATHS from config.py: {ADDITIONAL_SEARCH_PATHS}")
# Get PID for logging (already used by basicConfig)
_PID = os.getpid()
# Load environment variables from .env file if present
# This attempts to update os.environ
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
logging.info(f"Attempting to load .env file from: {dotenv_path}")
found_dotenv = load_dotenv() # Assuming this returns True/False or similar
logging.info(f".env file found and loaded: {found_dotenv}")
# Log effective values AFTER load_dotenv attempt
# Note: The config values might not automatically re-read from os.environ
# depending on how config.py is written. Let's check os.environ directly.
effective_user_dir = os.getenv('KICAD_USER_DIR')
effective_search_paths = os.getenv('KICAD_SEARCH_PATHS')
logging.info(f"os.environ['KICAD_USER_DIR'] after load_dotenv: {effective_user_dir}")
logging.info(f"os.environ['KICAD_SEARCH_PATHS'] after load_dotenv: {effective_search_paths}")
# Re-log the values imported from config.py to see if they reflect os.environ changes
# (This depends on config.py using os.getenv internally AFTER load_dotenv runs)
try:
from kicad_mcp import config
import importlib
importlib.reload(config) # Attempt to force re-reading config
logging.info(f"Effective KICAD_USER_DIR from config.py after reload: {config.KICAD_USER_DIR}")
logging.info(f"Effective ADDITIONAL_SEARCH_PATHS from config.py after reload: {config.ADDITIONAL_SEARCH_PATHS}")
except Exception as e:
logging.error(f"Could not reload config: {e}")
logging.info(f"Using potentially stale KICAD_USER_DIR from initial import: {KICAD_USER_DIR}")
logging.info(f"Using potentially stale ADDITIONAL_SEARCH_PATHS from initial import: {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
import asyncio
asyncio.run(server_main())
except Exception as e:
logging.exception(f"Unhandled exception in main") # Log exception details
raise