Skip to content

Commit 5b9d237

Browse files
committed
feat: Fix tools, improve stability, and update docs
1 parent 44b0bda commit 5b9d237

File tree

18 files changed

+328
-611
lines changed

18 files changed

+328
-611
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
venv/
33
env/
44
ENV/
5+
.venv/
56

67
# Environment files
78
.env

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,6 @@ KICAD_SEARCH_PATHS=~/pcb,~/Electronics,~/Projects/KiCad
7171
Once the environment is set up, you can run the server:
7272

7373
```bash
74-
# Run in development mode
75-
python -m mcp.dev main.py
76-
7774
# Or run directly
7875
python main.py
7976
```

docs/development.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ This guide provides detailed information for developers who want to modify or ex
1414
pip install -r requirements.txt
1515
```
1616

17-
2. **Run in development mode**:
17+
2. **Run the server**:
1818
```bash
19-
# Run with development server for better debugging
20-
python -m mcp.dev main.py
19+
python main.py
2120
```
2221

2322
3. **Use the MCP Inspector** for debugging:
@@ -240,11 +239,8 @@ To run tests:
240239
# Run all tests
241240
pytest
242241

243-
# Run specific test file
244-
pytest tests/test_resources.py
245-
246-
# Run with verbose output
247-
pytest -v
242+
# Run specific tests:
243+
pytest tests/test_tools.py::test_run_drc_check
248244
```
249245

250246
## Debugging

