Skip to content

Commit 0bbb78b

Browse files
authored
Merge branch 'main' into pr-3-security-input-validation
2 parents bd08a47 + 9dbea13 commit 0bbb78b

File tree

15 files changed

+1799
-42
lines changed

15 files changed

+1799
-42
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
---
2-
name: Bug Report
3-
about: Report a bug or issue with the KiCad MCP Server
4-
title: "[BUG] "
5-
labels: bug
6-
assignees: ''
7-
---
8-
91
## Bug Description
102
<!-- A clear and concise description of the bug -->
113

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,4 @@ jobs:
120120
uses: actions/upload-artifact@v4
121121
with:
122122
name: dist
123-
path: dist/
123+
path: dist/

.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ __pycache__/
1818
dist/
1919
build/
2020
*.egg-info/
21+
*.egg
22+
*.whl
23+
24+
# PyPI
25+
.pypirc
2126

2227
# Unit test / coverage reports
2328
htmlcov/
@@ -28,6 +33,7 @@ htmlcov/
2833
nosetests.xml
2934
coverage.xml
3035
*.cover
36+
.pytest_cache/
3137

3238
# Logs
3339
logs/
@@ -42,3 +48,25 @@ logs/
4248

4349
# MCP specific
4450
~/.kicad_mcp/drc_history/
51+
52+
# UV and modern Python tooling
53+
uv.lock
54+
.uv-cache/
55+
.ruff_cache/
56+
57+
# Pre-commit
58+
.pre-commit-config.yaml
59+
60+
# KiCad backup files
61+
*-backups/
62+
fp-info-cache
63+
*.bak
64+
*.backup
65+
*.kicad_pcb-bak
66+
*.kicad_sch-bak
67+
*.kicad_pro-bak
68+
*.kicad_prl
69+
*.kicad_prl-bak
70+
*.kicad_sch.lck
71+
*.kicad_pcb.lck
72+
*.kicad_pro.lck

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10

MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include README.md
2+
include LICENSE
3+
include requirements.txt
4+
include .env.example
5+
recursive-include kicad_mcp *.py
6+
recursive-include docs *.md

Makefile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.PHONY: help install test lint format clean build run
2+
3+
help:
4+
@echo "Available commands:"
5+
@echo " install Install dependencies"
6+
@echo " test Run tests"
7+
@echo " lint Run linting"
8+
@echo " format Format code"
9+
@echo " clean Clean build artifacts"
10+
@echo " build Build package"
11+
12+
install:
13+
uv sync --group dev
14+
15+
test:
16+
uv run python -m pytest tests/ -v
17+
18+
lint:
19+
uv run ruff check kicad_mcp/ tests/
20+
uv run mypy kicad_mcp/
21+
22+
format:
23+
uv run ruff format kicad_mcp/ tests/
24+
25+
clean:
26+
rm -rf dist/
27+
rm -rf build/
28+
rm -rf *.egg-info/
29+
rm -rf .pytest_cache/
30+
rm -rf htmlcov/
31+
rm -f coverage.xml
32+
find . -type d -name __pycache__ -delete
33+
find . -type f -name "*.pyc" -delete
34+
35+
build:
36+
uv build
37+
38+
run:
39+
uv run python main.py

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This guide will help you set up a Model Context Protocol (MCP) server for KiCad.
2222
- macOS, Windows, or Linux
2323
- Python 3.10 or higher
2424
- KiCad 9.0 or higher
25+
- uv 0.8.0 or higher
2526
- Claude Desktop (or another MCP client)
2627

2728
## Installation Steps
@@ -32,14 +33,15 @@ First, let's install dependencies and set up our environment:
3233

3334
```bash
3435
# Clone the repository
35-
git clone https://github.com/lamaalrajih/kicad-mcp.git .
36+
git clone https://github.com/lamaalrajih/kicad-mcp.git
37+
cd kicad-mcp
3638

37-
# Create a virtual environment and activate it
38-
python3 -m venv venv
39-
source venv/bin/activate # On Windows: venv\Scripts\activate
39+
# Install dependencies – `uv` will create a `.venv/` folder automatically
40+
# (Install `uv` first: `brew install uv` on macOS or `pipx install uv`)
41+
make install
4042

41-
# Install the MCP SDK and other dependencies
42-
pip install -r requirements.txt
43+
# Optional: activate the environment for manual commands
44+
source .venv/bin/activate
4345
```
4446

