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.
|
||||
|
||||
@ -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)
|
||||
- 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
|
||||
|
||||
### 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
|
||||
# Create a new directory for our project
|
||||
@ -24,34 +44,23 @@ cd ~/Projects/kicad-mcp
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# Install the MCP SDK
|
||||
pip install "mcp[cli]"
|
||||
# Install the MCP SDK and other dependencies
|
||||
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
|
||||
# Make the file executable (optional, but helpful)
|
||||
chmod +x kicad_mcp.py
|
||||
# Run in development mode
|
||||
python -m mcp.dev main.py
|
||||
|
||||
# Run the server in development mode
|
||||
python -m mcp.dev kicad_mcp.py
|
||||
# Or run directly
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 3. Test Your Server
|
||||
|
||||
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
|
||||
### 3. Configure Claude Desktop
|
||||
|
||||
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": {
|
||||
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
|
||||
"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.
|
||||
|
||||
### 5. Restart Claude Desktop
|
||||
### 4. Restart Claude Desktop
|
||||
|
||||
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
|
||||
2. Look for the tools icon (hammer symbol) in the Claude interface
|
||||
3. You should see the KiCad MCP tools available in the menu
|
||||
### Resources vs Tools vs Prompts
|
||||
|
||||
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?"
|
||||
- "Can you help me open my latest KiCad project?"
|
||||
- "Extract the bill of materials from my project at [path]"
|
||||
- "Validate my KiCad project at [path]"
|
||||
**Tools** are functions that perform actions or computations:
|
||||
- Similar to POST/PUT endpoints in REST APIs
|
||||
- Can have side effects (like opening applications or generating files)
|
||||
- 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
|
||||
|
||||
@ -110,108 +184,33 @@ If you encounter issues:
|
||||
1. **Server Not Appearing in Claude Desktop:**
|
||||
- Check your `claude_desktop_config.json` file for errors
|
||||
- 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:**
|
||||
- Check the terminal output when running the server in development mode
|
||||
- Make sure all required Python packages are installed
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
This project is open source under the MIT license.
|
||||
|
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