docs/troubleshooting.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,7 @@ This guide helps you troubleshoot common issues with the KiCad MCP Server.
175175
176176
To diagnose issues, check the server logs:
177177
178-
1. **Development Mode Logs**
179-
- When running in development mode with `python -m mcp.dev main.py`, logs appear in the console
180-
181-
2. **Claude Desktop Logs (macOS)**
178+
1. **Claude Desktop Logs (macOS)**
182179
- Server logs:
183180
```bash
184181
tail -n 20 -F ~/Library/Logs/Claude/mcp-server-kicad.log
@@ -188,7 +185,7 @@ To diagnose issues, check the server logs:
188185
tail -n 20 -F ~/Library/Logs/Claude/mcp.log
189186
```
190187
191-
3. **Claude Desktop Logs (Windows)**
188+
2. **Claude Desktop Logs (Windows)**
192189
- Check logs in:
193190
```
194191
%APPDATA%\Claude\Logs\
@@ -264,17 +261,12 @@ To diagnose issues, check the server logs:
264261

265262
If you're still experiencing problems:
266263
267-
1. Try running the server in development mode for more detailed output:
268-
```bash
269-
python -m mcp.dev main.py
270-
```
271-
272-
2. Use the MCP Inspector for direct server testing:
264+
1. Use the MCP Inspector for direct server testing:
273265
```bash
274266
npx @modelcontextprotocol/inspector uv --directory . run main.py
275267
```
276268
277-
3. Open an issue on GitHub with:
269+
2. Open an issue on GitHub with:
278270
- A clear description of the problem
279271
- Steps to reproduce
280272
- Error messages or logs

kicad_mcp/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
KICAD_USER_DIR = os.path.expanduser("~/Documents/KiCad")
1717
KICAD_APP_PATH = r"C:\Program Files\KiCad"
1818
elif system == "Linux":
19-
KICAD_USER_DIR = os.path.expanduser("~/kicad")
19+
KICAD_USER_DIR = os.path.expanduser("~/KiCad")
2020
KICAD_APP_PATH = "/usr/share/kicad"
2121
else:
2222
# Default to macOS paths if system is unknown

kicad_mcp/context.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
from contextlib import asynccontextmanager
55
from dataclasses import dataclass
66
from typing import AsyncIterator, Dict, Any
7+
import logging # Import logging
8+
import os # Added for PID
79

810
from mcp.server.fastmcp import FastMCP
911

10-
from kicad_mcp.utils.python_path import setup_kicad_python_path
12+
# Get PID for logging
13+
# _PID = os.getpid()
1114

1215
@dataclass
1316
class KiCadAppContext:
@@ -18,7 +21,7 @@ class KiCadAppContext:
1821
cache: Dict[str, Any]
1922

2023
@asynccontextmanager
21-
async def kicad_lifespan(server: FastMCP) -> AsyncIterator[KiCadAppContext]:
24+
async def kicad_lifespan(server: FastMCP, kicad_modules_available: bool = False) -> AsyncIterator[KiCadAppContext]:
2225
"""Manage KiCad MCP server lifecycle with type-safe context.
2326
2427
This function handles:
@@ -28,58 +31,55 @@ async def kicad_lifespan(server: FastMCP) -> AsyncIterator[KiCadAppContext]:
2831
2932
Args:
3033
server: The FastMCP server instance
34+
kicad_modules_available: Flag indicating if Python modules were found (passed from create_server)
3135
3236
Yields:
3337
KiCadAppContext: A typed context object shared across all handlers
3438
"""
35-
print("Starting KiCad MCP server initialization")
39+
logging.info(f"Starting KiCad MCP server initialization")
3640

37-
# Initialize resources on startup
38-
print("Setting up KiCad Python modules")
39-
kicad_modules_available = setup_kicad_python_path()
40-
print(f"KiCad Python modules available: {kicad_modules_available}")
41+
# Resources initialization - Python path setup removed
42+
# print("Setting up KiCad Python modules")
43+
# kicad_modules_available = setup_kicad_python_path() # Now passed as arg
44+
logging.info(f"KiCad Python module availability: {kicad_modules_available} (Setup logic removed)")
4145

4246
# Create in-memory cache for expensive operations
4347
cache: Dict[str, Any] = {}
4448

4549
# Initialize any other resources that need cleanup later
46-
created_temp_dirs = []
50+
created_temp_dirs = [] # Assuming this is managed elsewhere or not needed for now
4751

4852
try:
49-
# Import any KiCad modules that should be preloaded
50-
if kicad_modules_available:
51-
try:
52-
print("Preloading KiCad Python modules")
53-
54-
# Core PCB module used in multiple tools
55-
import pcbnew
56-
print(f"Successfully preloaded pcbnew module: {getattr(pcbnew, 'GetBuildVersion', lambda: 'unknown')()}")
57-
cache["pcbnew_version"] = getattr(pcbnew, "GetBuildVersion", lambda: "unknown")()
58-
except ImportError as e:
59-
print(f"Failed to preload some KiCad modules: {str(e)}")
53+
# --- Removed Python module preloading section ---
54+
# if kicad_modules_available:
55+
# try:
56+
# print("Preloading KiCad Python modules")
57+
# ...
58+
# except ImportError as e:
59+
# print(f"Failed to preload some KiCad modules: {str(e)}")
6060

6161
# Yield the context to the server - server runs during this time
62-
print("KiCad MCP server initialization complete")
62+
logging.info(f"KiCad MCP server initialization complete")
6363
yield KiCadAppContext(
64-
kicad_modules_available=kicad_modules_available,
64+
kicad_modules_available=kicad_modules_available, # Pass the flag through
6565
cache=cache
6666
)
6767
finally:
6868
# Clean up resources when server shuts down
69-
print("Shutting down KiCad MCP server")
69+
logging.info(f"Shutting down KiCad MCP server")
7070

7171
# Clear the cache
7272
if cache:
73-
print(f"Clearing cache with {len(cache)} entries")
73+
logging.info(f"Clearing cache with {len(cache)} entries")
7474
cache.clear()
7575

7676
# Clean up any temporary directories
7777
import shutil
7878
for temp_dir in created_temp_dirs:
7979
try:
80-
print(f"Removing temporary directory: {temp_dir}")
80+
logging.info(f"Removing temporary directory: {temp_dir}")
8181
shutil.rmtree(temp_dir, ignore_errors=True)
8282
except Exception as e:
83-
print(f"Error cleaning up temporary directory {temp_dir}: {str(e)}")
83+
logging.error(f"Error cleaning up temporary directory {temp_dir}: {str(e)}")
8484

85-
print("KiCad MCP server shutdown complete")
85+
logging.info(f"KiCad MCP server shutdown complete")

kicad_mcp/resources/projects.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,6 @@ def register_project_resources(mcp: FastMCP) -> None:
1515
mcp: The FastMCP server instance
1616
"""
1717

18-
@mcp.resource("kicad://projects")
19-
def list_projects_resource() -> str:
20-
"""List all KiCad projects as a formatted resource."""
21-
projects = find_kicad_projects()
22-
23-
if not projects:
24-
return "No KiCad projects found in your Documents/KiCad directory."
25-
26-
result = "# KiCad Projects\n\n"
27-
for project in sorted(projects, key=lambda p: p["modified"], reverse=True):
28-
result += f"## {project['name']}\n"
29-
result += f"- **Path**: {project['path']}\n"
30-
result += f"- **Last Modified**: {os.path.getmtime(project['path'])}\n\n"
31-
32-
return result
33-
3418
@mcp.resource("kicad://project/{project_path}")
3519
def get_project_details(project_path: str) -> str:
3620
"""Get details about a specific KiCad project."""

kicad_mcp/server.py

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import atexit
55
import os
66
import signal
7+
import logging
78
from typing import Callable
89
from mcp.server.fastmcp import FastMCP
910

@@ -31,9 +32,6 @@
3132
from kicad_mcp.prompts.bom_prompts import register_bom_prompts
3233
from kicad_mcp.prompts.pattern_prompts import register_pattern_prompts
3334

34-
# Import utils
35-
from kicad_mcp.utils.python_path import setup_kicad_python_path
36-
3735
# Import context management
3836
from kicad_mcp.context import kicad_lifespan
3937

@@ -56,7 +54,7 @@ def add_cleanup_handler(handler: Callable) -> None:
5654

5755
def run_cleanup_handlers() -> None:
5856
"""Run all registered cleanup handlers."""
59-
print("Running cleanup handlers...")
57+
logging.info(f"Running cleanup handlers...")
6058

6159
global _shutting_down
6260

@@ -65,28 +63,26 @@ def run_cleanup_handlers() -> None:
6563
return
6664

6765
_shutting_down = True
68-
print("Running cleanup handlers...")
66+
logging.info(f"Running cleanup handlers...")
6967

7068
for handler in cleanup_handlers:
7169
try:
7270
handler()
73-
print(f"Cleanup handler {handler.__name__} completed successfully")
71+
logging.info(f"Cleanup handler {handler.__name__} completed successfully")
7472
except Exception as e:
75-
print(f"Error in cleanup handler {handler.__name__}: {str(e)}", exc_info=True)
73+
logging.error(f"Error in cleanup handler {handler.__name__}: {str(e)}", exc_info=True)
7674

7775
def shutdown_server():
7876
"""Properly shutdown the server if it exists."""
7977
global _server_instance
8078

8179
if _server_instance:
8280
try:
83-
print("Shutting down KiCad MCP server")
84-
# The server should handle its own shutdown through its lifespan context
85-
# This is mostly a placeholder for any additional server shutdown code
81+
logging.info(f"Shutting down KiCad MCP server")
8682
_server_instance = None
87-
print("KiCad MCP server shutdown complete")
83+
logging.info(f"KiCad MCP server shutdown complete")
8884
except Exception as e:
89-
print(f"Error shutting down server: {str(e)}", exc_info=True)
85+
logging.error(f"Error shutting down server: {str(e)}", exc_info=True)
9086

9187

9288
def register_signal_handlers(server: FastMCP) -> None:
@@ -96,7 +92,7 @@ def register_signal_handlers(server: FastMCP) -> None:
9692
server: The FastMCP server instance
9793
"""
9894
def handle_exit_signal(signum, frame):
99-
print(f"Received signal {signum}, initiating shutdown...")
95+
logging.info(f"Received signal {signum}, initiating shutdown...")
10096

10197
# Run cleanup first
10298
run_cleanup_handlers()
@@ -111,30 +107,33 @@ def handle_exit_signal(signum, frame):
111107
for sig in (signal.SIGINT, signal.SIGTERM):
112108
try:
113109
signal.signal(sig, handle_exit_signal)
114-
print(f"Registered handler for signal {sig}")
110+
logging.info(f"Registered handler for signal {sig}")
115111
except (ValueError, AttributeError) as e:
116112
# Some signals may not be available on all platforms
117-
print(f"Could not register handler for signal {sig}: {str(e)}")
113+
logging.error(f"Could not register handler for signal {sig}: {str(e)}")
118114

119115

120116
def create_server() -> FastMCP:
121117
"""Create and configure the KiCad MCP server."""
122-
print("Initializing KiCad MCP server")
118+
logging.info(f"Initializing KiCad MCP server")
123119

124-
# Try to set up KiCad Python path
125-
kicad_modules_available = setup_kicad_python_path()
120+
# Try to set up KiCad Python path - Removed
121+
# kicad_modules_available = setup_kicad_python_path()
122+
kicad_modules_available = False # Set to False as we removed the setup logic
126123

127-
if kicad_modules_available:
128-
print("KiCad Python modules successfully configured")
129-
else:
130-
print("KiCad Python modules not available - some features will be disabled")
124+
# if kicad_modules_available:
125+
# print("KiCad Python modules successfully configured")
126+
# else:
127+
# Always print this now, as we rely on CLI
128+
logging.info(f"KiCad Python module setup removed; relying on kicad-cli for external operations.")
131129

132130
# Initialize FastMCP server
133-
mcp = FastMCP("KiCad", lifespan=kicad_lifespan)
134-
print("Created FastMCP server instance with lifespan management")
131+
# Pass the availability flag (always False now) to the lifespan context
132+
mcp = FastMCP("KiCad", lifespan=kicad_lifespan, lifespan_kwargs={"kicad_modules_available": kicad_modules_available})
133+
logging.info(f"Created FastMCP server instance with lifespan management")
135134

136135
# Register resources
137-
print("Registering resources...")
136+
logging.info(f"Registering resources...")
138137
register_project_resources(mcp)
139138
register_file_resources(mcp)
140139
register_drc_resources(mcp)
@@ -143,7 +142,7 @@ def create_server() -> FastMCP:
143142
register_pattern_resources(mcp)
144143

145144
# Register tools
146-
print("Registering tools...")
145+
logging.info(f"Registering tools...")
147146
register_project_tools(mcp)
148147
register_analysis_tools(mcp)
149148
register_export_tools(mcp)
@@ -153,7 +152,7 @@ def create_server() -> FastMCP:
153152
register_pattern_tools(mcp)
154153

155154
# Register prompts
156-
print("Registering prompts...")
155+
logging.info(f"Registering prompts...")
157156
register_prompts(mcp)
158157
register_drc_prompts(mcp)
159158
register_bom_prompts(mcp)
@@ -164,7 +163,7 @@ def create_server() -> FastMCP:
164163
atexit.register(run_cleanup_handlers)
165164

166165
# Add specific cleanup handlers
167-
add_cleanup_handler(lambda: print("KiCad MCP server shutdown complete"))
166+
add_cleanup_handler(lambda: logging.info(f"KiCad MCP server shutdown complete"))
168167

169168
# Add temp directory cleanup
170169
def cleanup_temp_dirs():
@@ -173,17 +172,17 @@ def cleanup_temp_dirs():
173172
from kicad_mcp.utils.temp_dir_manager import get_temp_dirs
174173

175174
temp_dirs = get_temp_dirs()
176-
print(f"Cleaning up {len(temp_dirs)} temporary directories")
175+
logging.info(f"Cleaning up {len(temp_dirs)} temporary directories")
177176

178177
for temp_dir in temp_dirs:
179178
try:
180179
if os.path.exists(temp_dir):
181180
shutil.rmtree(temp_dir, ignore_errors=True)
182-
print(f"Removed temporary directory: {temp_dir}")
181+
logging.info(f"Removed temporary directory: {temp_dir}")
183182
except Exception as e:
184-
print(f"Error cleaning up temporary directory {temp_dir}: {str(e)}")
183+
logging.error(f"Error cleaning up temporary directory {temp_dir}: {str(e)}")
185184

186185
add_cleanup_handler(cleanup_temp_dirs)
187186

188-
print("Server initialization complete")
187+
logging.info(f"Server initialization complete")
189188
return mcp

kicad_mcp/tools/bom_tools.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@ async def export_bom_with_python(schematic_file: str, output_dir: str, project_n
584584
# Try to import KiCad Python modules
585585
# This is a placeholder since exporting BOMs from schematic files
586586
# is complex and KiCad's API for this is not well-documented
587-
import pcbnew
587+
import kicad
588+
import kicad.pcbnew
588589

589590
# For now, return a message indicating this method is not implemented yet
590591
print("BOM export with Python modules not fully implemented")

0 commit comments

Comments
 (0)