4547
### 2. Configure Your Environment
@@ -89,7 +91,7 @@ vim ~/Library/Application\ Support/Claude/claude_desktop_config.json
8991
{
9092
"mcpServers": {
9193
"kicad": {
92-
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
94+
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/.venv/bin/python",
9395
"args": [
9496
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py"
9597
]

kicad_mcp/__init__.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,27 @@
33
44
A Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files.
55
"""
6+
from .server import *
7+
from .config import *
8+
from .context import *
69

7-
__version__ = "0.2.0"
10+
__version__ = "0.1.0"
11+
__author__ = "Lama Al Rajih"
12+
__description__ = "Model Context Protocol server for KiCad on Mac, Windows, and Linux"
13+
14+
__all__ = [
15+
# Package metadata
16+
"__version__",
17+
"__author__",
18+
"__description__",
19+
20+
# Server creation / shutdown helpers
21+
"create_server",
22+
"add_cleanup_handler",
23+
"run_cleanup_handlers",
24+
"shutdown_server",
25+
26+
# Lifespan / context helpers
27+
"kicad_lifespan",
28+
"KiCadAppContext",
29+
]

kicad_mcp/server.py

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
import os
66
import signal
77
import logging
8+
import functools
89
from typing import Callable
9-
from mcp.server.fastmcp import FastMCP
10+
from fastmcp import FastMCP
1011

1112
# Import resource handlers
1213
from kicad_mcp.resources.projects import register_project_resources
@@ -127,9 +128,11 @@ def create_server() -> FastMCP:
127128
# Always print this now, as we rely on CLI
128129
logging.info(f"KiCad Python module setup removed; relying on kicad-cli for external operations.")
129130

131+
# Build a lifespan callable with the kwarg baked in (FastMCP 2.x dropped lifespan_kwargs)
132+
lifespan_factory = functools.partial(kicad_lifespan, kicad_modules_available=kicad_modules_available)
133+
130134
# Initialize FastMCP server
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})
135+
mcp = FastMCP("KiCad", lifespan=lifespan_factory)
133136
logging.info(f"Created FastMCP server instance with lifespan management")
134137

135138
# Register resources
@@ -186,3 +189,43 @@ def cleanup_temp_dirs():
186189

187190
logging.info(f"Server initialization complete")
188191
return mcp
192+
193+
194+
def setup_signal_handlers() -> None:
195+
"""Setup signal handlers for graceful shutdown."""
196+
# Signal handlers are set up in register_signal_handlers
197+
pass
198+
199+
200+
def cleanup_handler() -> None:
201+
"""Handle cleanup during shutdown."""
202+
run_cleanup_handlers()
203+
204+
205+
def setup_logging() -> None:
206+
"""Configure logging for the server."""
207+
logging.basicConfig(
208+
level=logging.INFO,
209+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
210+
)
211+
212+
213+
def main() -> None:
214+
"""Start the KiCad MCP server (blocking)."""
215+
setup_logging()
216+
logging.info("Starting KiCad MCP server...")
217+
218+
server = create_server()
219+
220+
try:
221+
server.run() # FastMCP manages its own event loop
222+
except KeyboardInterrupt:
223+
logging.info("Server interrupted by user")
224+
except Exception as e:
225+
logging.error(f"Server error: {e}")
226+
finally:
227+
logging.info("Server shutdown complete")
228+
229+
230+
if __name__ == "__main__":
231+
main()

kicad_mcp/tools/export_tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def register_export_tools(mcp: FastMCP) -> None:
2020
"""
2121

2222
@mcp.tool()
23-
async def generate_pcb_thumbnail(project_path: str, ctx: Context) -> Optional[Image]:
23+
async def generate_pcb_thumbnail(project_path: str, ctx: Context):
2424
"""Generate a thumbnail image of a KiCad PCB layout using kicad-cli.
2525
2626
Args:
@@ -88,14 +88,14 @@ async def generate_pcb_thumbnail(project_path: str, ctx: Context) -> Optional[Im
8888
return None
8989

9090
@mcp.tool()
91-
async def generate_project_thumbnail(project_path: str, ctx: Context) -> Optional[Image]:
91+
async def generate_project_thumbnail(project_path: str, ctx: Context):
9292
"""Generate a thumbnail of a KiCad project's PCB layout (Alias for generate_pcb_thumbnail)."""
9393
# This function now just calls the main CLI-based thumbnail generator
9494
print(f"generate_project_thumbnail called, redirecting to generate_pcb_thumbnail for {project_path}")
9595
return await generate_pcb_thumbnail(project_path, ctx)
9696

9797
# Helper functions for thumbnail generation
98-
async def generate_thumbnail_with_cli(pcb_file: str, ctx: Context) -> Optional[Image]:
98+
async def generate_thumbnail_with_cli(pcb_file: str, ctx: Context):
9999
"""Generate PCB thumbnail using command line tools.
100100
This is a fallback method when the kicad Python module is not available or fails.
101101

0 commit comments

Comments
 (0)