a lot
This commit is contained in:
parent
e4035bdcb1
commit
9fa890bf90
247
README.md
247
README.md
@ -1,4 +1,4 @@
|
|||||||
# KiCad MCP Server - Setup Guide
|
# KiCad MCP Server
|
||||||
|
|
||||||
This guide will help you set up a Model Context Protocol (MCP) server for KiCad on macOS, allowing you to interact with KiCad projects through Claude Desktop or other MCP-compatible clients.
|
This guide will help you set up a Model Context Protocol (MCP) server for KiCad on macOS, allowing you to interact with KiCad projects through Claude Desktop or other MCP-compatible clients.
|
||||||
|
|
||||||
@ -9,11 +9,31 @@ This guide will help you set up a Model Context Protocol (MCP) server for KiCad
|
|||||||
- Claude Desktop (or another MCP client)
|
- Claude Desktop (or another MCP client)
|
||||||
- Basic familiarity with the terminal
|
- Basic familiarity with the terminal
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
The KiCad MCP Server is organized into a modular structure for better maintainability:
|
||||||
|
|
||||||
|
```
|
||||||
|
kicad-mcp/
|
||||||
|
├── README.md # Project documentation
|
||||||
|
├── main.py # Entry point that runs the server
|
||||||
|
├── config.py # Configuration constants and settings
|
||||||
|
├── requirements.txt # Python dependencies
|
||||||
|
├── kicad_mcp/ # Main package directory
|
||||||
|
│ ├── __init__.py # Package initialization
|
||||||
|
│ ├── server.py # MCP server setup
|
||||||
|
│ ├── resources/ # Resource handlers
|
||||||
|
│ ├── tools/ # Tool handlers
|
||||||
|
│ ├── prompts/ # Prompt templates
|
||||||
|
│ └── utils/ # Utility functions
|
||||||
|
└── tests/ # Unit tests
|
||||||
|
```
|
||||||
|
|
||||||
## Installation Steps
|
## Installation Steps
|
||||||
|
|
||||||
### 1. Set Up Your Python Environment
|
### 1. Set Up Your Python Environment
|
||||||
|
|
||||||
First, let's install `uv` (a fast Python package installer) and set up our environment:
|
First, let's install `pip` and set up our environment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create a new directory for our project
|
# Create a new directory for our project
|
||||||
@ -24,34 +44,23 @@ cd ~/Projects/kicad-mcp
|
|||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
|
|
||||||
# Install the MCP SDK
|
# Install the MCP SDK and other dependencies
|
||||||
pip install "mcp[cli]"
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Save the KiCad MCP Server Script
|
### 2. Run the Server
|
||||||
|
|
||||||
Create a new file called `kicad_mcp.py` in your project directory and paste the contents of the KiCad MCP Server script.
|
Once the environment is set up, you can run the server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Make the file executable (optional, but helpful)
|
# Run in development mode
|
||||||
chmod +x kicad_mcp.py
|
python -m mcp.dev main.py
|
||||||
|
|
||||||
# Run the server in development mode
|
# Or run directly
|
||||||
python -m mcp.dev kicad_mcp.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Test Your Server
|
### 3. Configure Claude Desktop
|
||||||
|
|
||||||
Let's make sure your server works correctly before integrating it with Claude Desktop:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if the file exists and has content
|
|
||||||
cat kicad_mcp.py | head -n 5
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see the server start up and display information about the available tools and resources.
|
|
||||||
|
|
||||||
### 4. Configure Claude Desktop
|
|
||||||
|
|
||||||
Now, let's configure Claude Desktop to use our MCP server:
|
Now, let's configure Claude Desktop to use our MCP server:
|
||||||
|
|
||||||
@ -73,7 +82,7 @@ nano ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
|||||||
"kicad": {
|
"kicad": {
|
||||||
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
|
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
|
||||||
"args": [
|
"args": [
|
||||||
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/kicad_mcp.py"
|
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,24 +93,89 @@ Replace `/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp` with the actual path to your
|
|||||||
|
|
||||||
3. Save the file and exit the editor.
|
3. Save the file and exit the editor.
|
||||||
|
|
||||||
### 5. Restart Claude Desktop
|
### 4. Restart Claude Desktop
|
||||||
|
|
||||||
Close and reopen Claude Desktop to load the new configuration.
|
Close and reopen Claude Desktop to load the new configuration.
|
||||||
|
|
||||||
## Usage
|
## Understanding MCP Components
|
||||||
|
|
||||||
Once the server is properly configured, you can interact with KiCad through Claude Desktop:
|
The Model Context Protocol (MCP) defines three primary ways to provide capabilities:
|
||||||
|
|
||||||
1. Open Claude Desktop
|
### Resources vs Tools vs Prompts
|
||||||
2. Look for the tools icon (hammer symbol) in the Claude interface
|
|
||||||
3. You should see the KiCad MCP tools available in the menu
|
|
||||||
|
|
||||||
Here are some example prompts you can use:
|
**Resources** are read-only data sources that LLMs can reference:
|
||||||
|
- Similar to GET endpoints in REST APIs
|
||||||
|
- Provide data without performing significant computation
|
||||||
|
- Used when the LLM needs to read information
|
||||||
|
- Typically accessed programmatically by the client application
|
||||||
|
- Example: `kicad://projects` returns a list of all KiCad projects
|
||||||
|
|
||||||
- "What KiCad projects do I have on my Mac?"
|
**Tools** are functions that perform actions or computations:
|
||||||
- "Can you help me open my latest KiCad project?"
|
- Similar to POST/PUT endpoints in REST APIs
|
||||||
- "Extract the bill of materials from my project at [path]"
|
- Can have side effects (like opening applications or generating files)
|
||||||
- "Validate my KiCad project at [path]"
|
- Used when the LLM needs to perform actions in the world
|
||||||
|
- Typically invoked directly by the LLM (with user approval)
|
||||||
|
- Example: `open_project()` launches KiCad with a specific project
|
||||||
|
|
||||||
|
**Prompts** are reusable templates for common interactions:
|
||||||
|
- Pre-defined conversation starters or instructions
|
||||||
|
- Help users articulate common questions or tasks
|
||||||
|
- Invoked by user choice (typically from a menu)
|
||||||
|
- Example: The `debug_pcb_issues` prompt helps users troubleshoot PCB problems
|
||||||
|
|
||||||
|
## Available Features
|
||||||
|
|
||||||
|
The KiCad MCP Server provides several capabilities:
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
- `kicad://projects` - List all KiCad projects
|
||||||
|
- `kicad://project/{path}` - Get details about a specific project
|
||||||
|
- `kicad://schematic/{path}` - Extract information from a schematic file
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
- Project management tools (find projects, get structure, open in KiCad)
|
||||||
|
- Analysis tools (validate projects, generate thumbnails)
|
||||||
|
- Export tools (extract bill of materials)
|
||||||
|
|
||||||
|
### Prompts
|
||||||
|
- Create new component guide
|
||||||
|
- Debug PCB issues guide
|
||||||
|
- PCB manufacturing checklist
|
||||||
|
|
||||||
|
## Development Guide
|
||||||
|
|
||||||
|
### Adding New Features
|
||||||
|
|
||||||
|
To add new features to the KiCad MCP Server, follow these steps:
|
||||||
|
|
||||||
|
1. Identify the category for your feature (resource, tool, or prompt)
|
||||||
|
2. Add your implementation to the appropriate module
|
||||||
|
3. Register your feature in the corresponding register function
|
||||||
|
4. Test your changes with the development tools
|
||||||
|
|
||||||
|
Example for adding a new tool:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# kicad_mcp/tools/analysis_tools.py
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def new_analysis_tool(project_path: str) -> Dict[str, Any]:
|
||||||
|
"""Description of your new tool."""
|
||||||
|
# Implementation goes here
|
||||||
|
return {"result": "success"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
This project uses pytest for testing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install development dependencies
|
||||||
|
pip install pytest
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
@ -110,108 +184,33 @@ If you encounter issues:
|
|||||||
1. **Server Not Appearing in Claude Desktop:**
|
1. **Server Not Appearing in Claude Desktop:**
|
||||||
- Check your `claude_desktop_config.json` file for errors
|
- Check your `claude_desktop_config.json` file for errors
|
||||||
- Make sure the path to your project and Python interpreter is correct
|
- Make sure the path to your project and Python interpreter is correct
|
||||||
- Ensure Python can access the `mcp` package (check by running `python -c "import mcp; print(mcp.__version__)"` in your venv)
|
- Ensure Python can access the `mcp` package
|
||||||
|
|
||||||
2. **Server Errors:**
|
2. **Server Errors:**
|
||||||
- Check the terminal output when running the server in development mode
|
- Check the terminal output when running the server in development mode
|
||||||
- Make sure all required Python packages are installed
|
- Make sure all required Python packages are installed
|
||||||
- Verify that your KiCad installation is in the standard location
|
- Verify that your KiCad installation is in the standard location
|
||||||
- Run `pip install -U "mcp[cli]"` to ensure you have the latest version
|
|
||||||
|
|
||||||
3. **Permission Issues:**
|
|
||||||
- Make sure the script is executable (`chmod +x kicad_mcp.py`)
|
|
||||||
- Check if Claude Desktop has permission to run the script
|
|
||||||
- If you get permission errors, try using the full path to your Python interpreter in the configuration
|
|
||||||
|
|
||||||
## Extending the Server
|
|
||||||
|
|
||||||
The provided MCP server implements basic KiCad functionality. To extend it:
|
|
||||||
|
|
||||||
1. Add new tools using the `@mcp.tool()` decorator
|
|
||||||
2. Add new resources using the `@mcp.resource()` decorator
|
|
||||||
3. Add new prompts using the `@mcp.prompt()` decorator
|
|
||||||
|
|
||||||
The MCP SDK provides a command-line interface for development and deployment. With your virtual environment activated, you can use commands like:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test your server in development mode
|
|
||||||
python -m mcp.dev kicad_mcp.py
|
|
||||||
|
|
||||||
# Install your server for use with Claude Desktop
|
|
||||||
python -m mcp.install kicad_mcp.py --name "KiCad"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Additional Resources
|
|
||||||
|
|
||||||
- [MCP Documentation](https://modelcontextprotocol.io/introduction)
|
|
||||||
- [KiCad Python API Documentation](https://docs.kicad.org/doxygen-python/namespacepcbnew.html)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Want to contribute to the KiCad MCP Server? Here's how you can help improve this project:
|
Want to contribute to the KiCad MCP Server? Here's how you can help improve this project:
|
||||||
|
|
||||||
### Getting Started
|
1. Fork the repository
|
||||||
|
2. Create a feature branch
|
||||||
|
3. Add your changes
|
||||||
|
4. Submit a pull request
|
||||||
|
|
||||||
The project currently consists of just two files:
|
## Future Development Ideas
|
||||||
|
|
||||||
- kicad_mcp.py - The main MCP server implementation
|
Interested in contributing? Here are some ideas for future development:
|
||||||
|
|
||||||
- This setup guide
|
1. Add Design Rule Check (DRC) report generation and parsing
|
||||||
|
2. Implement 3D model visualization tools
|
||||||
|
3. Create PCB review tools with annotations
|
||||||
|
4. Add support for generating manufacturing files
|
||||||
|
5. Implement component search tools
|
||||||
|
6. Add tests!
|
||||||
|
|
||||||
If you want to make improvements:
|
## License
|
||||||
|
|
||||||
1. Set up your environment as described in the installation steps
|
This project is open source under the MIT license.
|
||||||
|
|
||||||
2. Make your changes to the kicad_mcp.py file
|
|
||||||
|
|
||||||
3. Test your changes with Claude Desktop
|
|
||||||
|
|
||||||
### How to Contribute
|
|
||||||
|
|
||||||
#### Improving the Existing Server
|
|
||||||
|
|
||||||
You can improve the existing server by:
|
|
||||||
|
|
||||||
- Adding more tools and resources for KiCad
|
|
||||||
|
|
||||||
- Fixing bugs in the current implementation
|
|
||||||
|
|
||||||
- Improving error handling and user experience
|
|
||||||
|
|
||||||
- Adding support for more KiCad features
|
|
||||||
|
|
||||||
#### Testing Your Changes
|
|
||||||
|
|
||||||
Before sharing your changes, test them thoroughly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
|
|
||||||
# Run the server in development mode to check for errors
|
|
||||||
|
|
||||||
python -m mcp.dev kicad_mcp.py
|
|
||||||
|
|
||||||
# Test your changes with Claude Desktop
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Future Development Ideas
|
|
||||||
|
|
||||||
If you're looking for ways to improve the server, consider:
|
|
||||||
|
|
||||||
1. Adding support for KiCad's Python API (`pcbnew`) for deeper integration
|
|
||||||
|
|
||||||
2. Creating more specialized tools for PCB design review
|
|
||||||
|
|
||||||
3. Adding visualization capabilities for schematics and layouts
|
|
||||||
|
|
||||||
4. Improving project organization as the codebase grows
|
|
||||||
|
|
||||||
### Best Practices
|
|
||||||
|
|
||||||
- Keep the code simple and focused
|
|
||||||
|
|
||||||
- Document your functions with clear docstrings
|
|
||||||
|
|
||||||
- Handle errors gracefully with informative messages
|
|
||||||
|
|
||||||
- Test with different KiCad project structures
|
|
||||||
|
26
config.py
26
config.py
@ -0,0 +1,26 @@
|
|||||||
|
"""
|
||||||
|
Configuration settings for the KiCad MCP server.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
# KiCad paths
|
||||||
|
KICAD_USER_DIR = os.path.expanduser("~/Documents/KiCad")
|
||||||
|
KICAD_APP_PATH = "/Applications/KiCad/KiCad.app"
|
||||||
|
|
||||||
|
# File extensions
|
||||||
|
KICAD_EXTENSIONS = {
|
||||||
|
"project": ".kicad_pro",
|
||||||
|
"pcb": ".kicad_pcb",
|
||||||
|
"schematic": ".kicad_sch",
|
||||||
|
"design_rules": ".kicad_dru",
|
||||||
|
"worksheet": ".kicad_wks",
|
||||||
|
"footprint": ".kicad_mod",
|
||||||
|
"netlist": "_netlist.net",
|
||||||
|
"kibot_config": ".kibot.yaml",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Recognized data files
|
||||||
|
DATA_EXTENSIONS = [
|
||||||
|
".csv", # BOM or other data
|
||||||
|
".pos", # Component position file
|
||||||
|
]
|
@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
KiCad MCP Server - A Model Context Protocol server for KiCad.
|
||||||
|
"""
|
||||||
|
__version__ = "0.1.0"
|
@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Prompt templates for KiCad MCP Server.
|
||||||
|
"""
|
@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
Prompt templates for KiCad interactions.
|
||||||
|
"""
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
|
||||||
|
def register_prompts(mcp: FastMCP) -> None:
|
||||||
|
"""Register prompt templates with the MCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcp: The FastMCP server instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
@mcp.prompt()
|
||||||
|
def create_new_component() -> str:
|
||||||
|
"""Prompt for creating a new KiCad component."""
|
||||||
|
prompt = """
|
||||||
|
I want to create a new component in KiCad for my PCB design. I need help with:
|
||||||
|
|
||||||
|
1. Deciding on the correct component package/footprint
|
||||||
|
2. Creating the schematic symbol
|
||||||
|
3. Connecting the schematic symbol to the footprint
|
||||||
|
4. Adding the component to my design
|
||||||
|
|
||||||
|
Please provide step-by-step instructions on how to create a new component in KiCad.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
@mcp.prompt()
|
||||||
|
def debug_pcb_issues() -> str:
|
||||||
|
"""Prompt for debugging common PCB issues."""
|
||||||
|
prompt = """
|
||||||
|
I'm having issues with my KiCad PCB design. Can you help me troubleshoot the following problems:
|
||||||
|
|
||||||
|
1. Design rule check (DRC) errors
|
||||||
|
2. Electrical rule check (ERC) errors
|
||||||
|
3. Footprint mismatches
|
||||||
|
4. Routing challenges
|
||||||
|
|
||||||
|
Please provide a systematic approach to identifying and fixing these issues in KiCad.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
@mcp.prompt()
|
||||||
|
def pcb_manufacturing_checklist() -> str:
|
||||||
|
"""Prompt for PCB manufacturing preparation checklist."""
|
||||||
|
prompt = """
|
||||||
|
I'm preparing to send my KiCad PCB design for manufacturing. Please help me with a comprehensive checklist of things to verify before submitting my design, including:
|
||||||
|
|
||||||
|
1. Design rule compliance
|
||||||
|
2. Layer stack configuration
|
||||||
|
3. Manufacturing notes and specifications
|
||||||
|
4. Required output files (Gerber, drill, etc.)
|
||||||
|
5. Component placement considerations
|
||||||
|
|
||||||
|
Please provide a detailed checklist I can follow to ensure my design is ready for manufacturing.
|
||||||
|
"""
|
@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Resource handlers for KiCad MCP Server.
|
||||||
|
"""
|
@ -0,0 +1,46 @@
|
|||||||
|
"""
|
||||||
|
File content resources for KiCad files.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
|
||||||
|
def register_file_resources(mcp: FastMCP) -> None:
|
||||||
|
"""Register file-related resources with the MCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcp: The FastMCP server instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
@mcp.resource("kicad://schematic/{schematic_path}")
|
||||||
|
def get_schematic_info(schematic_path: str) -> str:
|
||||||
|
"""Extract information from a KiCad schematic file."""
|
||||||
|
if not os.path.exists(schematic_path):
|
||||||
|
return f"Schematic file not found: {schematic_path}"
|
||||||
|
|
||||||
|
# KiCad schematic files are in S-expression format (not JSON)
|
||||||
|
# This is a basic extraction of text-based information
|
||||||
|
try:
|
||||||
|
with open(schematic_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# Basic extraction of components
|
||||||
|
components = []
|
||||||
|
for line in content.split('\n'):
|
||||||
|
if '(symbol ' in line and 'lib_id' in line:
|
||||||
|
components.append(line.strip())
|
||||||
|
|
||||||
|
result = f"# Schematic: {os.path.basename(schematic_path)}\n\n"
|
||||||
|
result += f"## Components (Estimated Count: {len(components)})\n\n"
|
||||||
|
|
||||||
|
# Extract a sample of components
|
||||||
|
for i, comp in enumerate(components[:10]):
|
||||||
|
result += f"{comp}\n"
|
||||||
|
|
||||||
|
if len(components) > 10:
|
||||||
|
result += f"\n... and {len(components) - 10} more components\n"
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error reading schematic file: {str(e)}"
|
@ -0,0 +1,67 @@
|
|||||||
|
"""
|
||||||
|
Project listing and information resources.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
from kicad_mcp.utils.kicad_utils import find_kicad_projects
|
||||||
|
from kicad_mcp.utils.file_utils import get_project_files, load_project_json
|
||||||
|
|
||||||
|
|
||||||
|
def register_project_resources(mcp: FastMCP) -> None:
|
||||||
|
"""Register project-related resources with the MCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcp: The FastMCP server instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
@mcp.resource("kicad://projects")
|
||||||
|
def list_projects_resource() -> str:
|
||||||
|
"""List all KiCad projects as a formatted resource."""
|
||||||
|
projects = find_kicad_projects()
|
||||||
|
|
||||||
|
if not projects:
|
||||||
|
return "No KiCad projects found in your Documents/KiCad directory."
|
||||||
|
|
||||||
|
result = "# KiCad Projects\n\n"
|
||||||
|
for project in sorted(projects, key=lambda p: p["modified"], reverse=True):
|
||||||
|
result += f"## {project['name']}\n"
|
||||||
|
result += f"- **Path**: {project['path']}\n"
|
||||||
|
result += f"- **Last Modified**: {os.path.getmtime(project['path'])}\n\n"
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@mcp.resource("kicad://project/{project_path}")
|
||||||
|
def get_project_details(project_path: str) -> str:
|
||||||
|
"""Get details about a specific KiCad project."""
|
||||||
|
if not os.path.exists(project_path):
|
||||||
|
return f"Project not found: {project_path}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Load project file
|
||||||
|
project_data = load_project_json(project_path)
|
||||||
|
if not project_data:
|
||||||
|
return f"Error reading project file: {project_path}"
|
||||||
|
|
||||||
|
# Get related files
|
||||||
|
files = get_project_files(project_path)
|
||||||
|
|
||||||
|
# Format project details
|
||||||
|
result = f"# Project: {os.path.basename(project_path)[:-10]}\n\n"
|
||||||
|
|
||||||
|
result += "## Project Files\n"
|
||||||
|
for file_type, file_path in files.items():
|
||||||
|
result += f"- **{file_type}**: {file_path}\n"
|
||||||
|
|
||||||
|
result += "\n## Project Settings\n"
|
||||||
|
|
||||||
|
# Extract metadata
|
||||||
|
if "metadata" in project_data:
|
||||||
|
metadata = project_data["metadata"]
|
||||||
|
for key, value in metadata.items():
|
||||||
|
result += f"- **{key}**: {value}\n"
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error reading project file: {str(e)}"
|
@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
MCP server creation and configuration.
|
||||||
|
"""
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
# Import resource handlers
|
||||||
|
from kicad_mcp.resources.projects import register_project_resources
|
||||||
|
from kicad_mcp.resources.files import register_file_resources
|
||||||
|
|
||||||
|
# Import tool handlers
|
||||||
|
from kicad_mcp.tools.project_tools import register_project_tools
|
||||||
|
from kicad_mcp.tools.analysis_tools import register_analysis_tools
|
||||||
|
from kicad_mcp.tools.export_tools import register_export_tools
|
||||||
|
|
||||||
|
# Import prompt handlers
|
||||||
|
from kicad_mcp.prompts.templates import register_prompts
|
||||||
|
|
||||||
|
def create_server() -> FastMCP:
|
||||||
|
"""Create and configure the KiCad MCP server."""
|
||||||
|
# Initialize FastMCP server
|
||||||
|
mcp = FastMCP("KiCad")
|
||||||
|
|
||||||
|
# Register resources
|
||||||
|
register_project_resources(mcp)
|
||||||
|
register_file_resources(mcp)
|
||||||
|
|
||||||
|
# Register tools
|
||||||
|
register_project_tools(mcp)
|
||||||
|
register_analysis_tools(mcp)
|
||||||
|
register_export_tools(mcp)
|
||||||
|
|
||||||
|
# Register prompts
|
||||||
|
register_prompts(mcp)
|
||||||
|
|
||||||
|
return mcp
|
@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Tool handlers for KiCad MCP Server.
|
||||||
|
"""
|
@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Analysis and validation tools for KiCad projects.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
from mcp.server.fastmcp import FastMCP, Context, Image
|
||||||
|
|
||||||
|
from kicad_mcp.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
|
def register_analysis_tools(mcp: FastMCP) -> None:
|
||||||
|
"""Register analysis and validation tools with the MCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcp: The FastMCP server instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def validate_project(project_path: str) -> Dict[str, Any]:
|
||||||
|
"""Basic validation of a KiCad project."""
|
||||||
|
if not os.path.exists(project_path):
|
||||||
|
return {"valid": False, "error": f"Project not found: {project_path}"}
|
||||||
|
|
||||||
|
issues = []
|
||||||
|
files = get_project_files(project_path)
|
||||||
|
|
||||||
|
# Check for essential files
|
||||||
|
if "pcb" not in files:
|
||||||
|
issues.append("Missing PCB layout file")
|
||||||
|
|
||||||
|
if "schematic" not in files:
|
||||||
|
issues.append("Missing schematic file")
|
||||||
|
|
||||||
|
# Validate project file
|
||||||
|
try:
|
||||||
|
with open(project_path, 'r') as f:
|
||||||
|
import json
|
||||||
|
json.load(f)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
issues.append("Invalid project file format (JSON parsing error)")
|
||||||
|
except Exception as e:
|
||||||
|
issues.append(f"Error reading project file: {str(e)}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"valid": len(issues) == 0,
|
||||||
|
"path": project_path,
|
||||||
|
"issues": issues if issues else None,
|
||||||
|
"files_found": list(files.keys())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
|||||||
|
"""
|
||||||
|
Export and file generation tools for KiCad projects.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from typing import Dict, Any
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
from kicad_mcp.utils.file_utils import get_project_files
|
||||||
|
from kicad_mcp.utils.kicad_utils import get_project_name_from_path
|
||||||
|
|
||||||
|
|
||||||
|
def register_export_tools(mcp: FastMCP) -> None:
|
||||||
|
"""Register export and file generation tools with the MCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcp: The FastMCP server instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def extract_bom(project_path: str) -> Dict[str, Any]:
|
||||||
|
"""Extract a Bill of Materials (BOM) from a KiCad project."""
|
||||||
|
if not os.path.exists(project_path):
|
||||||
|
return {"success": False, "error": f"Project not found: {project_path}"}
|
||||||
|
|
||||||
|
project_dir = os.path.dirname(project_path)
|
||||||
|
project_name = get_project_name_from_path(project_path)
|
||||||
|
|
||||||
|
# Look for existing BOM files
|
||||||
|
bom_files = []
|
||||||
|
for file in os.listdir(project_dir):
|
||||||
|
if file.startswith(project_name) and file.endswith('.csv') and 'bom' in file.lower():
|
||||||
|
bom_files.append(os.path.join(project_dir, file))
|
||||||
|
|
||||||
|
if not bom_files:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": "No BOM files found. You need to generate a BOM using KiCad first."
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Read the first BOM file
|
||||||
|
bom_path = bom_files[0]
|
||||||
|
with open(bom_path, 'r') as f:
|
||||||
|
bom_content = f.read()
|
||||||
|
|
||||||
|
# Parse CSV (simplified)
|
||||||
|
lines = bom_content.strip().split('\n')
|
||||||
|
headers = lines[0].split(',')
|
||||||
|
|
||||||
|
components = []
|
||||||
|
for line in lines[1:]:
|
||||||
|
values = line.split(',')
|
||||||
|
if len(values) >= len(headers):
|
||||||
|
component = {}
|
||||||
|
for i, header in enumerate(headers):
|
||||||
|
component[header.strip()] = values[i].strip()
|
||||||
|
components.append(component)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"bom_file": bom_path,
|
||||||
|
"headers": headers,
|
||||||
|
"component_count": len(components),
|
||||||
|
"components": components
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": str(e)}
|
@ -0,0 +1,53 @@
|
|||||||
|
"""
|
||||||
|
Project management tools for KiCad.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
from kicad_mcp.utils.kicad_utils import find_kicad_projects, open_kicad_project
|
||||||
|
from kicad_mcp.utils.file_utils import get_project_files, load_project_json
|
||||||
|
|
||||||
|
|
||||||
|
def register_project_tools(mcp: FastMCP) -> None:
|
||||||
|
"""Register project management tools with the MCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcp: The FastMCP server instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def find_projects() -> List[Dict[str, Any]]:
|
||||||
|
"""Find all KiCad projects on this system."""
|
||||||
|
return find_kicad_projects()
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def get_project_structure(project_path: str) -> Dict[str, Any]:
|
||||||
|
"""Get the structure and files of a KiCad project."""
|
||||||
|
if not os.path.exists(project_path):
|
||||||
|
return {"error": f"Project not found: {project_path}"}
|
||||||
|
|
||||||
|
project_dir = os.path.dirname(project_path)
|
||||||
|
project_name = os.path.basename(project_path)[:-10] # Remove .kicad_pro extension
|
||||||
|
|
||||||
|
# Get related files
|
||||||
|
files = get_project_files(project_path)
|
||||||
|
|
||||||
|
# Get project metadata
|
||||||
|
metadata = {}
|
||||||
|
project_data = load_project_json(project_path)
|
||||||
|
if project_data and "metadata" in project_data:
|
||||||
|
metadata = project_data["metadata"]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"name": project_name,
|
||||||
|
"path": project_path,
|
||||||
|
"directory": project_dir,
|
||||||
|
"files": files,
|
||||||
|
"metadata": metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def open_project(project_path: str) -> Dict[str, Any]:
|
||||||
|
"""Open a KiCad project in KiCad."""
|
||||||
|
return open_kicad_project(project_path)
|
@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Utility functions for KiCad MCP Server.
|
||||||
|
"""
|
@ -0,0 +1,66 @@
|
|||||||
|
"""
|
||||||
|
File handling utilities for KiCad MCP Server.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from typing import Dict, List, Any, Optional
|
||||||
|
|
||||||
|
from kicad_mcp.utils.kicad_utils import get_project_name_from_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_files(project_path: str) -> Dict[str, str]:
|
||||||
|
"""Get all files related to a KiCad project.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_path: Path to the .kicad_pro file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mapping file types to file paths
|
||||||
|
"""
|
||||||
|
from kicad_mcp.config import KICAD_EXTENSIONS, DATA_EXTENSIONS
|
||||||
|
|
||||||
|
project_dir = os.path.dirname(project_path)
|
||||||
|
project_name = get_project_name_from_path(project_path)
|
||||||
|
|
||||||
|
files = {}
|
||||||
|
|
||||||
|
# Check for standard KiCad files
|
||||||
|
for file_type, extension in KICAD_EXTENSIONS.items():
|
||||||
|
if file_type == "project":
|
||||||
|
# We already have the project file
|
||||||
|
files[file_type] = project_path
|
||||||
|
continue
|
||||||
|
|
||||||
|
file_path = os.path.join(project_dir, f"{project_name}{extension}")
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
files[file_type] = file_path
|
||||||
|
|
||||||
|
# Check for data files
|
||||||
|
for ext in DATA_EXTENSIONS:
|
||||||
|
for file in os.listdir(project_dir):
|
||||||
|
if file.startswith(project_name) and file.endswith(ext):
|
||||||
|
# Extract the type from filename (e.g., project_name-bom.csv -> bom)
|
||||||
|
file_type = file[len(project_name):].strip('-_')
|
||||||
|
file_type = file_type.split('.')[0]
|
||||||
|
if not file_type:
|
||||||
|
file_type = ext[1:] # Use extension if no specific type
|
||||||
|
|
||||||
|
files[file_type] = os.path.join(project_dir, file)
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def load_project_json(project_path: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Load and parse a KiCad project file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_path: Path to the .kicad_pro file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Parsed JSON data or None if parsing failed
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(project_path, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except Exception:
|
||||||
|
return None
|
@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
KiCad-specific utility functions.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
|
||||||
|
from kicad_mcp.config import KICAD_USER_DIR, KICAD_APP_PATH, KICAD_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
|
def find_kicad_projects() -> List[Dict[str, Any]]:
|
||||||
|
"""Find KiCad projects in the user's directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of dictionaries with project information
|
||||||
|
"""
|
||||||
|
projects = []
|
||||||
|
|
||||||
|
for root, _, files in os.walk(KICAD_USER_DIR):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(KICAD_EXTENSIONS["project"]):
|
||||||
|
project_path = os.path.join(root, file)
|
||||||
|
rel_path = os.path.relpath(project_path, KICAD_USER_DIR)
|
||||||
|
project_name = get_project_name_from_path(project_path)
|
||||||
|
|
||||||
|
projects.append({
|
||||||
|
"name": project_name,
|
||||||
|
"path": project_path,
|
||||||
|
"relative_path": rel_path,
|
||||||
|
"modified": os.path.getmtime(project_path)
|
||||||
|
})
|
||||||
|
|
||||||
|
return projects
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_name_from_path(project_path: str) -> str:
|
||||||
|
"""Extract the project name from a .kicad_pro file path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_path: Path to the .kicad_pro file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Project name without extension
|
||||||
|
"""
|
||||||
|
basename = os.path.basename(project_path)
|
||||||
|
return basename[:-len(KICAD_EXTENSIONS["project"])]
|
||||||
|
|
||||||
|
|
||||||
|
def open_kicad_project(project_path: str) -> Dict[str, Any]:
|
||||||
|
"""Open a KiCad project using the KiCad application.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_path: Path to the .kicad_pro file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with result information
|
||||||
|
"""
|
||||||
|
if not os.path.exists(project_path):
|
||||||
|
return {"success": False, "error": f"Project not found: {project_path}"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# On MacOS, use the 'open' command to open the project in KiCad
|
||||||
|
cmd = ["open", "-a", KICAD_APP_PATH, project_path]
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": result.returncode == 0,
|
||||||
|
"command": " ".join(cmd),
|
||||||
|
"output": result.stdout,
|
||||||
|
"error": result.stderr if result.returncode != 0 else None
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": str(e)}
|
10
main.py
10
main.py
@ -0,0 +1,10 @@
|
|||||||
|
#!/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.
|
||||||
|
"""
|
||||||
|
from kicad_mcp.server import create_server
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
server = create_server()
|
||||||
|
server.run(transport='stdio')
|
@ -0,0 +1,2 @@
|
|||||||
|
mcp[cli]
|
||||||
|
httpx
|
Loading…
x
Reference in New Issue
Block a user