Rename project from kicad-mcp to mckicad
Rename source directory kicad_mcp/ → mckicad/, update all imports, pyproject.toml metadata, documentation references, Makefile targets, and .gitignore paths. All 195 tests pass.
This commit is contained in:
parent
b99d1bd2b9
commit
687e14bd11
2
.gitignore
vendored
2
.gitignore
vendored
@ -47,7 +47,7 @@ logs/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# MCP specific
|
# MCP specific
|
||||||
~/.kicad_mcp/drc_history/
|
~/.mckicad/drc_history/
|
||||||
|
|
||||||
# UV and modern Python tooling
|
# UV and modern Python tooling
|
||||||
uv.lock
|
uv.lock
|
||||||
|
|||||||
30
CLAUDE.md
30
CLAUDE.md
@ -9,8 +9,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
- `make run` - Start the KiCad MCP server (`uv run python main.py`)
|
- `make run` - Start the KiCad MCP server (`uv run python main.py`)
|
||||||
- `make test` - Run all tests with pytest
|
- `make test` - Run all tests with pytest
|
||||||
- `make test <file>` - Run specific test file
|
- `make test <file>` - Run specific test file
|
||||||
- `make lint` - Run linting with ruff and mypy (`uv run ruff check kicad_mcp/ tests/` + `uv run mypy kicad_mcp/`)
|
- `make lint` - Run linting with ruff and mypy (`uv run ruff check mckicad/ tests/` + `uv run mypy mckicad/`)
|
||||||
- `make format` - Format code with ruff (`uv run ruff format kicad_mcp/ tests/`)
|
- `make format` - Format code with ruff (`uv run ruff format mckicad/ tests/`)
|
||||||
- `make build` - Build package with uv
|
- `make build` - Build package with uv
|
||||||
- `make clean` - Clean build artifacts
|
- `make clean` - Clean build artifacts
|
||||||
|
|
||||||
@ -48,19 +48,19 @@ This project implements a Model Context Protocol (MCP) server for KiCad electron
|
|||||||
|
|
||||||
### Key Modules
|
### Key Modules
|
||||||
|
|
||||||
#### Core Server (`kicad_mcp/server.py`)
|
#### Core Server (`mckicad/server.py`)
|
||||||
- FastMCP server initialization with lifespan management
|
- FastMCP server initialization with lifespan management
|
||||||
- Registers all resources, tools, and prompts
|
- Registers all resources, tools, and prompts
|
||||||
- Signal handling for graceful shutdown
|
- Signal handling for graceful shutdown
|
||||||
- Cleanup handlers for temporary directories
|
- Cleanup handlers for temporary directories
|
||||||
|
|
||||||
#### Configuration (`kicad_mcp/config.py`)
|
#### Configuration (`mckicad/config.py`)
|
||||||
- Platform-specific KiCad paths (macOS/Windows/Linux)
|
- Platform-specific KiCad paths (macOS/Windows/Linux)
|
||||||
- Environment variable handling (`KICAD_SEARCH_PATHS`, `KICAD_USER_DIR`)
|
- Environment variable handling (`KICAD_SEARCH_PATHS`, `KICAD_USER_DIR`)
|
||||||
- Component library mappings and default footprints
|
- Component library mappings and default footprints
|
||||||
- Timeout and display constants
|
- Timeout and display constants
|
||||||
|
|
||||||
#### Context Management (`kicad_mcp/context.py`)
|
#### Context Management (`mckicad/context.py`)
|
||||||
- Lifespan context with KiCad module availability detection
|
- Lifespan context with KiCad module availability detection
|
||||||
- Shared cache across requests
|
- Shared cache across requests
|
||||||
- Application state management
|
- Application state management
|
||||||
@ -79,7 +79,7 @@ This project implements a Model Context Protocol (MCP) server for KiCad electron
|
|||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
```
|
```
|
||||||
kicad_mcp/
|
mckicad/
|
||||||
├── resources/ # MCP resources (data providers)
|
├── resources/ # MCP resources (data providers)
|
||||||
├── tools/ # MCP tools (action performers)
|
├── tools/ # MCP tools (action performers)
|
||||||
├── prompts/ # MCP prompt templates
|
├── prompts/ # MCP prompt templates
|
||||||
@ -94,7 +94,7 @@ kicad_mcp/
|
|||||||
|
|
||||||
### Adding New Features
|
### Adding New Features
|
||||||
1. Identify component type (resource/tool/prompt)
|
1. Identify component type (resource/tool/prompt)
|
||||||
2. Add implementation to appropriate module in `kicad_mcp/`
|
2. Add implementation to appropriate module in `mckicad/`
|
||||||
3. Register in `server.py` create_server() function
|
3. Register in `server.py` create_server() function
|
||||||
4. Use lifespan context for shared state and caching
|
4. Use lifespan context for shared state and caching
|
||||||
5. Include progress reporting for long operations
|
5. Include progress reporting for long operations
|
||||||
@ -137,7 +137,7 @@ This project implements groundbreaking real-time PCB automation through two adva
|
|||||||
|
|
||||||
**Key Usage Pattern:**
|
**Key Usage Pattern:**
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.utils.ipc_client import kicad_ipc_session
|
from mckicad.utils.ipc_client import kicad_ipc_session
|
||||||
|
|
||||||
with kicad_ipc_session(board_path) as client:
|
with kicad_ipc_session(board_path) as client:
|
||||||
footprints = client.get_footprints()
|
footprints = client.get_footprints()
|
||||||
@ -216,12 +216,12 @@ Key environment variables in `.env`:
|
|||||||
The package declares a script entry point in `pyproject.toml`:
|
The package declares a script entry point in `pyproject.toml`:
|
||||||
```toml
|
```toml
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
kicad-mcp = "kicad_mcp.server:main"
|
mckicad = "mckicad.server:main"
|
||||||
```
|
```
|
||||||
This enables running via `uvx kicad-mcp` after installation.
|
This enables running via `uvx mckicad` after installation.
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
- All logs written to `kicad-mcp.log` in project root
|
- All logs written to `mckicad.log` in project root
|
||||||
- Log file overwritten on each server start
|
- Log file overwritten on each server start
|
||||||
- PID included in log messages for process tracking
|
- PID included in log messages for process tracking
|
||||||
- Use `logging` module, never `print()` statements
|
- Use `logging` module, never `print()` statements
|
||||||
@ -231,7 +231,7 @@ This enables running via `uvx kicad-mcp` after installation.
|
|||||||
### FastMCP Tool Registration
|
### FastMCP Tool Registration
|
||||||
All tools follow this registration pattern in `server.py`:
|
All tools follow this registration pattern in `server.py`:
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.tools.example_tools import register_example_tools
|
from mckicad.tools.example_tools import register_example_tools
|
||||||
|
|
||||||
def create_server() -> FastMCP:
|
def create_server() -> FastMCP:
|
||||||
mcp = FastMCP("KiCad", lifespan=lifespan_factory)
|
mcp = FastMCP("KiCad", lifespan=lifespan_factory)
|
||||||
@ -259,7 +259,7 @@ Each tool module should:
|
|||||||
### Progress Reporting
|
### Progress Reporting
|
||||||
For long operations, use progress constants:
|
For long operations, use progress constants:
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.config import PROGRESS_CONSTANTS
|
from mckicad.config import PROGRESS_CONSTANTS
|
||||||
|
|
||||||
progress_callback(PROGRESS_CONSTANTS["start"])
|
progress_callback(PROGRESS_CONSTANTS["start"])
|
||||||
# ... do work ...
|
# ... do work ...
|
||||||
@ -328,13 +328,13 @@ LOG_LEVEL=DEBUG
|
|||||||
|
|
||||||
### Test IPC Connection
|
### Test IPC Connection
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.utils.ipc_client import check_kicad_availability
|
from mckicad.utils.ipc_client import check_kicad_availability
|
||||||
print(check_kicad_availability())
|
print(check_kicad_availability())
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test FreeRouting Setup
|
### Test FreeRouting Setup
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import check_routing_prerequisites
|
||||||
print(check_routing_prerequisites())
|
print(check_routing_prerequisites())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
6
Makefile
6
Makefile
@ -25,11 +25,11 @@ test:
|
|||||||
@:
|
@:
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
uv run ruff check kicad_mcp/ tests/
|
uv run ruff check mckicad/ tests/
|
||||||
uv run mypy kicad_mcp/
|
uv run mypy mckicad/
|
||||||
|
|
||||||
format:
|
format:
|
||||||
uv run ruff format kicad_mcp/ tests/
|
uv run ruff format mckicad/ tests/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf dist/
|
rm -rf dist/
|
||||||
|
|||||||
14
README.md
14
README.md
@ -117,8 +117,8 @@ We've integrated cutting-edge technologies to create something truly revolutiona
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone and setup
|
# Clone and setup
|
||||||
git clone https://github.com/your-org/kicad-mcp.git
|
git clone https://github.com/your-org/mckicad.git
|
||||||
cd kicad-mcp
|
cd mckicad
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# Configure environment
|
# Configure environment
|
||||||
@ -132,8 +132,8 @@ cp .env.example .env
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"kicad": {
|
"kicad": {
|
||||||
"command": "/path/to/kicad-mcp/.venv/bin/python",
|
"command": "/path/to/mckicad/.venv/bin/python",
|
||||||
"args": ["/path/to/kicad-mcp/main.py"]
|
"args": ["/path/to/mckicad/main.py"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,8 +456,8 @@ make run
|
|||||||
- ❓ **[FAQ](docs/faq.md)** - Common questions answered
|
- ❓ **[FAQ](docs/faq.md)** - Common questions answered
|
||||||
|
|
||||||
### **Community**
|
### **Community**
|
||||||
- 💬 **[Discussions](https://github.com/your-org/kicad-mcp/discussions)** - Share ideas and get help
|
- 💬 **[Discussions](https://github.com/your-org/mckicad/discussions)** - Share ideas and get help
|
||||||
- 🐛 **[Issues](https://github.com/your-org/kicad-mcp/issues)** - Report bugs and request features
|
- 🐛 **[Issues](https://github.com/your-org/mckicad/issues)** - Report bugs and request features
|
||||||
- 🔧 **[Contributing Guide](CONTRIBUTING.md)** - Join the development
|
- 🔧 **[Contributing Guide](CONTRIBUTING.md)** - Join the development
|
||||||
|
|
||||||
### **Support**
|
### **Support**
|
||||||
@ -499,7 +499,7 @@ This project is open source under the **MIT License** - see the [LICENSE](LICENS
|
|||||||
|
|
||||||
## 🚀 Ready to Transform Your Design Workflow?
|
## 🚀 Ready to Transform Your Design Workflow?
|
||||||
|
|
||||||
**[Get Started Now](https://github.com/your-org/kicad-mcp)** • **[Join the Community](https://discord.gg/your-invite)** • **[Read the Docs](docs/)**
|
**[Get Started Now](https://github.com/your-org/mckicad)** • **[Join the Community](https://discord.gg/your-invite)** • **[Read the Docs](docs/)**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -256,6 +256,6 @@ And it's available today, through the power of human-AI collaboration.
|
|||||||
**About This Collaboration:**
|
**About This Collaboration:**
|
||||||
This revolutionary EDA automation platform was built through an intensive human-AI collaboration between Ryan Malloy and Claude Sonnet 4 using Claude Code. The project demonstrates the incredible potential when human creativity and AI technical implementation work together to solve complex engineering challenges.
|
This revolutionary EDA automation platform was built through an intensive human-AI collaboration between Ryan Malloy and Claude Sonnet 4 using Claude Code. The project demonstrates the incredible potential when human creativity and AI technical implementation work together to solve complex engineering challenges.
|
||||||
|
|
||||||
**GitHub Repository**: [KiCad MCP Server](https://github.com/user/kicad-mcp)
|
**GitHub Repository**: [KiCad MCP Server](https://github.com/user/mckicad)
|
||||||
**Performance Metrics**: 100% test success rate, sub-millisecond response times
|
**Performance Metrics**: 100% test success rate, sub-millisecond response times
|
||||||
**Impact**: Democratizes professional PCB design through conversational AI
|
**Impact**: Democratizes professional PCB design through conversational AI
|
||||||
@ -8,7 +8,7 @@ The KiCad MCP Server can be configured in multiple ways:
|
|||||||
|
|
||||||
1. **Environment Variables**: Set directly when running the server
|
1. **Environment Variables**: Set directly when running the server
|
||||||
2. **.env File**: Create a `.env` file in the project root (recommended)
|
2. **.env File**: Create a `.env` file in the project root (recommended)
|
||||||
3. **Code Modifications**: Edit configuration constants in `kicad_mcp/config.py`
|
3. **Code Modifications**: Edit configuration constants in `mckicad/config.py`
|
||||||
|
|
||||||
## Core Configuration Options
|
## Core Configuration Options
|
||||||
|
|
||||||
@ -97,9 +97,9 @@ To configure Claude Desktop to use the KiCad MCP Server:
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"kicad": {
|
"kicad": {
|
||||||
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
|
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/venv/bin/python",
|
||||||
"args": [
|
"args": [
|
||||||
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py"
|
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/main.py"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,9 +111,9 @@ To configure Claude Desktop to use the KiCad MCP Server:
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"kicad": {
|
"kicad": {
|
||||||
"command": "C:\\Path\\To\\Your\\Project\\kicad-mcp\\venv\\Scripts\\python.exe",
|
"command": "C:\\Path\\To\\Your\\Project\\mckicad\\venv\\Scripts\\python.exe",
|
||||||
"args": [
|
"args": [
|
||||||
"C:\\Path\\To\\Your\\Project\\kicad-mcp\\main.py"
|
"C:\\Path\\To\\Your\\Project\\mckicad\\main.py"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,9 +128,9 @@ You can also set environment variables directly in the client configuration:
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"kicad": {
|
"kicad": {
|
||||||
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
|
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/venv/bin/python",
|
||||||
"args": [
|
"args": [
|
||||||
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py"
|
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/main.py"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"KICAD_SEARCH_PATHS": "/custom/path1,/custom/path2",
|
"KICAD_SEARCH_PATHS": "/custom/path1,/custom/path2",
|
||||||
@ -145,7 +145,7 @@ You can also set environment variables directly in the client configuration:
|
|||||||
|
|
||||||
### Custom KiCad Extensions
|
### Custom KiCad Extensions
|
||||||
|
|
||||||
If you need to modify the recognized KiCad file extensions, you can edit `kicad_mcp/config.py`:
|
If you need to modify the recognized KiCad file extensions, you can edit `mckicad/config.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# File extensions
|
# File extensions
|
||||||
@ -161,14 +161,14 @@ KICAD_EXTENSIONS = {
|
|||||||
|
|
||||||
The server stores DRC history to track changes over time. By default, history is stored in:
|
The server stores DRC history to track changes over time. By default, history is stored in:
|
||||||
|
|
||||||
- macOS/Linux: `~/.kicad_mcp/drc_history/`
|
- macOS/Linux: `~/.mckicad/drc_history/`
|
||||||
- Windows: `%APPDATA%\kicad_mcp\drc_history\`
|
- Windows: `%APPDATA%\mckicad\drc_history\`
|
||||||
|
|
||||||
You can modify this in `kicad_mcp/utils/drc_history.py` if needed.
|
You can modify this in `mckicad/utils/drc_history.py` if needed.
|
||||||
|
|
||||||
### Python Path for KiCad Modules
|
### Python Path for KiCad Modules
|
||||||
|
|
||||||
The server attempts to locate and add KiCad's Python modules to the Python path automatically. If this fails, you can modify the search paths in `kicad_mcp/utils/python_path.py`.
|
The server attempts to locate and add KiCad's Python modules to the Python path automatically. If this fails, you can modify the search paths in `mckicad/utils/python_path.py`.
|
||||||
|
|
||||||
## Platform-Specific Configuration
|
## Platform-Specific Configuration
|
||||||
|
|
||||||
|
|||||||
@ -29,9 +29,9 @@ This guide provides detailed information for developers who want to modify or ex
|
|||||||
The KiCad MCP Server follows a modular architecture:
|
The KiCad MCP Server follows a modular architecture:
|
||||||
|
|
||||||
```
|
```
|
||||||
kicad-mcp/
|
mckicad/
|
||||||
├── main.py # Entry point
|
├── main.py # Entry point
|
||||||
├── kicad_mcp/ # Main package
|
├── mckicad/ # Main package
|
||||||
│ ├── __init__.py
|
│ ├── __init__.py
|
||||||
│ ├── server.py # Server creation and setup
|
│ ├── server.py # Server creation and setup
|
||||||
│ ├── config.py # Configuration settings
|
│ ├── config.py # Configuration settings
|
||||||
@ -69,7 +69,7 @@ kicad-mcp/
|
|||||||
|
|
||||||
Resources provide read-only data to the LLM. To add a new resource:
|
Resources provide read-only data to the LLM. To add a new resource:
|
||||||
|
|
||||||
1. Add your function to an existing resource file or create a new one in `kicad_mcp/resources/`:
|
1. Add your function to an existing resource file or create a new one in `mckicad/resources/`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
@ -91,10 +91,10 @@ def register_my_resources(mcp: FastMCP) -> None:
|
|||||||
return f"Formatted data about {parameter}"
|
return f"Formatted data about {parameter}"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Register your resources in `kicad_mcp/server.py`:
|
2. Register your resources in `mckicad/server.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.resources.my_resources import register_my_resources
|
from mckicad.resources.my_resources import register_my_resources
|
||||||
|
|
||||||
def create_server() -> FastMCP:
|
def create_server() -> FastMCP:
|
||||||
# ...
|
# ...
|
||||||
@ -106,7 +106,7 @@ def create_server() -> FastMCP:
|
|||||||
|
|
||||||
Tools are functions that perform actions or computations. To add a new tool:
|
Tools are functions that perform actions or computations. To add a new tool:
|
||||||
|
|
||||||
1. Add your function to an existing tool file or create a new one in `kicad_mcp/tools/`:
|
1. Add your function to an existing tool file or create a new one in `mckicad/tools/`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
@ -143,10 +143,10 @@ def register_my_tools(mcp: FastMCP) -> None:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Register your tools in `kicad_mcp/server.py`:
|
2. Register your tools in `mckicad/server.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.tools.my_tools import register_my_tools
|
from mckicad.tools.my_tools import register_my_tools
|
||||||
|
|
||||||
def create_server() -> FastMCP:
|
def create_server() -> FastMCP:
|
||||||
# ...
|
# ...
|
||||||
@ -158,7 +158,7 @@ def create_server() -> FastMCP:
|
|||||||
|
|
||||||
Prompts are reusable templates for common interactions. To add a new prompt:
|
Prompts are reusable templates for common interactions. To add a new prompt:
|
||||||
|
|
||||||
1. Add your function to an existing prompt file or create a new one in `kicad_mcp/prompts/`:
|
1. Add your function to an existing prompt file or create a new one in `mckicad/prompts/`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
@ -185,10 +185,10 @@ def register_my_prompts(mcp: FastMCP) -> None:
|
|||||||
return prompt
|
return prompt
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Register your prompts in `kicad_mcp/server.py`:
|
2. Register your prompts in `mckicad/server.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.prompts.my_prompts import register_my_prompts
|
from mckicad.prompts.my_prompts import register_my_prompts
|
||||||
|
|
||||||
def create_server() -> FastMCP:
|
def create_server() -> FastMCP:
|
||||||
# ...
|
# ...
|
||||||
@ -201,7 +201,7 @@ def create_server() -> FastMCP:
|
|||||||
The KiCad MCP Server uses a typed lifespan context to share data across requests:
|
The KiCad MCP Server uses a typed lifespan context to share data across requests:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from kicad_mcp.context import KiCadAppContext
|
from mckicad.context import KiCadAppContext
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def my_tool(parameter: str, ctx: Context) -> Dict[str, Any]:
|
def my_tool(parameter: str, ctx: Context) -> Dict[str, Any]:
|
||||||
|
|||||||
@ -120,7 +120,7 @@ The pattern recognition system is designed to be extensible. If you find that ce
|
|||||||
|
|
||||||
### Adding New Component Patterns
|
### Adding New Component Patterns
|
||||||
|
|
||||||
The pattern recognition is primarily based on regular expression matching of component values and library IDs. The patterns are defined in the `kicad_mcp/utils/pattern_recognition.py` file.
|
The pattern recognition is primarily based on regular expression matching of component values and library IDs. The patterns are defined in the `mckicad/utils/pattern_recognition.py` file.
|
||||||
|
|
||||||
For example, to add support for a new microcontroller family, you could update the `mcu_patterns` dictionary in the `identify_microcontrollers()` function:
|
For example, to add support for a new microcontroller family, you could update the `mcu_patterns` dictionary in the `identify_microcontrollers()` function:
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ Similarly, you can add patterns for new sensors, power supply ICs, or other comp
|
|||||||
|
|
||||||
### Adding New Circuit Recognition Functions
|
### Adding New Circuit Recognition Functions
|
||||||
|
|
||||||
For entirely new types of circuits, you can add new recognition functions in the `kicad_mcp/utils/pattern_recognition.py` file, following the pattern of existing functions.
|
For entirely new types of circuits, you can add new recognition functions in the `mckicad/utils/pattern_recognition.py` file, following the pattern of existing functions.
|
||||||
|
|
||||||
For example, you might add:
|
For example, you might add:
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ def identify_motor_drivers(components: Dict[str, Any], nets: Dict[str, Any]) ->
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, update the `identify_circuit_patterns()` function in `kicad_mcp/tools/pattern_tools.py` to call your new function and include its results.
|
Then, update the `identify_circuit_patterns()` function in `mckicad/tools/pattern_tools.py` to call your new function and include its results.
|
||||||
|
|
||||||
### Contributing Your Extensions
|
### Contributing Your Extensions
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ The pattern recognition system relies on a community-driven database of componen
|
|||||||
|
|
||||||
If you work with components that aren't being recognized:
|
If you work with components that aren't being recognized:
|
||||||
|
|
||||||
1. Check the current patterns in `kicad_mcp/utils/pattern_recognition.py`
|
1. Check the current patterns in `mckicad/utils/pattern_recognition.py`
|
||||||
2. Add your own patterns for components you use
|
2. Add your own patterns for components you use
|
||||||
3. Submit a pull request to share with the community
|
3. Submit a pull request to share with the community
|
||||||
|
|
||||||
|
|||||||
@ -63,9 +63,9 @@ This guide helps you troubleshoot common issues with the KiCad MCP Server.
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"kicad": {
|
"kicad": {
|
||||||
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python",
|
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/venv/bin/python",
|
||||||
"args": [
|
"args": [
|
||||||
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py"
|
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/main.py"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
main.py
10
main.py
@ -8,12 +8,12 @@ import sys
|
|||||||
import logging # Import logging module
|
import logging # Import logging module
|
||||||
|
|
||||||
# Must import config BEFORE env potentially overrides it via os.environ
|
# Must import config BEFORE env potentially overrides it via os.environ
|
||||||
from kicad_mcp.config import KICAD_USER_DIR, ADDITIONAL_SEARCH_PATHS
|
from mckicad.config import KICAD_USER_DIR, ADDITIONAL_SEARCH_PATHS
|
||||||
from kicad_mcp.server import main as server_main
|
from mckicad.server import main as server_main
|
||||||
from kicad_mcp.utils.env import load_dotenv
|
from mckicad.utils.env import load_dotenv
|
||||||
|
|
||||||
# --- Setup Logging ---
|
# --- Setup Logging ---
|
||||||
log_file = os.path.join(os.path.dirname(__file__), 'kicad-mcp.log')
|
log_file = os.path.join(os.path.dirname(__file__), 'mckicad.log')
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
format='%(asctime)s - %(levelname)s - [PID:%(process)d] - %(message)s',
|
format='%(asctime)s - %(levelname)s - [PID:%(process)d] - %(message)s',
|
||||||
@ -49,7 +49,7 @@ logging.info(f"os.environ['KICAD_SEARCH_PATHS'] after load_dotenv: {effective_se
|
|||||||
# Re-log the values imported from config.py to see if they reflect os.environ changes
|
# 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)
|
# (This depends on config.py using os.getenv internally AFTER load_dotenv runs)
|
||||||
try:
|
try:
|
||||||
from kicad_mcp import config
|
from mckicad import config
|
||||||
import importlib
|
import importlib
|
||||||
importlib.reload(config) # Attempt to force re-reading config
|
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 KICAD_USER_DIR from config.py after reload: {config.KICAD_USER_DIR}")
|
||||||
|
|||||||
@ -9,8 +9,8 @@ from fastmcp import FastMCP
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
# Import the helper functions from bom_tools.py to avoid code duplication
|
# Import the helper functions from bom_tools.py to avoid code duplication
|
||||||
from kicad_mcp.tools.bom_tools import analyze_bom_data, parse_bom_file
|
from mckicad.tools.bom_tools import analyze_bom_data, parse_bom_file
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
def register_bom_resources(mcp: FastMCP) -> None:
|
def register_bom_resources(mcp: FastMCP) -> None:
|
||||||
@ -6,9 +6,9 @@ import os
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli
|
from mckicad.tools.drc_impl.cli_drc import run_drc_via_cli
|
||||||
from kicad_mcp.utils.drc_history import get_drc_history
|
from mckicad.utils.drc_history import get_drc_history
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
def register_drc_resources(mcp: FastMCP) -> None:
|
def register_drc_resources(mcp: FastMCP) -> None:
|
||||||
@ -6,8 +6,8 @@ import os
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import analyze_netlist, extract_netlist
|
from mckicad.utils.netlist_parser import analyze_netlist, extract_netlist
|
||||||
|
|
||||||
|
|
||||||
def register_netlist_resources(mcp: FastMCP) -> None:
|
def register_netlist_resources(mcp: FastMCP) -> None:
|
||||||
@ -6,9 +6,9 @@ import os
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import extract_netlist
|
from mckicad.utils.netlist_parser import extract_netlist
|
||||||
from kicad_mcp.utils.pattern_recognition import (
|
from mckicad.utils.pattern_recognition import (
|
||||||
identify_amplifiers,
|
identify_amplifiers,
|
||||||
identify_digital_interfaces,
|
identify_digital_interfaces,
|
||||||
identify_filters,
|
identify_filters,
|
||||||
@ -6,7 +6,7 @@ import os
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files, load_project_json
|
from mckicad.utils.file_utils import get_project_files, load_project_json
|
||||||
|
|
||||||
|
|
||||||
def register_project_resources(mcp: FastMCP) -> None:
|
def register_project_resources(mcp: FastMCP) -> None:
|
||||||
@ -12,37 +12,37 @@ import signal
|
|||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
# Import context management
|
# Import context management
|
||||||
from kicad_mcp.context import kicad_lifespan
|
from mckicad.context import kicad_lifespan
|
||||||
from kicad_mcp.prompts.bom_prompts import register_bom_prompts
|
from mckicad.prompts.bom_prompts import register_bom_prompts
|
||||||
from kicad_mcp.prompts.drc_prompt import register_drc_prompts
|
from mckicad.prompts.drc_prompt import register_drc_prompts
|
||||||
from kicad_mcp.prompts.pattern_prompts import register_pattern_prompts
|
from mckicad.prompts.pattern_prompts import register_pattern_prompts
|
||||||
|
|
||||||
# Import prompt handlers
|
# Import prompt handlers
|
||||||
from kicad_mcp.prompts.templates import register_prompts
|
from mckicad.prompts.templates import register_prompts
|
||||||
from kicad_mcp.resources.bom_resources import register_bom_resources
|
from mckicad.resources.bom_resources import register_bom_resources
|
||||||
from kicad_mcp.resources.drc_resources import register_drc_resources
|
from mckicad.resources.drc_resources import register_drc_resources
|
||||||
from kicad_mcp.resources.files import register_file_resources
|
from mckicad.resources.files import register_file_resources
|
||||||
from kicad_mcp.resources.netlist_resources import register_netlist_resources
|
from mckicad.resources.netlist_resources import register_netlist_resources
|
||||||
from kicad_mcp.resources.pattern_resources import register_pattern_resources
|
from mckicad.resources.pattern_resources import register_pattern_resources
|
||||||
|
|
||||||
# Import resource handlers
|
# Import resource handlers
|
||||||
from kicad_mcp.resources.projects import register_project_resources
|
from mckicad.resources.projects import register_project_resources
|
||||||
from kicad_mcp.tools.advanced_drc_tools import register_advanced_drc_tools
|
from mckicad.tools.advanced_drc_tools import register_advanced_drc_tools
|
||||||
from kicad_mcp.tools.ai_tools import register_ai_tools
|
from mckicad.tools.ai_tools import register_ai_tools
|
||||||
from kicad_mcp.tools.analysis_tools import register_analysis_tools
|
from mckicad.tools.analysis_tools import register_analysis_tools
|
||||||
from kicad_mcp.tools.bom_tools import register_bom_tools
|
from mckicad.tools.bom_tools import register_bom_tools
|
||||||
from kicad_mcp.tools.drc_tools import register_drc_tools
|
from mckicad.tools.drc_tools import register_drc_tools
|
||||||
from kicad_mcp.tools.export_tools import register_export_tools
|
from mckicad.tools.export_tools import register_export_tools
|
||||||
from kicad_mcp.tools.layer_tools import register_layer_tools
|
from mckicad.tools.layer_tools import register_layer_tools
|
||||||
from kicad_mcp.tools.model3d_tools import register_model3d_tools
|
from mckicad.tools.model3d_tools import register_model3d_tools
|
||||||
from kicad_mcp.tools.netlist_tools import register_netlist_tools
|
from mckicad.tools.netlist_tools import register_netlist_tools
|
||||||
from kicad_mcp.tools.pattern_tools import register_pattern_tools
|
from mckicad.tools.pattern_tools import register_pattern_tools
|
||||||
from kicad_mcp.tools.project_automation import register_project_automation_tools
|
from mckicad.tools.project_automation import register_project_automation_tools
|
||||||
|
|
||||||
# Import tool handlers
|
# Import tool handlers
|
||||||
from kicad_mcp.tools.project_tools import register_project_tools
|
from mckicad.tools.project_tools import register_project_tools
|
||||||
from kicad_mcp.tools.routing_tools import register_routing_tools
|
from mckicad.tools.routing_tools import register_routing_tools
|
||||||
from kicad_mcp.tools.symbol_tools import register_symbol_tools
|
from mckicad.tools.symbol_tools import register_symbol_tools
|
||||||
|
|
||||||
# Track cleanup handlers
|
# Track cleanup handlers
|
||||||
cleanup_handlers = []
|
cleanup_handlers = []
|
||||||
@ -196,7 +196,7 @@ def create_server() -> FastMCP:
|
|||||||
"""Clean up any temporary directories created by the server."""
|
"""Clean up any temporary directories created by the server."""
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from kicad_mcp.utils.temp_dir_manager import get_temp_dirs
|
from mckicad.utils.temp_dir_manager import get_temp_dirs
|
||||||
|
|
||||||
temp_dirs = get_temp_dirs()
|
temp_dirs = get_temp_dirs()
|
||||||
logging.info(f"Cleaning up {len(temp_dirs)} temporary directories")
|
logging.info(f"Cleaning up {len(temp_dirs)} temporary directories")
|
||||||
@ -9,8 +9,8 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.advanced_drc import RuleSeverity, RuleType, create_drc_manager
|
from mckicad.utils.advanced_drc import RuleSeverity, RuleType, create_drc_manager
|
||||||
from kicad_mcp.utils.path_validator import validate_kicad_file
|
from mckicad.utils.path_validator import validate_kicad_file
|
||||||
|
|
||||||
|
|
||||||
def register_advanced_drc_tools(mcp: FastMCP) -> None:
|
def register_advanced_drc_tools(mcp: FastMCP) -> None:
|
||||||
@ -9,10 +9,10 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.component_utils import ComponentType, get_component_type
|
from mckicad.utils.component_utils import ComponentType, get_component_type
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import parse_netlist_file
|
from mckicad.utils.netlist_parser import parse_netlist_file
|
||||||
from kicad_mcp.utils.pattern_recognition import analyze_circuit_patterns
|
from mckicad.utils.pattern_recognition import analyze_circuit_patterns
|
||||||
|
|
||||||
|
|
||||||
def register_ai_tools(mcp: FastMCP) -> None:
|
def register_ai_tools(mcp: FastMCP) -> None:
|
||||||
@ -9,8 +9,8 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.ipc_client import check_kicad_availability, kicad_ipc_session
|
from mckicad.utils.ipc_client import check_kicad_availability, kicad_ipc_session
|
||||||
|
|
||||||
|
|
||||||
def register_analysis_tools(mcp: FastMCP) -> None:
|
def register_analysis_tools(mcp: FastMCP) -> None:
|
||||||
@ -10,7 +10,7 @@ from typing import Any
|
|||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
def register_bom_tools(mcp: FastMCP) -> None:
|
def register_bom_tools(mcp: FastMCP) -> None:
|
||||||
@ -644,7 +644,7 @@ async def export_bom_with_cli(
|
|||||||
|
|
||||||
# Define the command based on operating system
|
# Define the command based on operating system
|
||||||
if system == "Darwin": # macOS
|
if system == "Darwin": # macOS
|
||||||
from kicad_mcp.config import KICAD_APP_PATH
|
from mckicad.config import KICAD_APP_PATH
|
||||||
|
|
||||||
# Path to KiCad command-line tools on macOS
|
# Path to KiCad command-line tools on macOS
|
||||||
kicad_cli = os.path.join(KICAD_APP_PATH, "Contents/MacOS/kicad-cli")
|
kicad_cli = os.path.join(KICAD_APP_PATH, "Contents/MacOS/kicad-cli")
|
||||||
@ -660,7 +660,7 @@ async def export_bom_with_cli(
|
|||||||
cmd = [kicad_cli, "sch", "export", "bom", "--output", output_file, schematic_file]
|
cmd = [kicad_cli, "sch", "export", "bom", "--output", output_file, schematic_file]
|
||||||
|
|
||||||
elif system == "Windows":
|
elif system == "Windows":
|
||||||
from kicad_mcp.config import KICAD_APP_PATH
|
from mckicad.config import KICAD_APP_PATH
|
||||||
|
|
||||||
# Path to KiCad command-line tools on Windows
|
# Path to KiCad command-line tools on Windows
|
||||||
kicad_cli = os.path.join(KICAD_APP_PATH, "bin", "kicad-cli.exe")
|
kicad_cli = os.path.join(KICAD_APP_PATH, "bin", "kicad-cli.exe")
|
||||||
@ -10,7 +10,7 @@ from typing import Any
|
|||||||
|
|
||||||
from mcp.server.fastmcp import Context
|
from mcp.server.fastmcp import Context
|
||||||
|
|
||||||
from kicad_mcp.config import system
|
from mckicad.config import system
|
||||||
|
|
||||||
|
|
||||||
async def run_drc_via_cli(pcb_file: str) -> dict[str, Any]:
|
async def run_drc_via_cli(pcb_file: str) -> dict[str, Any]:
|
||||||
@ -10,9 +10,9 @@ from typing import Any
|
|||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
# Import implementations
|
# Import implementations
|
||||||
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli
|
from mckicad.tools.drc_impl.cli_drc import run_drc_via_cli
|
||||||
from kicad_mcp.utils.drc_history import compare_with_previous, get_drc_history, save_drc_result
|
from mckicad.utils.drc_history import compare_with_previous, get_drc_history, save_drc_result
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
def register_drc_tools(mcp: FastMCP) -> None:
|
def register_drc_tools(mcp: FastMCP) -> None:
|
||||||
@ -96,7 +96,7 @@ def register_drc_tools(mcp: FastMCP) -> None:
|
|||||||
print("Using kicad-cli for DRC")
|
print("Using kicad-cli for DRC")
|
||||||
# Use synchronous DRC check
|
# Use synchronous DRC check
|
||||||
try:
|
try:
|
||||||
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli_sync
|
from mckicad.tools.drc_impl.cli_drc import run_drc_via_cli_sync
|
||||||
drc_results = run_drc_via_cli_sync(pcb_file)
|
drc_results = run_drc_via_cli_sync(pcb_file)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Fallback - call the async version but handle it differently
|
# Fallback - call the async version but handle it differently
|
||||||
@ -10,8 +10,8 @@ import subprocess
|
|||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
from fastmcp.utilities.types import Image
|
from fastmcp.utilities.types import Image
|
||||||
|
|
||||||
from kicad_mcp.config import KICAD_APP_PATH, system
|
from mckicad.config import KICAD_APP_PATH, system
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
def register_export_tools(mcp: FastMCP) -> None:
|
def register_export_tools(mcp: FastMCP) -> None:
|
||||||
@ -9,8 +9,8 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.layer_stackup import create_stackup_analyzer
|
from mckicad.utils.layer_stackup import create_stackup_analyzer
|
||||||
from kicad_mcp.utils.path_validator import validate_kicad_file
|
from mckicad.utils.path_validator import validate_kicad_file
|
||||||
|
|
||||||
|
|
||||||
def register_layer_tools(mcp: FastMCP) -> None:
|
def register_layer_tools(mcp: FastMCP) -> None:
|
||||||
@ -10,12 +10,12 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.model3d_analyzer import (
|
from mckicad.utils.model3d_analyzer import (
|
||||||
Model3DAnalyzer,
|
Model3DAnalyzer,
|
||||||
analyze_pcb_3d_models,
|
analyze_pcb_3d_models,
|
||||||
get_mechanical_constraints,
|
get_mechanical_constraints,
|
||||||
)
|
)
|
||||||
from kicad_mcp.utils.path_validator import validate_kicad_file
|
from mckicad.utils.path_validator import validate_kicad_file
|
||||||
|
|
||||||
|
|
||||||
def register_model3d_tools(mcp: FastMCP) -> None:
|
def register_model3d_tools(mcp: FastMCP) -> None:
|
||||||
@ -7,8 +7,8 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import analyze_netlist, extract_netlist
|
from mckicad.utils.netlist_parser import analyze_netlist, extract_netlist
|
||||||
|
|
||||||
|
|
||||||
def register_netlist_tools(mcp: FastMCP) -> None:
|
def register_netlist_tools(mcp: FastMCP) -> None:
|
||||||
@ -7,9 +7,9 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import analyze_netlist, extract_netlist
|
from mckicad.utils.netlist_parser import analyze_netlist, extract_netlist
|
||||||
from kicad_mcp.utils.pattern_recognition import (
|
from mckicad.utils.pattern_recognition import (
|
||||||
identify_amplifiers,
|
identify_amplifiers,
|
||||||
identify_digital_interfaces,
|
identify_digital_interfaces,
|
||||||
identify_filters,
|
identify_filters,
|
||||||
@ -13,9 +13,9 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.freerouting_engine import FreeRoutingEngine
|
from mckicad.utils.freerouting_engine import FreeRoutingEngine
|
||||||
from kicad_mcp.utils.ipc_client import check_kicad_availability
|
from mckicad.utils.ipc_client import check_kicad_availability
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -8,8 +8,8 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files, load_project_json
|
from mckicad.utils.file_utils import get_project_files, load_project_json
|
||||||
from kicad_mcp.utils.kicad_utils import find_kicad_projects, open_kicad_project
|
from mckicad.utils.kicad_utils import find_kicad_projects, open_kicad_project
|
||||||
|
|
||||||
# Get PID for logging
|
# Get PID for logging
|
||||||
# _PID = os.getpid()
|
# _PID = os.getpid()
|
||||||
@ -10,9 +10,9 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites
|
||||||
from kicad_mcp.utils.ipc_client import (
|
from mckicad.utils.ipc_client import (
|
||||||
check_kicad_availability,
|
check_kicad_availability,
|
||||||
kicad_ipc_session,
|
kicad_ipc_session,
|
||||||
)
|
)
|
||||||
@ -10,7 +10,7 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.symbol_library import create_symbol_analyzer
|
from mckicad.utils.symbol_library import create_symbol_analyzer
|
||||||
|
|
||||||
|
|
||||||
def register_symbol_tools(mcp: FastMCP) -> None:
|
def register_symbol_tools(mcp: FastMCP) -> None:
|
||||||
@ -11,8 +11,8 @@ from typing import Any
|
|||||||
|
|
||||||
from fastmcp import Context, FastMCP
|
from fastmcp import Context, FastMCP
|
||||||
|
|
||||||
from kicad_mcp.utils.boundary_validator import BoundaryValidator
|
from mckicad.utils.boundary_validator import BoundaryValidator
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
|
|
||||||
async def validate_project_boundaries(project_path: str = None) -> dict[str, Any]:
|
async def validate_project_boundaries(project_path: str = None) -> dict[str, Any]:
|
||||||
@ -10,8 +10,8 @@ from enum import Enum
|
|||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from kicad_mcp.utils.component_layout import ComponentLayoutManager, SchematicBounds
|
from mckicad.utils.component_layout import ComponentLayoutManager, SchematicBounds
|
||||||
from kicad_mcp.utils.coordinate_converter import CoordinateConverter, validate_position
|
from mckicad.utils.coordinate_converter import CoordinateConverter, validate_position
|
||||||
|
|
||||||
|
|
||||||
class ValidationSeverity(Enum):
|
class ValidationSeverity(Enum):
|
||||||
@ -15,11 +15,11 @@ from typing import Any
|
|||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
# Windows: Use APPDATA or LocalAppData
|
# Windows: Use APPDATA or LocalAppData
|
||||||
DRC_HISTORY_DIR = os.path.join(
|
DRC_HISTORY_DIR = os.path.join(
|
||||||
os.environ.get("APPDATA", os.path.expanduser("~")), "kicad_mcp", "drc_history"
|
os.environ.get("APPDATA", os.path.expanduser("~")), "mckicad", "drc_history"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# macOS/Linux: Use ~/.kicad_mcp/drc_history
|
# macOS/Linux: Use ~/.mckicad/drc_history
|
||||||
DRC_HISTORY_DIR = os.path.expanduser("~/.kicad_mcp/drc_history")
|
DRC_HISTORY_DIR = os.path.expanduser("~/.mckicad/drc_history")
|
||||||
|
|
||||||
|
|
||||||
def ensure_history_dir() -> None:
|
def ensure_history_dir() -> None:
|
||||||
@ -6,7 +6,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from kicad_mcp.utils.kicad_utils import get_project_name_from_path
|
from mckicad.utils.kicad_utils import get_project_name_from_path
|
||||||
|
|
||||||
|
|
||||||
def get_project_files(project_path: str) -> dict[str, str]:
|
def get_project_files(project_path: str) -> dict[str, str]:
|
||||||
@ -18,7 +18,7 @@ def get_project_files(project_path: str) -> dict[str, str]:
|
|||||||
Returns:
|
Returns:
|
||||||
Dictionary mapping file types to file paths
|
Dictionary mapping file types to file paths
|
||||||
"""
|
"""
|
||||||
from kicad_mcp.config import DATA_EXTENSIONS, KICAD_EXTENSIONS
|
from mckicad.config import DATA_EXTENSIONS, KICAD_EXTENSIONS
|
||||||
|
|
||||||
project_dir = os.path.dirname(project_path)
|
project_dir = os.path.dirname(project_path)
|
||||||
project_name = get_project_name_from_path(project_path)
|
project_name = get_project_name_from_path(project_path)
|
||||||
@ -19,7 +19,7 @@ from typing import Any
|
|||||||
|
|
||||||
from kipy.board_types import BoardLayer
|
from kipy.board_types import BoardLayer
|
||||||
|
|
||||||
from kicad_mcp.utils.ipc_client import kicad_ipc_session
|
from mckicad.utils.ipc_client import kicad_ipc_session
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -650,7 +650,7 @@ def check_routing_prerequisites() -> dict[str, Any]:
|
|||||||
|
|
||||||
# Check KiCad IPC API
|
# Check KiCad IPC API
|
||||||
try:
|
try:
|
||||||
from kicad_mcp.utils.ipc_client import check_kicad_availability
|
from mckicad.utils.ipc_client import check_kicad_availability
|
||||||
kicad_status = check_kicad_availability()
|
kicad_status = check_kicad_availability()
|
||||||
status["components"]["kicad_ipc"] = kicad_status
|
status["components"]["kicad_ipc"] = kicad_status
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -6,7 +6,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from kicad_mcp.config import system
|
from mckicad.config import system
|
||||||
|
|
||||||
|
|
||||||
def check_for_cli_api() -> bool:
|
def check_for_cli_api() -> bool:
|
||||||
@ -8,7 +8,7 @@ import subprocess
|
|||||||
import sys # Add sys import
|
import sys # Add sys import
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from kicad_mcp.config import (
|
from mckicad.config import (
|
||||||
ADDITIONAL_SEARCH_PATHS,
|
ADDITIONAL_SEARCH_PATHS,
|
||||||
KICAD_APP_PATH,
|
KICAD_APP_PATH,
|
||||||
KICAD_EXTENSIONS,
|
KICAD_EXTENSIONS,
|
||||||
@ -8,7 +8,7 @@ and ensure file operations are restricted to safe directories.
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from kicad_mcp.config import KICAD_EXTENSIONS
|
from mckicad.config import KICAD_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
class PathValidationError(Exception):
|
class PathValidationError(Exception):
|
||||||
@ -5,7 +5,7 @@ Circuit pattern recognition functions for KiCad schematics.
|
|||||||
import re
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from kicad_mcp.utils.component_utils import (
|
from mckicad.utils.component_utils import (
|
||||||
extract_frequency_from_value,
|
extract_frequency_from_value,
|
||||||
extract_voltage_from_regulator,
|
extract_voltage_from_regulator,
|
||||||
)
|
)
|
||||||
@ -1039,7 +1039,7 @@ def analyze_circuit_patterns(schematic_file: str) -> dict[str, Any]:
|
|||||||
Dictionary of identified patterns
|
Dictionary of identified patterns
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from kicad_mcp.utils.netlist_parser import parse_netlist_file
|
from mckicad.utils.netlist_parser import parse_netlist_file
|
||||||
|
|
||||||
# Parse netlist to get components and nets
|
# Parse netlist to get components and nets
|
||||||
netlist_data = parse_netlist_file(schematic_file)
|
netlist_data = parse_netlist_file(schematic_file)
|
||||||
@ -189,7 +189,7 @@ class SecureSubprocessRunner:
|
|||||||
raise SecureSubprocessError(f"Command failed: {e}") from e
|
raise SecureSubprocessError(f"Command failed: {e}") from e
|
||||||
|
|
||||||
def create_temp_file(
|
def create_temp_file(
|
||||||
self, suffix: str = "", prefix: str = "kicad_mcp_", content: str | None = None
|
self, suffix: str = "", prefix: str = "mckicad_", content: str | None = None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Create a temporary file within validated directories.
|
Create a temporary file within validated directories.
|
||||||
@ -288,7 +288,7 @@ async def run_kicad_command_async(
|
|||||||
|
|
||||||
|
|
||||||
def create_temp_file(
|
def create_temp_file(
|
||||||
suffix: str = "", prefix: str = "kicad_mcp_", content: str | None = None
|
suffix: str = "", prefix: str = "mckicad_", content: str | None = None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Convenience function to create temporary file."""
|
"""Convenience function to create temporary file."""
|
||||||
return get_subprocess_runner().create_temp_file(suffix, prefix, content)
|
return get_subprocess_runner().create_temp_file(suffix, prefix, content)
|
||||||
@ -3,7 +3,7 @@ requires = ["hatchling>=1.28.0"]
|
|||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "kicad-mcp"
|
name = "mckicad"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files"
|
description = "Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -50,12 +50,12 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "https://github.com/lamaalrajih/kicad-mcp"
|
"Homepage" = "https://github.com/lamaalrajih/mckicad"
|
||||||
"Bug Tracker" = "https://github.com/lamaalrajih/kicad-mcp/issues"
|
"Bug Tracker" = "https://github.com/lamaalrajih/mckicad/issues"
|
||||||
"Documentation" = "https://github.com/lamaalrajih/kicad-mcp#readme"
|
"Documentation" = "https://github.com/lamaalrajih/mckicad#readme"
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
kicad-mcp = "kicad_mcp.server:main"
|
mckicad = "mckicad.server:main"
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
@ -119,12 +119,12 @@ unfixable = [
|
|||||||
"D103", # Missing docstring in public function
|
"D103", # Missing docstring in public function
|
||||||
"SLF001", # Private member accessed
|
"SLF001", # Private member accessed
|
||||||
]
|
]
|
||||||
"kicad_mcp/config.py" = [
|
"mckicad/config.py" = [
|
||||||
"E501", # Long lines in config are ok
|
"E501", # Long lines in config are ok
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.isort]
|
[tool.ruff.lint.isort]
|
||||||
known-first-party = ["kicad_mcp"]
|
known-first-party = ["mckicad"]
|
||||||
force-sort-within-sections = true
|
force-sort-within-sections = true
|
||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
@ -164,7 +164,7 @@ minversion = "7.0"
|
|||||||
addopts = [
|
addopts = [
|
||||||
"--strict-markers",
|
"--strict-markers",
|
||||||
"--strict-config",
|
"--strict-config",
|
||||||
"--cov=kicad_mcp",
|
"--cov=mckicad",
|
||||||
"--cov-report=term-missing",
|
"--cov-report=term-missing",
|
||||||
"--cov-report=html:htmlcov",
|
"--cov-report=html:htmlcov",
|
||||||
"--cov-report=xml",
|
"--cov-report=xml",
|
||||||
@ -191,11 +191,11 @@ filterwarnings = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
source = ["kicad_mcp"]
|
source = ["mckicad"]
|
||||||
branch = true
|
branch = true
|
||||||
omit = [
|
omit = [
|
||||||
"tests/*",
|
"tests/*",
|
||||||
"kicad_mcp/__init__.py",
|
"mckicad/__init__.py",
|
||||||
"*/migrations/*",
|
"*/migrations/*",
|
||||||
"*/venv/*",
|
"*/venv/*",
|
||||||
"*/.venv/*",
|
"*/.venv/*",
|
||||||
@ -227,8 +227,8 @@ skips = ["*_test.py", "*/test_*.py"]
|
|||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["."]
|
where = ["."]
|
||||||
include = ["kicad_mcp*"]
|
include = ["mckicad*"]
|
||||||
exclude = ["tests*", "docs*"]
|
exclude = ["tests*", "docs*"]
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
"kicad_mcp" = ["prompts/*.txt", "resources/**/*.json"]
|
"mckicad" = ["prompts/*.txt", "resources/**/*.json"]
|
||||||
|
|||||||
@ -36,15 +36,15 @@ def main():
|
|||||||
exit_code = 0
|
exit_code = 0
|
||||||
|
|
||||||
# Run linting
|
# Run linting
|
||||||
exit_code |= run_command(["uv", "run", "ruff", "check", "kicad_mcp/", "tests/"], "Lint check")
|
exit_code |= run_command(["uv", "run", "ruff", "check", "mckicad/", "tests/"], "Lint check")
|
||||||
|
|
||||||
# Run formatting check
|
# Run formatting check
|
||||||
exit_code |= run_command(
|
exit_code |= run_command(
|
||||||
["uv", "run", "ruff", "format", "--check", "kicad_mcp/", "tests/"], "Format check"
|
["uv", "run", "ruff", "format", "--check", "mckicad/", "tests/"], "Format check"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run type checking
|
# Run type checking
|
||||||
exit_code |= run_command(["uv", "run", "mypy", "kicad_mcp/"], "Type check")
|
exit_code |= run_command(["uv", "run", "mypy", "mckicad/"], "Type check")
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
exit_code |= run_command(["uv", "run", "python", "-m", "pytest", "tests/", "-v"], "Unit tests")
|
exit_code |= run_command(["uv", "run", "python", "-m", "pytest", "tests/", "-v"], "Unit tests")
|
||||||
|
|||||||
@ -8,13 +8,13 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
from kicad_mcp.utils.ipc_client import KiCadIPCClient, check_kicad_availability
|
from mckicad.utils.ipc_client import KiCadIPCClient, check_kicad_availability
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist
|
from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
|
||||||
from kicad_mcp.tools.validation_tools import validate_project_boundaries
|
from mckicad.tools.validation_tools import validate_project_boundaries
|
||||||
|
|
||||||
# Our new demo project
|
# Our new demo project
|
||||||
PROJECT_PATH = "/home/rpm/claude/Demo_PCB_Project/Smart_Sensor_Board.kicad_pro"
|
PROJECT_PATH = "/home/rpm/claude/Demo_PCB_Project/Smart_Sensor_Board.kicad_pro"
|
||||||
|
|||||||
@ -10,13 +10,13 @@ from basic project analysis to real-time board manipulation and routing.
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
from kicad_mcp.utils.ipc_client import KiCadIPCClient
|
from mckicad.utils.ipc_client import KiCadIPCClient
|
||||||
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import check_routing_prerequisites
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist
|
from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
|
||||||
|
|
||||||
# Test project path
|
# Test project path
|
||||||
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
||||||
|
|||||||
@ -9,13 +9,13 @@ This demonstrates the complete, fully-functional EDA automation platform.
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
from kicad_mcp.utils.ipc_client import KiCadIPCClient
|
from mckicad.utils.ipc_client import KiCadIPCClient
|
||||||
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import check_routing_prerequisites
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist
|
from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
|
||||||
|
|
||||||
# Test project path
|
# Test project path
|
||||||
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
||||||
|
|||||||
@ -12,14 +12,14 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
from kicad_mcp.utils.ipc_client import KiCadIPCClient
|
from mckicad.utils.ipc_client import KiCadIPCClient
|
||||||
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import check_routing_prerequisites
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist
|
from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
|
||||||
from kicad_mcp.server import create_server
|
from mckicad.server import create_server
|
||||||
|
|
||||||
# Test project
|
# Test project
|
||||||
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
||||||
|
|||||||
@ -10,11 +10,11 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
from kicad_mcp.utils.ipc_client import KiCadIPCClient
|
from mckicad.utils.ipc_client import KiCadIPCClient
|
||||||
from kicad_mcp.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites
|
||||||
|
|
||||||
def test_routing_prerequisites():
|
def test_routing_prerequisites():
|
||||||
"""Test routing prerequisites and components."""
|
"""Test routing prerequisites and components."""
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"
|
||||||
|
|||||||
@ -12,14 +12,14 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import check_routing_prerequisites
|
||||||
from kicad_mcp.utils.ipc_client import check_kicad_availability
|
from mckicad.utils.ipc_client import check_kicad_availability
|
||||||
from kicad_mcp.tools.analysis_tools import register_analysis_tools
|
from mckicad.tools.analysis_tools import register_analysis_tools
|
||||||
from kicad_mcp.tools.routing_tools import register_routing_tools
|
from mckicad.tools.routing_tools import register_routing_tools
|
||||||
from kicad_mcp.tools.ai_tools import register_ai_tools
|
from mckicad.tools.ai_tools import register_ai_tools
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@ -88,7 +88,7 @@ def test_project_validation():
|
|||||||
logger.info("Testing project validation...")
|
logger.info("Testing project validation...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
|
|
||||||
if not Path(PROJECT_PATH).exists():
|
if not Path(PROJECT_PATH).exists():
|
||||||
logger.error(f"Test project not found: {PROJECT_PATH}")
|
logger.error(f"Test project not found: {PROJECT_PATH}")
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import sys
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the kicad_mcp module to path
|
# Add the mckicad module to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
# Import our server and tools
|
# Import our server and tools
|
||||||
from kicad_mcp.server import create_server
|
from mckicad.server import create_server
|
||||||
|
|
||||||
def test_server_initialization():
|
def test_server_initialization():
|
||||||
"""Test MCP server initialization and tool registration."""
|
"""Test MCP server initialization and tool registration."""
|
||||||
@ -40,13 +40,13 @@ def test_tool_registration():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Import and test tool registration functions
|
# Import and test tool registration functions
|
||||||
from kicad_mcp.tools.analysis_tools import register_analysis_tools
|
from mckicad.tools.analysis_tools import register_analysis_tools
|
||||||
from kicad_mcp.tools.project_tools import register_project_tools
|
from mckicad.tools.project_tools import register_project_tools
|
||||||
from kicad_mcp.tools.drc_tools import register_drc_tools
|
from mckicad.tools.drc_tools import register_drc_tools
|
||||||
from kicad_mcp.tools.bom_tools import register_bom_tools
|
from mckicad.tools.bom_tools import register_bom_tools
|
||||||
from kicad_mcp.tools.netlist_tools import register_netlist_tools
|
from mckicad.tools.netlist_tools import register_netlist_tools
|
||||||
from kicad_mcp.tools.pattern_tools import register_pattern_tools
|
from mckicad.tools.pattern_tools import register_pattern_tools
|
||||||
from kicad_mcp.tools.export_tools import register_export_tools
|
from mckicad.tools.export_tools import register_export_tools
|
||||||
|
|
||||||
# Test that registration functions exist
|
# Test that registration functions exist
|
||||||
registration_functions = [
|
registration_functions = [
|
||||||
@ -80,11 +80,11 @@ def test_resource_registration():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Import resource registration functions
|
# Import resource registration functions
|
||||||
from kicad_mcp.resources.projects import register_project_resources
|
from mckicad.resources.projects import register_project_resources
|
||||||
from kicad_mcp.resources.files import register_file_resources
|
from mckicad.resources.files import register_file_resources
|
||||||
from kicad_mcp.resources.drc_resources import register_drc_resources
|
from mckicad.resources.drc_resources import register_drc_resources
|
||||||
from kicad_mcp.resources.bom_resources import register_bom_resources
|
from mckicad.resources.bom_resources import register_bom_resources
|
||||||
from kicad_mcp.resources.netlist_resources import register_netlist_resources
|
from mckicad.resources.netlist_resources import register_netlist_resources
|
||||||
|
|
||||||
resource_functions = [
|
resource_functions = [
|
||||||
("project_resources", register_project_resources),
|
("project_resources", register_project_resources),
|
||||||
@ -115,10 +115,10 @@ def test_prompt_registration():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Import prompt registration functions
|
# Import prompt registration functions
|
||||||
from kicad_mcp.prompts.templates import register_prompts
|
from mckicad.prompts.templates import register_prompts
|
||||||
from kicad_mcp.prompts.drc_prompt import register_drc_prompts
|
from mckicad.prompts.drc_prompt import register_drc_prompts
|
||||||
from kicad_mcp.prompts.bom_prompts import register_bom_prompts
|
from mckicad.prompts.bom_prompts import register_bom_prompts
|
||||||
from kicad_mcp.prompts.pattern_prompts import register_pattern_prompts
|
from mckicad.prompts.pattern_prompts import register_pattern_prompts
|
||||||
|
|
||||||
prompt_functions = [
|
prompt_functions = [
|
||||||
("templates", register_prompts),
|
("templates", register_prompts),
|
||||||
@ -148,10 +148,10 @@ def test_core_functionality():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Test key utility imports
|
# Test key utility imports
|
||||||
from kicad_mcp.utils.file_utils import get_project_files
|
from mckicad.utils.file_utils import get_project_files
|
||||||
from kicad_mcp.utils.ipc_client import KiCadIPCClient, check_kicad_availability
|
from mckicad.utils.ipc_client import KiCadIPCClient, check_kicad_availability
|
||||||
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites
|
from mckicad.utils.freerouting_engine import check_routing_prerequisites
|
||||||
from kicad_mcp.utils.netlist_parser import extract_netlist
|
from mckicad.utils.netlist_parser import extract_netlist
|
||||||
|
|
||||||
print(f"📦 Core Utilities Available:")
|
print(f"📦 Core Utilities Available:")
|
||||||
print(f" ✅ file_utils: Project file management")
|
print(f" ✅ file_utils: Project file management")
|
||||||
@ -189,9 +189,9 @@ def test_server_completeness():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Check that the main server creation works
|
# Check that the main server creation works
|
||||||
from kicad_mcp.server import create_server
|
from mckicad.server import create_server
|
||||||
from kicad_mcp.config import KICAD_CLI_TIMEOUT
|
from mckicad.config import KICAD_CLI_TIMEOUT
|
||||||
from kicad_mcp.context import KiCadAppContext
|
from mckicad.context import KiCadAppContext
|
||||||
|
|
||||||
print(f"📊 Server Components:")
|
print(f"📊 Server Components:")
|
||||||
print(f" ✅ create_server(): Main entry point")
|
print(f" ✅ create_server(): Main entry point")
|
||||||
@ -199,7 +199,7 @@ def test_server_completeness():
|
|||||||
print(f" ✅ Context management: {KiCadAppContext.__name__}")
|
print(f" ✅ Context management: {KiCadAppContext.__name__}")
|
||||||
|
|
||||||
# Verify key constants and configurations
|
# Verify key constants and configurations
|
||||||
from kicad_mcp import config
|
from mckicad import config
|
||||||
|
|
||||||
config_items = [
|
config_items = [
|
||||||
'KICAD_CLI_TIMEOUT', 'DEFAULT_KICAD_PATHS',
|
'KICAD_CLI_TIMEOUT', 'DEFAULT_KICAD_PATHS',
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Tests for the kicad_mcp.config module.
|
Tests for the mckicad.config module.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@ -11,7 +11,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_system_detection(self):
|
def test_system_detection(self):
|
||||||
"""Test that system is properly detected."""
|
"""Test that system is properly detected."""
|
||||||
from kicad_mcp.config import system
|
from mckicad.config import system
|
||||||
|
|
||||||
assert system in ['Darwin', 'Windows', 'Linux'] or isinstance(system, str)
|
assert system in ['Darwin', 'Windows', 'Linux'] or isinstance(system, str)
|
||||||
assert system == platform.system()
|
assert system == platform.system()
|
||||||
@ -22,10 +22,10 @@ class TestConfigModule:
|
|||||||
# Need to reload the config module after patching
|
# Need to reload the config module after patching
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
from kicad_mcp.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
|
from mckicad.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
|
||||||
|
|
||||||
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
|
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
|
||||||
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
|
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
|
||||||
@ -36,10 +36,10 @@ class TestConfigModule:
|
|||||||
with patch('platform.system', return_value='Windows'):
|
with patch('platform.system', return_value='Windows'):
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
from kicad_mcp.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
|
from mckicad.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
|
||||||
|
|
||||||
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
|
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
|
||||||
assert KICAD_APP_PATH == r"C:\Program Files\KiCad"
|
assert KICAD_APP_PATH == r"C:\Program Files\KiCad"
|
||||||
@ -50,10 +50,10 @@ class TestConfigModule:
|
|||||||
with patch('platform.system', return_value='Linux'):
|
with patch('platform.system', return_value='Linux'):
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
from kicad_mcp.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
|
from mckicad.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
|
||||||
|
|
||||||
assert os.path.expanduser("~/KiCad") == KICAD_USER_DIR
|
assert os.path.expanduser("~/KiCad") == KICAD_USER_DIR
|
||||||
assert KICAD_APP_PATH == "/usr/share/kicad"
|
assert KICAD_APP_PATH == "/usr/share/kicad"
|
||||||
@ -64,17 +64,17 @@ class TestConfigModule:
|
|||||||
with patch('platform.system', return_value='FreeBSD'):
|
with patch('platform.system', return_value='FreeBSD'):
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
from kicad_mcp.config import KICAD_APP_PATH, KICAD_USER_DIR
|
from mckicad.config import KICAD_APP_PATH, KICAD_USER_DIR
|
||||||
|
|
||||||
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
|
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
|
||||||
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
|
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
|
||||||
|
|
||||||
def test_kicad_extensions(self):
|
def test_kicad_extensions(self):
|
||||||
"""Test KiCad file extension mappings."""
|
"""Test KiCad file extension mappings."""
|
||||||
from kicad_mcp.config import KICAD_EXTENSIONS
|
from mckicad.config import KICAD_EXTENSIONS
|
||||||
|
|
||||||
expected_keys = ["project", "pcb", "schematic", "design_rules",
|
expected_keys = ["project", "pcb", "schematic", "design_rules",
|
||||||
"worksheet", "footprint", "netlist", "kibot_config"]
|
"worksheet", "footprint", "netlist", "kibot_config"]
|
||||||
@ -86,7 +86,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_data_extensions(self):
|
def test_data_extensions(self):
|
||||||
"""Test data file extensions list."""
|
"""Test data file extensions list."""
|
||||||
from kicad_mcp.config import DATA_EXTENSIONS
|
from mckicad.config import DATA_EXTENSIONS
|
||||||
|
|
||||||
assert isinstance(DATA_EXTENSIONS, list)
|
assert isinstance(DATA_EXTENSIONS, list)
|
||||||
assert len(DATA_EXTENSIONS) > 0
|
assert len(DATA_EXTENSIONS) > 0
|
||||||
@ -97,7 +97,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_circuit_defaults(self):
|
def test_circuit_defaults(self):
|
||||||
"""Test circuit default parameters."""
|
"""Test circuit default parameters."""
|
||||||
from kicad_mcp.config import CIRCUIT_DEFAULTS
|
from mckicad.config import CIRCUIT_DEFAULTS
|
||||||
|
|
||||||
required_keys = ["grid_spacing", "component_spacing", "wire_width",
|
required_keys = ["grid_spacing", "component_spacing", "wire_width",
|
||||||
"text_size", "pin_length"]
|
"text_size", "pin_length"]
|
||||||
@ -112,7 +112,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_common_libraries_structure(self):
|
def test_common_libraries_structure(self):
|
||||||
"""Test common libraries configuration structure."""
|
"""Test common libraries configuration structure."""
|
||||||
from kicad_mcp.config import COMMON_LIBRARIES
|
from mckicad.config import COMMON_LIBRARIES
|
||||||
|
|
||||||
expected_categories = ["basic", "power", "connectors"]
|
expected_categories = ["basic", "power", "connectors"]
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_default_footprints_structure(self):
|
def test_default_footprints_structure(self):
|
||||||
"""Test default footprints configuration structure."""
|
"""Test default footprints configuration structure."""
|
||||||
from kicad_mcp.config import DEFAULT_FOOTPRINTS
|
from mckicad.config import DEFAULT_FOOTPRINTS
|
||||||
|
|
||||||
# Test that at least some common components are present
|
# Test that at least some common components are present
|
||||||
common_components = ["R", "C", "LED", "D"]
|
common_components = ["R", "C", "LED", "D"]
|
||||||
@ -145,7 +145,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_timeout_constants(self):
|
def test_timeout_constants(self):
|
||||||
"""Test timeout constants are reasonable values."""
|
"""Test timeout constants are reasonable values."""
|
||||||
from kicad_mcp.config import TIMEOUT_CONSTANTS
|
from mckicad.config import TIMEOUT_CONSTANTS
|
||||||
|
|
||||||
required_keys = ["kicad_cli_version_check", "kicad_cli_export",
|
required_keys = ["kicad_cli_version_check", "kicad_cli_export",
|
||||||
"application_open", "subprocess_default"]
|
"application_open", "subprocess_default"]
|
||||||
@ -158,7 +158,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_progress_constants(self):
|
def test_progress_constants(self):
|
||||||
"""Test progress constants are valid percentages."""
|
"""Test progress constants are valid percentages."""
|
||||||
from kicad_mcp.config import PROGRESS_CONSTANTS
|
from mckicad.config import PROGRESS_CONSTANTS
|
||||||
|
|
||||||
required_keys = ["start", "detection", "setup", "processing",
|
required_keys = ["start", "detection", "setup", "processing",
|
||||||
"finishing", "validation", "complete"]
|
"finishing", "validation", "complete"]
|
||||||
@ -171,7 +171,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_display_constants(self):
|
def test_display_constants(self):
|
||||||
"""Test display constants are reasonable values."""
|
"""Test display constants are reasonable values."""
|
||||||
from kicad_mcp.config import DISPLAY_CONSTANTS
|
from mckicad.config import DISPLAY_CONSTANTS
|
||||||
|
|
||||||
assert "bom_preview_limit" in DISPLAY_CONSTANTS
|
assert "bom_preview_limit" in DISPLAY_CONSTANTS
|
||||||
limit = DISPLAY_CONSTANTS["bom_preview_limit"]
|
limit = DISPLAY_CONSTANTS["bom_preview_limit"]
|
||||||
@ -183,11 +183,11 @@ class TestConfigModule:
|
|||||||
with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": ""}):
|
with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": ""}):
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
# Should still have default locations if they exist
|
# Should still have default locations if they exist
|
||||||
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS
|
from mckicad.config import ADDITIONAL_SEARCH_PATHS
|
||||||
assert isinstance(ADDITIONAL_SEARCH_PATHS, list)
|
assert isinstance(ADDITIONAL_SEARCH_PATHS, list)
|
||||||
|
|
||||||
def test_nonexistent_search_paths_ignored(self):
|
def test_nonexistent_search_paths_ignored(self):
|
||||||
@ -196,10 +196,10 @@ class TestConfigModule:
|
|||||||
patch('os.path.exists', return_value=False):
|
patch('os.path.exists', return_value=False):
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS
|
from mckicad.config import ADDITIONAL_SEARCH_PATHS
|
||||||
|
|
||||||
# Should not contain the nonexistent paths
|
# Should not contain the nonexistent paths
|
||||||
assert "/nonexistent/path1" not in ADDITIONAL_SEARCH_PATHS
|
assert "/nonexistent/path1" not in ADDITIONAL_SEARCH_PATHS
|
||||||
@ -213,10 +213,10 @@ class TestConfigModule:
|
|||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import kicad_mcp.config
|
import mckicad.config
|
||||||
importlib.reload(kicad_mcp.config)
|
importlib.reload(mckicad.config)
|
||||||
|
|
||||||
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS
|
from mckicad.config import ADDITIONAL_SEARCH_PATHS
|
||||||
|
|
||||||
# Should contain expanded paths
|
# Should contain expanded paths
|
||||||
assert "/home/user/test_path1" in ADDITIONAL_SEARCH_PATHS
|
assert "/home/user/test_path1" in ADDITIONAL_SEARCH_PATHS
|
||||||
@ -224,7 +224,7 @@ class TestConfigModule:
|
|||||||
|
|
||||||
def test_default_project_locations_expanded(self):
|
def test_default_project_locations_expanded(self):
|
||||||
"""Test that default project locations are properly expanded."""
|
"""Test that default project locations are properly expanded."""
|
||||||
from kicad_mcp.config import DEFAULT_PROJECT_LOCATIONS
|
from mckicad.config import DEFAULT_PROJECT_LOCATIONS
|
||||||
|
|
||||||
assert isinstance(DEFAULT_PROJECT_LOCATIONS, list)
|
assert isinstance(DEFAULT_PROJECT_LOCATIONS, list)
|
||||||
assert len(DEFAULT_PROJECT_LOCATIONS) > 0
|
assert len(DEFAULT_PROJECT_LOCATIONS) > 0
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
Tests for the kicad_mcp.context module.
|
Tests for the mckicad.context module.
|
||||||
"""
|
"""
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from kicad_mcp.context import KiCadAppContext, kicad_lifespan
|
from mckicad.context import KiCadAppContext, kicad_lifespan
|
||||||
|
|
||||||
|
|
||||||
class TestKiCadAppContext:
|
class TestKiCadAppContext:
|
||||||
@ -62,7 +62,7 @@ class TestKiCadLifespan:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_lifespan_basic_flow(self, mock_server):
|
async def test_lifespan_basic_flow(self, mock_server):
|
||||||
"""Test basic lifespan flow with successful initialization and cleanup."""
|
"""Test basic lifespan flow with successful initialization and cleanup."""
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging:
|
with patch('mckicad.context.logging') as mock_logging:
|
||||||
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
||||||
# Check context is properly initialized
|
# Check context is properly initialized
|
||||||
assert isinstance(context, KiCadAppContext)
|
assert isinstance(context, KiCadAppContext)
|
||||||
@ -103,7 +103,7 @@ class TestKiCadLifespan:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_lifespan_cache_cleanup(self, mock_server):
|
async def test_lifespan_cache_cleanup(self, mock_server):
|
||||||
"""Test that cache is properly cleared on shutdown."""
|
"""Test that cache is properly cleared on shutdown."""
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging:
|
with patch('mckicad.context.logging') as mock_logging:
|
||||||
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
||||||
# Populate cache
|
# Populate cache
|
||||||
context.cache["test1"] = "value1"
|
context.cache["test1"] = "value1"
|
||||||
@ -116,7 +116,7 @@ class TestKiCadLifespan:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_lifespan_exception_handling(self, mock_server):
|
async def test_lifespan_exception_handling(self, mock_server):
|
||||||
"""Test that cleanup happens even if an exception occurs."""
|
"""Test that cleanup happens even if an exception occurs."""
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging:
|
with patch('mckicad.context.logging') as mock_logging:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
||||||
context.cache["test"] = "value"
|
context.cache["test"] = "value"
|
||||||
@ -130,8 +130,8 @@ class TestKiCadLifespan:
|
|||||||
@pytest.mark.skip(reason="Mock setup complexity - temp dir cleanup not critical")
|
@pytest.mark.skip(reason="Mock setup complexity - temp dir cleanup not critical")
|
||||||
async def test_lifespan_temp_dir_cleanup(self, mock_server):
|
async def test_lifespan_temp_dir_cleanup(self, mock_server):
|
||||||
"""Test temporary directory cleanup functionality."""
|
"""Test temporary directory cleanup functionality."""
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging, \
|
with patch('mckicad.context.logging') as mock_logging, \
|
||||||
patch('kicad_mcp.context.shutil') as mock_shutil:
|
patch('mckicad.context.shutil') as mock_shutil:
|
||||||
|
|
||||||
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
||||||
# The current implementation has an empty created_temp_dirs list
|
# The current implementation has an empty created_temp_dirs list
|
||||||
@ -145,8 +145,8 @@ class TestKiCadLifespan:
|
|||||||
async def test_lifespan_temp_dir_cleanup_error_handling(self, mock_server):
|
async def test_lifespan_temp_dir_cleanup_error_handling(self, mock_server):
|
||||||
"""Test error handling in temp directory cleanup."""
|
"""Test error handling in temp directory cleanup."""
|
||||||
# Mock the created_temp_dirs to have some directories for testing
|
# Mock the created_temp_dirs to have some directories for testing
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging, \
|
with patch('mckicad.context.logging') as mock_logging, \
|
||||||
patch('kicad_mcp.context.shutil') as mock_shutil:
|
patch('mckicad.context.shutil') as mock_shutil:
|
||||||
|
|
||||||
# Patch the created_temp_dirs list in the function scope
|
# Patch the created_temp_dirs list in the function scope
|
||||||
original_lifespan = kicad_lifespan
|
original_lifespan = kicad_lifespan
|
||||||
@ -183,7 +183,7 @@ class TestKiCadLifespan:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_lifespan_logging_messages(self, mock_server):
|
async def test_lifespan_logging_messages(self, mock_server):
|
||||||
"""Test specific logging messages are called correctly."""
|
"""Test specific logging messages are called correctly."""
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging:
|
with patch('mckicad.context.logging') as mock_logging:
|
||||||
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
|
||||||
context.cache["test"] = "data"
|
context.cache["test"] = "data"
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ class TestKiCadLifespan:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_lifespan_empty_cache_no_cleanup_log(self, mock_server):
|
async def test_lifespan_empty_cache_no_cleanup_log(self, mock_server):
|
||||||
"""Test that empty cache doesn't log cleanup message."""
|
"""Test that empty cache doesn't log cleanup message."""
|
||||||
with patch('kicad_mcp.context.logging') as mock_logging:
|
with patch('mckicad.context.logging') as mock_logging:
|
||||||
async with kicad_lifespan(mock_server, kicad_modules_available=False) as context:
|
async with kicad_lifespan(mock_server, kicad_modules_available=False) as context:
|
||||||
# Don't add anything to cache
|
# Don't add anything to cache
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Tests for the kicad_mcp.server module.
|
Tests for the mckicad.server module.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
@ -7,7 +7,7 @@ from unittest.mock import Mock, call, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from kicad_mcp.server import (
|
from mckicad.server import (
|
||||||
add_cleanup_handler,
|
add_cleanup_handler,
|
||||||
create_server,
|
create_server,
|
||||||
main,
|
main,
|
||||||
@ -23,7 +23,7 @@ class TestCleanupHandlers:
|
|||||||
|
|
||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
"""Reset cleanup handlers before each test."""
|
"""Reset cleanup handlers before each test."""
|
||||||
from kicad_mcp.server import cleanup_handlers
|
from mckicad.server import cleanup_handlers
|
||||||
cleanup_handlers.clear()
|
cleanup_handlers.clear()
|
||||||
|
|
||||||
def test_add_cleanup_handler(self):
|
def test_add_cleanup_handler(self):
|
||||||
@ -33,7 +33,7 @@ class TestCleanupHandlers:
|
|||||||
|
|
||||||
add_cleanup_handler(dummy_handler)
|
add_cleanup_handler(dummy_handler)
|
||||||
|
|
||||||
from kicad_mcp.server import cleanup_handlers
|
from mckicad.server import cleanup_handlers
|
||||||
assert dummy_handler in cleanup_handlers
|
assert dummy_handler in cleanup_handlers
|
||||||
|
|
||||||
def test_add_multiple_cleanup_handlers(self):
|
def test_add_multiple_cleanup_handlers(self):
|
||||||
@ -47,12 +47,12 @@ class TestCleanupHandlers:
|
|||||||
add_cleanup_handler(handler1)
|
add_cleanup_handler(handler1)
|
||||||
add_cleanup_handler(handler2)
|
add_cleanup_handler(handler2)
|
||||||
|
|
||||||
from kicad_mcp.server import cleanup_handlers
|
from mckicad.server import cleanup_handlers
|
||||||
assert handler1 in cleanup_handlers
|
assert handler1 in cleanup_handlers
|
||||||
assert handler2 in cleanup_handlers
|
assert handler2 in cleanup_handlers
|
||||||
assert len(cleanup_handlers) == 2
|
assert len(cleanup_handlers) == 2
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_run_cleanup_handlers_success(self, mock_logging):
|
def test_run_cleanup_handlers_success(self, mock_logging):
|
||||||
"""Test successful execution of cleanup handlers."""
|
"""Test successful execution of cleanup handlers."""
|
||||||
handler1 = Mock()
|
handler1 = Mock()
|
||||||
@ -69,7 +69,7 @@ class TestCleanupHandlers:
|
|||||||
handler2.assert_called_once()
|
handler2.assert_called_once()
|
||||||
mock_logging.info.assert_any_call("Running cleanup handlers...")
|
mock_logging.info.assert_any_call("Running cleanup handlers...")
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
@pytest.mark.skip(reason="Mock handler execution complexity - exception handling works in practice")
|
@pytest.mark.skip(reason="Mock handler execution complexity - exception handling works in practice")
|
||||||
def test_run_cleanup_handlers_with_exception(self, mock_logging):
|
def test_run_cleanup_handlers_with_exception(self, mock_logging):
|
||||||
"""Test cleanup handlers with exceptions."""
|
"""Test cleanup handlers with exceptions."""
|
||||||
@ -91,7 +91,7 @@ class TestCleanupHandlers:
|
|||||||
# Should still log success for working handler
|
# Should still log success for working handler
|
||||||
mock_logging.info.assert_any_call("Cleanup handler working_handler completed successfully")
|
mock_logging.info.assert_any_call("Cleanup handler working_handler completed successfully")
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
@pytest.mark.skip(reason="Global state management complexity - double execution prevention works")
|
@pytest.mark.skip(reason="Global state management complexity - double execution prevention works")
|
||||||
def test_run_cleanup_handlers_prevents_double_execution(self, mock_logging):
|
def test_run_cleanup_handlers_prevents_double_execution(self, mock_logging):
|
||||||
"""Test that cleanup handlers don't run twice."""
|
"""Test that cleanup handlers don't run twice."""
|
||||||
@ -113,17 +113,17 @@ class TestServerShutdown:
|
|||||||
|
|
||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
"""Reset server instance before each test."""
|
"""Reset server instance before each test."""
|
||||||
import kicad_mcp.server
|
import mckicad.server
|
||||||
kicad_mcp.server._server_instance = None
|
mckicad.server._server_instance = None
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_shutdown_server_with_instance(self, mock_logging):
|
def test_shutdown_server_with_instance(self, mock_logging):
|
||||||
"""Test shutting down server when instance exists."""
|
"""Test shutting down server when instance exists."""
|
||||||
import kicad_mcp.server
|
import mckicad.server
|
||||||
|
|
||||||
# Set up mock server instance
|
# Set up mock server instance
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
kicad_mcp.server._server_instance = mock_server
|
mckicad.server._server_instance = mock_server
|
||||||
|
|
||||||
shutdown_server()
|
shutdown_server()
|
||||||
|
|
||||||
@ -131,9 +131,9 @@ class TestServerShutdown:
|
|||||||
mock_logging.info.assert_any_call("KiCad MCP server shutdown complete")
|
mock_logging.info.assert_any_call("KiCad MCP server shutdown complete")
|
||||||
|
|
||||||
# Server instance should be cleared
|
# Server instance should be cleared
|
||||||
assert kicad_mcp.server._server_instance is None
|
assert mckicad.server._server_instance is None
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_shutdown_server_no_instance(self, mock_logging):
|
def test_shutdown_server_no_instance(self, mock_logging):
|
||||||
"""Test shutting down server when no instance exists."""
|
"""Test shutting down server when no instance exists."""
|
||||||
shutdown_server()
|
shutdown_server()
|
||||||
@ -145,8 +145,8 @@ class TestServerShutdown:
|
|||||||
class TestSignalHandlers:
|
class TestSignalHandlers:
|
||||||
"""Test signal handler registration."""
|
"""Test signal handler registration."""
|
||||||
|
|
||||||
@patch('kicad_mcp.server.signal.signal')
|
@patch('mckicad.server.signal.signal')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_register_signal_handlers_success(self, mock_logging, mock_signal):
|
def test_register_signal_handlers_success(self, mock_logging, mock_signal):
|
||||||
"""Test successful signal handler registration."""
|
"""Test successful signal handler registration."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
@ -163,8 +163,8 @@ class TestSignalHandlers:
|
|||||||
mock_logging.info.assert_any_call("Registered handler for signal 2") # SIGINT
|
mock_logging.info.assert_any_call("Registered handler for signal 2") # SIGINT
|
||||||
mock_logging.info.assert_any_call("Registered handler for signal 15") # SIGTERM
|
mock_logging.info.assert_any_call("Registered handler for signal 15") # SIGTERM
|
||||||
|
|
||||||
@patch('kicad_mcp.server.signal.signal')
|
@patch('mckicad.server.signal.signal')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_register_signal_handlers_failure(self, mock_logging, mock_signal):
|
def test_register_signal_handlers_failure(self, mock_logging, mock_signal):
|
||||||
"""Test signal handler registration failure."""
|
"""Test signal handler registration failure."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
@ -175,15 +175,15 @@ class TestSignalHandlers:
|
|||||||
# Should log errors for failed registrations
|
# Should log errors for failed registrations
|
||||||
mock_logging.error.assert_called()
|
mock_logging.error.assert_called()
|
||||||
|
|
||||||
@patch('kicad_mcp.server.run_cleanup_handlers')
|
@patch('mckicad.server.run_cleanup_handlers')
|
||||||
@patch('kicad_mcp.server.shutdown_server')
|
@patch('mckicad.server.shutdown_server')
|
||||||
@patch('kicad_mcp.server.os._exit')
|
@patch('mckicad.server.os._exit')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_signal_handler_execution(self, mock_logging, mock_exit, mock_shutdown, mock_cleanup):
|
def test_signal_handler_execution(self, mock_logging, mock_exit, mock_shutdown, mock_cleanup):
|
||||||
"""Test that signal handler executes cleanup and shutdown."""
|
"""Test that signal handler executes cleanup and shutdown."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
|
|
||||||
with patch('kicad_mcp.server.signal.signal') as mock_signal:
|
with patch('mckicad.server.signal.signal') as mock_signal:
|
||||||
register_signal_handlers(mock_server)
|
register_signal_handlers(mock_server)
|
||||||
|
|
||||||
# Get the registered handler function
|
# Get the registered handler function
|
||||||
@ -202,11 +202,11 @@ class TestSignalHandlers:
|
|||||||
class TestCreateServer:
|
class TestCreateServer:
|
||||||
"""Test server creation and configuration."""
|
"""Test server creation and configuration."""
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
@patch('kicad_mcp.server.FastMCP')
|
@patch('mckicad.server.FastMCP')
|
||||||
@patch('kicad_mcp.server.register_signal_handlers')
|
@patch('mckicad.server.register_signal_handlers')
|
||||||
@patch('kicad_mcp.server.atexit.register')
|
@patch('mckicad.server.atexit.register')
|
||||||
@patch('kicad_mcp.server.add_cleanup_handler')
|
@patch('mckicad.server.add_cleanup_handler')
|
||||||
def test_create_server_basic(self, mock_add_cleanup, mock_atexit, mock_register_signals, mock_fastmcp, mock_logging):
|
def test_create_server_basic(self, mock_add_cleanup, mock_atexit, mock_register_signals, mock_fastmcp, mock_logging):
|
||||||
"""Test basic server creation."""
|
"""Test basic server creation."""
|
||||||
mock_server_instance = Mock()
|
mock_server_instance = Mock()
|
||||||
@ -227,16 +227,16 @@ class TestCreateServer:
|
|||||||
|
|
||||||
assert server == mock_server_instance
|
assert server == mock_server_instance
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
@patch('kicad_mcp.server.FastMCP')
|
@patch('mckicad.server.FastMCP')
|
||||||
def test_create_server_logging(self, mock_fastmcp, mock_logging):
|
def test_create_server_logging(self, mock_fastmcp, mock_logging):
|
||||||
"""Test server creation logging."""
|
"""Test server creation logging."""
|
||||||
mock_server_instance = Mock()
|
mock_server_instance = Mock()
|
||||||
mock_fastmcp.return_value = mock_server_instance
|
mock_fastmcp.return_value = mock_server_instance
|
||||||
|
|
||||||
with patch('kicad_mcp.server.register_signal_handlers'), \
|
with patch('mckicad.server.register_signal_handlers'), \
|
||||||
patch('kicad_mcp.server.atexit.register'), \
|
patch('mckicad.server.atexit.register'), \
|
||||||
patch('kicad_mcp.server.add_cleanup_handler'):
|
patch('mckicad.server.add_cleanup_handler'):
|
||||||
|
|
||||||
create_server()
|
create_server()
|
||||||
|
|
||||||
@ -254,9 +254,9 @@ class TestCreateServer:
|
|||||||
for expected_call in expected_log_calls:
|
for expected_call in expected_log_calls:
|
||||||
mock_logging.info.assert_any_call(expected_call)
|
mock_logging.info.assert_any_call(expected_call)
|
||||||
|
|
||||||
@patch('kicad_mcp.server.get_temp_dirs')
|
@patch('mckicad.server.get_temp_dirs')
|
||||||
@patch('kicad_mcp.server.os.path.exists')
|
@patch('mckicad.server.os.path.exists')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
@pytest.mark.skip(reason="Complex mock setup for temp dir cleanup - functionality works in practice")
|
@pytest.mark.skip(reason="Complex mock setup for temp dir cleanup - functionality works in practice")
|
||||||
def test_temp_directory_cleanup_handler(self, mock_logging, mock_exists, mock_get_temp_dirs):
|
def test_temp_directory_cleanup_handler(self, mock_logging, mock_exists, mock_get_temp_dirs):
|
||||||
"""Test that temp directory cleanup handler works correctly."""
|
"""Test that temp directory cleanup handler works correctly."""
|
||||||
@ -264,11 +264,11 @@ class TestCreateServer:
|
|||||||
mock_get_temp_dirs.return_value = ["/tmp/test1", "/tmp/test2"]
|
mock_get_temp_dirs.return_value = ["/tmp/test1", "/tmp/test2"]
|
||||||
mock_exists.return_value = True
|
mock_exists.return_value = True
|
||||||
|
|
||||||
with patch('kicad_mcp.server.FastMCP'), \
|
with patch('mckicad.server.FastMCP'), \
|
||||||
patch('kicad_mcp.server.register_signal_handlers'), \
|
patch('mckicad.server.register_signal_handlers'), \
|
||||||
patch('kicad_mcp.server.atexit.register'), \
|
patch('mckicad.server.atexit.register'), \
|
||||||
patch('kicad_mcp.server.add_cleanup_handler') as mock_add_cleanup, \
|
patch('mckicad.server.add_cleanup_handler') as mock_add_cleanup, \
|
||||||
patch('kicad_mcp.server.shutil.rmtree') as mock_rmtree:
|
patch('mckicad.server.shutil.rmtree') as mock_rmtree:
|
||||||
|
|
||||||
create_server()
|
create_server()
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ class TestCreateServer:
|
|||||||
class TestSetupLogging:
|
class TestSetupLogging:
|
||||||
"""Test logging configuration."""
|
"""Test logging configuration."""
|
||||||
|
|
||||||
@patch('kicad_mcp.server.logging.basicConfig')
|
@patch('mckicad.server.logging.basicConfig')
|
||||||
def test_setup_logging(self, mock_basic_config):
|
def test_setup_logging(self, mock_basic_config):
|
||||||
"""Test logging setup configuration."""
|
"""Test logging setup configuration."""
|
||||||
setup_logging()
|
setup_logging()
|
||||||
@ -308,9 +308,9 @@ class TestSetupLogging:
|
|||||||
class TestMain:
|
class TestMain:
|
||||||
"""Test main server entry point."""
|
"""Test main server entry point."""
|
||||||
|
|
||||||
@patch('kicad_mcp.server.setup_logging')
|
@patch('mckicad.server.setup_logging')
|
||||||
@patch('kicad_mcp.server.create_server')
|
@patch('mckicad.server.create_server')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_main_successful_run(self, mock_logging, mock_create_server, mock_setup_logging):
|
def test_main_successful_run(self, mock_logging, mock_create_server, mock_setup_logging):
|
||||||
"""Test successful main execution."""
|
"""Test successful main execution."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
@ -325,9 +325,9 @@ class TestMain:
|
|||||||
mock_logging.info.assert_any_call("Starting KiCad MCP server...")
|
mock_logging.info.assert_any_call("Starting KiCad MCP server...")
|
||||||
mock_logging.info.assert_any_call("Server shutdown complete")
|
mock_logging.info.assert_any_call("Server shutdown complete")
|
||||||
|
|
||||||
@patch('kicad_mcp.server.setup_logging')
|
@patch('mckicad.server.setup_logging')
|
||||||
@patch('kicad_mcp.server.create_server')
|
@patch('mckicad.server.create_server')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_main_keyboard_interrupt(self, mock_logging, mock_create_server, mock_setup_logging):
|
def test_main_keyboard_interrupt(self, mock_logging, mock_create_server, mock_setup_logging):
|
||||||
"""Test main with keyboard interrupt."""
|
"""Test main with keyboard interrupt."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
@ -339,9 +339,9 @@ class TestMain:
|
|||||||
mock_logging.info.assert_any_call("Server interrupted by user")
|
mock_logging.info.assert_any_call("Server interrupted by user")
|
||||||
mock_logging.info.assert_any_call("Server shutdown complete")
|
mock_logging.info.assert_any_call("Server shutdown complete")
|
||||||
|
|
||||||
@patch('kicad_mcp.server.setup_logging')
|
@patch('mckicad.server.setup_logging')
|
||||||
@patch('kicad_mcp.server.create_server')
|
@patch('mckicad.server.create_server')
|
||||||
@patch('kicad_mcp.server.logging')
|
@patch('mckicad.server.logging')
|
||||||
def test_main_exception(self, mock_logging, mock_create_server, mock_setup_logging):
|
def test_main_exception(self, mock_logging, mock_create_server, mock_setup_logging):
|
||||||
"""Test main with general exception."""
|
"""Test main with general exception."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
@ -353,15 +353,15 @@ class TestMain:
|
|||||||
mock_logging.error.assert_any_call("Server error: Server error")
|
mock_logging.error.assert_any_call("Server error: Server error")
|
||||||
mock_logging.info.assert_any_call("Server shutdown complete")
|
mock_logging.info.assert_any_call("Server shutdown complete")
|
||||||
|
|
||||||
@patch('kicad_mcp.server.setup_logging')
|
@patch('mckicad.server.setup_logging')
|
||||||
@patch('kicad_mcp.server.create_server')
|
@patch('mckicad.server.create_server')
|
||||||
def test_main_cleanup_always_runs(self, mock_create_server, mock_setup_logging):
|
def test_main_cleanup_always_runs(self, mock_create_server, mock_setup_logging):
|
||||||
"""Test that cleanup always runs even with exceptions."""
|
"""Test that cleanup always runs even with exceptions."""
|
||||||
mock_server = Mock()
|
mock_server = Mock()
|
||||||
mock_server.run.side_effect = Exception("Test exception")
|
mock_server.run.side_effect = Exception("Test exception")
|
||||||
mock_create_server.return_value = mock_server
|
mock_create_server.return_value = mock_server
|
||||||
|
|
||||||
with patch('kicad_mcp.server.logging') as mock_logging:
|
with patch('mckicad.server.logging') as mock_logging:
|
||||||
main()
|
main()
|
||||||
|
|
||||||
# Verify finally block executed
|
# Verify finally block executed
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
"""
|
"""
|
||||||
Tests for the kicad_mcp.utils.component_utils module.
|
Tests for the mckicad.utils.component_utils module.
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from kicad_mcp.utils.component_utils import (
|
from mckicad.utils.component_utils import (
|
||||||
extract_capacitance_value,
|
extract_capacitance_value,
|
||||||
extract_frequency_from_value,
|
extract_frequency_from_value,
|
||||||
extract_inductance_value,
|
extract_inductance_value,
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
Tests for the kicad_mcp.utils.file_utils module.
|
Tests for the mckicad.utils.file_utils module.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from unittest.mock import mock_open, patch
|
from unittest.mock import mock_open, patch
|
||||||
|
|
||||||
from kicad_mcp.utils.file_utils import get_project_files, load_project_json
|
from mckicad.utils.file_utils import get_project_files, load_project_json
|
||||||
|
|
||||||
|
|
||||||
class TestGetProjectFiles:
|
class TestGetProjectFiles:
|
||||||
"""Test get_project_files function."""
|
"""Test get_project_files function."""
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
@patch('os.path.dirname')
|
@patch('os.path.dirname')
|
||||||
@patch('os.path.exists')
|
@patch('os.path.exists')
|
||||||
@patch('os.listdir')
|
@patch('os.listdir')
|
||||||
@ -31,7 +31,7 @@ class TestGetProjectFiles:
|
|||||||
assert "bom" in result
|
assert "bom" in result
|
||||||
assert result["bom"] == "/test/project/myproject-bom.csv"
|
assert result["bom"] == "/test/project/myproject-bom.csv"
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
@patch('os.path.dirname')
|
@patch('os.path.dirname')
|
||||||
@patch('os.path.exists')
|
@patch('os.path.exists')
|
||||||
@patch('os.listdir')
|
@patch('os.listdir')
|
||||||
@ -55,7 +55,7 @@ class TestGetProjectFiles:
|
|||||||
if file_type in result:
|
if file_type in result:
|
||||||
assert result[file_type].startswith("/test/project/test_project")
|
assert result[file_type].startswith("/test/project/test_project")
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
@patch('os.path.dirname')
|
@patch('os.path.dirname')
|
||||||
@patch('os.path.exists')
|
@patch('os.path.exists')
|
||||||
@patch('os.listdir')
|
@patch('os.listdir')
|
||||||
@ -84,7 +84,7 @@ class TestGetProjectFiles:
|
|||||||
assert result["bom"] == "/test/project/project-bom.csv"
|
assert result["bom"] == "/test/project/project-bom.csv"
|
||||||
assert result["positions"] == "/test/project/project_positions.pos"
|
assert result["positions"] == "/test/project/project_positions.pos"
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
@patch('os.path.dirname')
|
@patch('os.path.dirname')
|
||||||
@patch('os.path.exists')
|
@patch('os.path.exists')
|
||||||
@patch('os.listdir')
|
@patch('os.listdir')
|
||||||
@ -102,7 +102,7 @@ class TestGetProjectFiles:
|
|||||||
# Should not crash and return basic result
|
# Should not crash and return basic result
|
||||||
assert len(result) >= 1
|
assert len(result) >= 1
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
@patch('os.path.dirname')
|
@patch('os.path.dirname')
|
||||||
@patch('os.path.exists')
|
@patch('os.path.exists')
|
||||||
@patch('os.listdir')
|
@patch('os.listdir')
|
||||||
@ -119,7 +119,7 @@ class TestGetProjectFiles:
|
|||||||
assert result["project"] == "/test/project/project.kicad_pro"
|
assert result["project"] == "/test/project/project.kicad_pro"
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
@patch('os.path.dirname')
|
@patch('os.path.dirname')
|
||||||
@patch('os.path.exists')
|
@patch('os.path.exists')
|
||||||
@patch('os.listdir')
|
@patch('os.listdir')
|
||||||
@ -307,7 +307,7 @@ class TestIntegration:
|
|||||||
assert json_data == project_data
|
assert json_data == project_data
|
||||||
assert json_data["board"]["thickness"] == 1.6
|
assert json_data["board"]["thickness"] == 1.6
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path')
|
@patch('mckicad.utils.file_utils.get_project_name_from_path')
|
||||||
def test_project_name_integration(self, mock_get_name):
|
def test_project_name_integration(self, mock_get_name):
|
||||||
"""Test integration with get_project_name_from_path function."""
|
"""Test integration with get_project_name_from_path function."""
|
||||||
mock_get_name.return_value = "custom_name"
|
mock_get_name.return_value = "custom_name"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Tests for the kicad_mcp.utils.kicad_cli module.
|
Tests for the mckicad.utils.kicad_cli module.
|
||||||
"""
|
"""
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -7,7 +7,7 @@ from unittest.mock import Mock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from kicad_mcp.utils.kicad_cli import (
|
from mckicad.utils.kicad_cli import (
|
||||||
KiCadCLIError,
|
KiCadCLIError,
|
||||||
KiCadCLIManager,
|
KiCadCLIManager,
|
||||||
find_kicad_cli,
|
find_kicad_cli,
|
||||||
@ -44,8 +44,8 @@ class TestKiCadCLIManager:
|
|||||||
assert manager._cache_validated is False
|
assert manager._cache_validated is False
|
||||||
assert manager._system == platform.system()
|
assert manager._system == platform.system()
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path')
|
||||||
def test_find_kicad_cli_success(self, mock_validate, mock_detect):
|
def test_find_kicad_cli_success(self, mock_validate, mock_detect):
|
||||||
"""Test successful CLI detection."""
|
"""Test successful CLI detection."""
|
||||||
mock_detect.return_value = "/usr/bin/kicad-cli"
|
mock_detect.return_value = "/usr/bin/kicad-cli"
|
||||||
@ -57,7 +57,7 @@ class TestKiCadCLIManager:
|
|||||||
assert self.manager._cached_cli_path == "/usr/bin/kicad-cli"
|
assert self.manager._cached_cli_path == "/usr/bin/kicad-cli"
|
||||||
assert self.manager._cache_validated is True
|
assert self.manager._cache_validated is True
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
|
||||||
def test_find_kicad_cli_not_found(self, mock_detect):
|
def test_find_kicad_cli_not_found(self, mock_detect):
|
||||||
"""Test CLI detection failure."""
|
"""Test CLI detection failure."""
|
||||||
mock_detect.return_value = None
|
mock_detect.return_value = None
|
||||||
@ -68,8 +68,8 @@ class TestKiCadCLIManager:
|
|||||||
assert self.manager._cached_cli_path is None
|
assert self.manager._cached_cli_path is None
|
||||||
assert self.manager._cache_validated is False
|
assert self.manager._cache_validated is False
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path')
|
||||||
def test_find_kicad_cli_validation_failure(self, mock_validate, mock_detect):
|
def test_find_kicad_cli_validation_failure(self, mock_validate, mock_detect):
|
||||||
"""Test CLI detection with validation failure."""
|
"""Test CLI detection with validation failure."""
|
||||||
mock_detect.return_value = "/usr/bin/kicad-cli"
|
mock_detect.return_value = "/usr/bin/kicad-cli"
|
||||||
@ -86,7 +86,7 @@ class TestKiCadCLIManager:
|
|||||||
self.manager._cached_cli_path = "/cached/path"
|
self.manager._cached_cli_path = "/cached/path"
|
||||||
self.manager._cache_validated = True
|
self.manager._cache_validated = True
|
||||||
|
|
||||||
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect:
|
with patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect:
|
||||||
result = self.manager.find_kicad_cli()
|
result = self.manager.find_kicad_cli()
|
||||||
|
|
||||||
assert result == "/cached/path"
|
assert result == "/cached/path"
|
||||||
@ -97,8 +97,8 @@ class TestKiCadCLIManager:
|
|||||||
self.manager._cached_cli_path = "/cached/path"
|
self.manager._cached_cli_path = "/cached/path"
|
||||||
self.manager._cache_validated = True
|
self.manager._cache_validated = True
|
||||||
|
|
||||||
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \
|
with patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \
|
||||||
patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate:
|
patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate:
|
||||||
|
|
||||||
mock_detect.return_value = "/new/path"
|
mock_detect.return_value = "/new/path"
|
||||||
mock_validate.return_value = True
|
mock_validate.return_value = True
|
||||||
@ -108,7 +108,7 @@ class TestKiCadCLIManager:
|
|||||||
assert result == "/new/path"
|
assert result == "/new/path"
|
||||||
mock_detect.assert_called_once()
|
mock_detect.assert_called_once()
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_get_cli_path_success(self, mock_find):
|
def test_get_cli_path_success(self, mock_find):
|
||||||
"""Test successful CLI path retrieval."""
|
"""Test successful CLI path retrieval."""
|
||||||
mock_find.return_value = "/usr/bin/kicad-cli"
|
mock_find.return_value = "/usr/bin/kicad-cli"
|
||||||
@ -117,7 +117,7 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert result == "/usr/bin/kicad-cli"
|
assert result == "/usr/bin/kicad-cli"
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_get_cli_path_not_required(self, mock_find):
|
def test_get_cli_path_not_required(self, mock_find):
|
||||||
"""Test CLI path retrieval when not required."""
|
"""Test CLI path retrieval when not required."""
|
||||||
mock_find.return_value = None
|
mock_find.return_value = None
|
||||||
@ -126,7 +126,7 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_get_cli_path_required_raises(self, mock_find):
|
def test_get_cli_path_required_raises(self, mock_find):
|
||||||
"""Test that exception is raised when CLI required but not found."""
|
"""Test that exception is raised when CLI required but not found."""
|
||||||
mock_find.return_value = None
|
mock_find.return_value = None
|
||||||
@ -136,22 +136,22 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert "KiCad CLI not found" in str(exc_info.value)
|
assert "KiCad CLI not found" in str(exc_info.value)
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_is_available_true(self, mock_find):
|
def test_is_available_true(self, mock_find):
|
||||||
"""Test is_available returns True when CLI found."""
|
"""Test is_available returns True when CLI found."""
|
||||||
mock_find.return_value = "/usr/bin/kicad-cli"
|
mock_find.return_value = "/usr/bin/kicad-cli"
|
||||||
|
|
||||||
assert self.manager.is_available() is True
|
assert self.manager.is_available() is True
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_is_available_false(self, mock_find):
|
def test_is_available_false(self, mock_find):
|
||||||
"""Test is_available returns False when CLI not found."""
|
"""Test is_available returns False when CLI not found."""
|
||||||
mock_find.return_value = None
|
mock_find.return_value = None
|
||||||
|
|
||||||
assert self.manager.is_available() is False
|
assert self.manager.is_available() is False
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.subprocess.run')
|
@patch('mckicad.utils.kicad_cli.subprocess.run')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_get_version_success(self, mock_find, mock_run):
|
def test_get_version_success(self, mock_find, mock_run):
|
||||||
"""Test successful version retrieval."""
|
"""Test successful version retrieval."""
|
||||||
mock_find.return_value = "/usr/bin/kicad-cli"
|
mock_find.return_value = "/usr/bin/kicad-cli"
|
||||||
@ -165,7 +165,7 @@ class TestKiCadCLIManager:
|
|||||||
assert version == "KiCad 7.0.0"
|
assert version == "KiCad 7.0.0"
|
||||||
mock_run.assert_called_once()
|
mock_run.assert_called_once()
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_get_version_cli_not_found(self, mock_find):
|
def test_get_version_cli_not_found(self, mock_find):
|
||||||
"""Test version retrieval when CLI not found."""
|
"""Test version retrieval when CLI not found."""
|
||||||
mock_find.return_value = None
|
mock_find.return_value = None
|
||||||
@ -174,8 +174,8 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert version is None
|
assert version is None
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.subprocess.run')
|
@patch('mckicad.utils.kicad_cli.subprocess.run')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
@patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
|
||||||
def test_get_version_subprocess_error(self, mock_find, mock_run):
|
def test_get_version_subprocess_error(self, mock_find, mock_run):
|
||||||
"""Test version retrieval with subprocess error."""
|
"""Test version retrieval with subprocess error."""
|
||||||
mock_find.return_value = "/usr/bin/kicad-cli"
|
mock_find.return_value = "/usr/bin/kicad-cli"
|
||||||
@ -185,9 +185,9 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert version is None
|
assert version is None
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.environ.get')
|
@patch('mckicad.utils.kicad_cli.os.environ.get')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.path.isfile')
|
@patch('mckicad.utils.kicad_cli.os.path.isfile')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.access')
|
@patch('mckicad.utils.kicad_cli.os.access')
|
||||||
def test_detect_cli_path_environment_variable(self, mock_access, mock_isfile, mock_env_get):
|
def test_detect_cli_path_environment_variable(self, mock_access, mock_isfile, mock_env_get):
|
||||||
"""Test CLI detection from environment variable."""
|
"""Test CLI detection from environment variable."""
|
||||||
mock_env_get.return_value = "/custom/kicad-cli"
|
mock_env_get.return_value = "/custom/kicad-cli"
|
||||||
@ -198,8 +198,8 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert result == "/custom/kicad-cli"
|
assert result == "/custom/kicad-cli"
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.environ.get')
|
@patch('mckicad.utils.kicad_cli.os.environ.get')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.shutil.which')
|
@patch('mckicad.utils.kicad_cli.shutil.which')
|
||||||
def test_detect_cli_path_system_path(self, mock_which, mock_env_get):
|
def test_detect_cli_path_system_path(self, mock_which, mock_env_get):
|
||||||
"""Test CLI detection from system PATH."""
|
"""Test CLI detection from system PATH."""
|
||||||
mock_env_get.return_value = None
|
mock_env_get.return_value = None
|
||||||
@ -209,10 +209,10 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert result == "/usr/bin/kicad-cli"
|
assert result == "/usr/bin/kicad-cli"
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.environ.get')
|
@patch('mckicad.utils.kicad_cli.os.environ.get')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.shutil.which')
|
@patch('mckicad.utils.kicad_cli.shutil.which')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.path.isfile')
|
@patch('mckicad.utils.kicad_cli.os.path.isfile')
|
||||||
@patch('kicad_mcp.utils.kicad_cli.os.access')
|
@patch('mckicad.utils.kicad_cli.os.access')
|
||||||
def test_detect_cli_path_common_locations(self, mock_access, mock_isfile, mock_which, mock_env_get):
|
def test_detect_cli_path_common_locations(self, mock_access, mock_isfile, mock_which, mock_env_get):
|
||||||
"""Test CLI detection from common installation paths."""
|
"""Test CLI detection from common installation paths."""
|
||||||
mock_env_get.return_value = None
|
mock_env_get.return_value = None
|
||||||
@ -265,7 +265,7 @@ class TestKiCadCLIManager:
|
|||||||
assert "/usr/bin/kicad-cli" in paths
|
assert "/usr/bin/kicad-cli" in paths
|
||||||
assert "/snap/kicad/current/usr/bin/kicad-cli" in paths
|
assert "/snap/kicad/current/usr/bin/kicad-cli" in paths
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.subprocess.run')
|
@patch('mckicad.utils.kicad_cli.subprocess.run')
|
||||||
def test_validate_cli_path_success(self, mock_run):
|
def test_validate_cli_path_success(self, mock_run):
|
||||||
"""Test successful CLI validation."""
|
"""Test successful CLI validation."""
|
||||||
mock_result = Mock()
|
mock_result = Mock()
|
||||||
@ -276,7 +276,7 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert result is True
|
assert result is True
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.subprocess.run')
|
@patch('mckicad.utils.kicad_cli.subprocess.run')
|
||||||
def test_validate_cli_path_failure(self, mock_run):
|
def test_validate_cli_path_failure(self, mock_run):
|
||||||
"""Test CLI validation failure."""
|
"""Test CLI validation failure."""
|
||||||
mock_result = Mock()
|
mock_result = Mock()
|
||||||
@ -287,7 +287,7 @@ class TestKiCadCLIManager:
|
|||||||
|
|
||||||
assert result is False
|
assert result is False
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.subprocess.run')
|
@patch('mckicad.utils.kicad_cli.subprocess.run')
|
||||||
def test_validate_cli_path_exception(self, mock_run):
|
def test_validate_cli_path_exception(self, mock_run):
|
||||||
"""Test CLI validation with exception."""
|
"""Test CLI validation with exception."""
|
||||||
mock_run.side_effect = subprocess.SubprocessError("Test error")
|
mock_run.side_effect = subprocess.SubprocessError("Test error")
|
||||||
@ -302,8 +302,8 @@ class TestGlobalFunctions:
|
|||||||
|
|
||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
"""Reset global manager before each test."""
|
"""Reset global manager before each test."""
|
||||||
import kicad_mcp.utils.kicad_cli
|
import mckicad.utils.kicad_cli
|
||||||
kicad_mcp.utils.kicad_cli._cli_manager = None
|
mckicad.utils.kicad_cli._cli_manager = None
|
||||||
|
|
||||||
def test_get_cli_manager_singleton(self):
|
def test_get_cli_manager_singleton(self):
|
||||||
"""Test that get_cli_manager returns singleton instance."""
|
"""Test that get_cli_manager returns singleton instance."""
|
||||||
@ -313,7 +313,7 @@ class TestGlobalFunctions:
|
|||||||
assert manager1 is manager2
|
assert manager1 is manager2
|
||||||
assert isinstance(manager1, KiCadCLIManager)
|
assert isinstance(manager1, KiCadCLIManager)
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager')
|
@patch('mckicad.utils.kicad_cli.get_cli_manager')
|
||||||
def test_find_kicad_cli_convenience(self, mock_get_manager):
|
def test_find_kicad_cli_convenience(self, mock_get_manager):
|
||||||
"""Test find_kicad_cli convenience function."""
|
"""Test find_kicad_cli convenience function."""
|
||||||
mock_manager = Mock()
|
mock_manager = Mock()
|
||||||
@ -325,7 +325,7 @@ class TestGlobalFunctions:
|
|||||||
assert result == "/usr/bin/kicad-cli"
|
assert result == "/usr/bin/kicad-cli"
|
||||||
mock_manager.find_kicad_cli.assert_called_once_with(True)
|
mock_manager.find_kicad_cli.assert_called_once_with(True)
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager')
|
@patch('mckicad.utils.kicad_cli.get_cli_manager')
|
||||||
def test_get_kicad_cli_path_convenience(self, mock_get_manager):
|
def test_get_kicad_cli_path_convenience(self, mock_get_manager):
|
||||||
"""Test get_kicad_cli_path convenience function."""
|
"""Test get_kicad_cli_path convenience function."""
|
||||||
mock_manager = Mock()
|
mock_manager = Mock()
|
||||||
@ -337,7 +337,7 @@ class TestGlobalFunctions:
|
|||||||
assert result == "/usr/bin/kicad-cli"
|
assert result == "/usr/bin/kicad-cli"
|
||||||
mock_manager.get_cli_path.assert_called_once_with(False)
|
mock_manager.get_cli_path.assert_called_once_with(False)
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager')
|
@patch('mckicad.utils.kicad_cli.get_cli_manager')
|
||||||
def test_is_kicad_cli_available_convenience(self, mock_get_manager):
|
def test_is_kicad_cli_available_convenience(self, mock_get_manager):
|
||||||
"""Test is_kicad_cli_available convenience function."""
|
"""Test is_kicad_cli_available convenience function."""
|
||||||
mock_manager = Mock()
|
mock_manager = Mock()
|
||||||
@ -349,7 +349,7 @@ class TestGlobalFunctions:
|
|||||||
assert result is True
|
assert result is True
|
||||||
mock_manager.is_available.assert_called_once()
|
mock_manager.is_available.assert_called_once()
|
||||||
|
|
||||||
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager')
|
@patch('mckicad.utils.kicad_cli.get_cli_manager')
|
||||||
def test_get_kicad_version_convenience(self, mock_get_manager):
|
def test_get_kicad_version_convenience(self, mock_get_manager):
|
||||||
"""Test get_kicad_version convenience function."""
|
"""Test get_kicad_version convenience function."""
|
||||||
mock_manager = Mock()
|
mock_manager = Mock()
|
||||||
@ -374,8 +374,8 @@ class TestIntegration:
|
|||||||
assert not manager._cache_validated
|
assert not manager._cache_validated
|
||||||
|
|
||||||
# Simulate finding CLI
|
# Simulate finding CLI
|
||||||
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \
|
with patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \
|
||||||
patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate:
|
patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate:
|
||||||
|
|
||||||
mock_detect.return_value = "/test/kicad-cli"
|
mock_detect.return_value = "/test/kicad-cli"
|
||||||
mock_validate.return_value = True
|
mock_validate.return_value = True
|
||||||
@ -401,7 +401,7 @@ class TestIntegration:
|
|||||||
"""Test that errors are properly propagated."""
|
"""Test that errors are properly propagated."""
|
||||||
manager = KiCadCLIManager()
|
manager = KiCadCLIManager()
|
||||||
|
|
||||||
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') as mock_find:
|
with patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') as mock_find:
|
||||||
mock_find.return_value = None
|
mock_find.return_value = None
|
||||||
|
|
||||||
# Should not raise when required=False
|
# Should not raise when required=False
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import tempfile
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from kicad_mcp.utils.path_validator import (
|
from mckicad.utils.path_validator import (
|
||||||
PathValidationError,
|
PathValidationError,
|
||||||
PathValidator,
|
PathValidator,
|
||||||
validate_directory,
|
validate_directory,
|
||||||
@ -200,7 +200,7 @@ class TestConvenienceFunctions:
|
|||||||
"""Test validate_path convenience function."""
|
"""Test validate_path convenience function."""
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
# Add temp_dir to default validator
|
# Add temp_dir to default validator
|
||||||
from kicad_mcp.utils.path_validator import get_default_validator
|
from mckicad.utils.path_validator import get_default_validator
|
||||||
|
|
||||||
get_default_validator().add_trusted_root(temp_dir)
|
get_default_validator().add_trusted_root(temp_dir)
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ class TestConvenienceFunctions:
|
|||||||
"""Test validate_kicad_file convenience function."""
|
"""Test validate_kicad_file convenience function."""
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
# Add temp_dir to default validator
|
# Add temp_dir to default validator
|
||||||
from kicad_mcp.utils.path_validator import get_default_validator
|
from mckicad.utils.path_validator import get_default_validator
|
||||||
|
|
||||||
get_default_validator().add_trusted_root(temp_dir)
|
get_default_validator().add_trusted_root(temp_dir)
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ class TestConvenienceFunctions:
|
|||||||
"""Test validate_directory convenience function."""
|
"""Test validate_directory convenience function."""
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
# Add temp_dir to default validator
|
# Add temp_dir to default validator
|
||||||
from kicad_mcp.utils.path_validator import get_default_validator
|
from mckicad.utils.path_validator import get_default_validator
|
||||||
|
|
||||||
get_default_validator().add_trusted_root(temp_dir)
|
get_default_validator().add_trusted_root(temp_dir)
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,9 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from kicad_mcp.utils.kicad_cli import KiCadCLIError
|
from mckicad.utils.kicad_cli import KiCadCLIError
|
||||||
from kicad_mcp.utils.path_validator import PathValidationError, PathValidator
|
from mckicad.utils.path_validator import PathValidationError, PathValidator
|
||||||
from kicad_mcp.utils.secure_subprocess import (
|
from mckicad.utils.secure_subprocess import (
|
||||||
SecureSubprocessError,
|
SecureSubprocessError,
|
||||||
SecureSubprocessRunner,
|
SecureSubprocessRunner,
|
||||||
create_temp_file,
|
create_temp_file,
|
||||||
@ -22,7 +22,7 @@ from kicad_mcp.utils.secure_subprocess import (
|
|||||||
def _kicad_cli_available():
|
def _kicad_cli_available():
|
||||||
"""Check if KiCad CLI is available."""
|
"""Check if KiCad CLI is available."""
|
||||||
try:
|
try:
|
||||||
from kicad_mcp.utils.kicad_cli import get_kicad_cli_path
|
from mckicad.utils.kicad_cli import get_kicad_cli_path
|
||||||
|
|
||||||
get_kicad_cli_path()
|
get_kicad_cli_path()
|
||||||
return True
|
return True
|
||||||
@ -45,7 +45,7 @@ class TestSecureSubprocessRunner:
|
|||||||
runner = SecureSubprocessRunner(path_validator=validator)
|
runner = SecureSubprocessRunner(path_validator=validator)
|
||||||
assert runner.path_validator is validator
|
assert runner.path_validator is validator
|
||||||
|
|
||||||
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path")
|
@patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
|
||||||
@patch.object(SecureSubprocessRunner, "_run_subprocess")
|
@patch.object(SecureSubprocessRunner, "_run_subprocess")
|
||||||
def test_run_kicad_command_success(self, mock_run_subprocess, mock_get_cli):
|
def test_run_kicad_command_success(self, mock_run_subprocess, mock_get_cli):
|
||||||
"""Test successful KiCad command execution."""
|
"""Test successful KiCad command execution."""
|
||||||
@ -76,7 +76,7 @@ class TestSecureSubprocessRunner:
|
|||||||
assert result is mock_result
|
assert result is mock_result
|
||||||
mock_run_subprocess.assert_called_once()
|
mock_run_subprocess.assert_called_once()
|
||||||
|
|
||||||
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path")
|
@patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
|
||||||
def test_run_kicad_command_cli_not_found(self, mock_get_cli):
|
def test_run_kicad_command_cli_not_found(self, mock_get_cli):
|
||||||
"""Test KiCad command when CLI not found."""
|
"""Test KiCad command when CLI not found."""
|
||||||
mock_get_cli.side_effect = KiCadCLIError("CLI not found")
|
mock_get_cli.side_effect = KiCadCLIError("CLI not found")
|
||||||
@ -113,7 +113,7 @@ class TestSecureSubprocessRunner:
|
|||||||
output_files=["/etc/output.svg"],
|
output_files=["/etc/output.svg"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path")
|
@patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
|
||||||
@patch.object(SecureSubprocessRunner, "_run_subprocess")
|
@patch.object(SecureSubprocessRunner, "_run_subprocess")
|
||||||
def test_run_kicad_command_with_working_dir(self, mock_run_subprocess, mock_get_cli):
|
def test_run_kicad_command_with_working_dir(self, mock_run_subprocess, mock_get_cli):
|
||||||
"""Test KiCad command with working directory."""
|
"""Test KiCad command with working directory."""
|
||||||
@ -132,7 +132,7 @@ class TestSecureSubprocessRunner:
|
|||||||
call_args = mock_run_subprocess.call_args
|
call_args = mock_run_subprocess.call_args
|
||||||
assert call_args[1]["working_dir"] == os.path.realpath(temp_dir)
|
assert call_args[1]["working_dir"] == os.path.realpath(temp_dir)
|
||||||
|
|
||||||
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path")
|
@patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
|
||||||
@patch.object(SecureSubprocessRunner, "_run_subprocess")
|
@patch.object(SecureSubprocessRunner, "_run_subprocess")
|
||||||
def test_run_kicad_command_subprocess_error(self, mock_run_subprocess, mock_get_cli):
|
def test_run_kicad_command_subprocess_error(self, mock_run_subprocess, mock_get_cli):
|
||||||
"""Test KiCad command with subprocess error."""
|
"""Test KiCad command with subprocess error."""
|
||||||
@ -145,7 +145,7 @@ class TestSecureSubprocessRunner:
|
|||||||
runner.run_kicad_command(["--version"])
|
runner.run_kicad_command(["--version"])
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path")
|
@patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
|
||||||
@patch.object(SecureSubprocessRunner, "run_kicad_command")
|
@patch.object(SecureSubprocessRunner, "run_kicad_command")
|
||||||
async def test_run_kicad_command_async(self, mock_run_command, mock_get_cli):
|
async def test_run_kicad_command_async(self, mock_run_command, mock_get_cli):
|
||||||
"""Test async KiCad command execution."""
|
"""Test async KiCad command execution."""
|
||||||
|
|||||||
236
uv.lock
generated
236
uv.lock
generated
@ -565,39 +565,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b4/39/61981f3c5d123f6ceff73479e3ace1de81d90af95632e2fe3d094b0ac8a9/filelock-3.21.1-py3-none-any.whl", hash = "sha256:f3aa1887cdf3514b13a379b5835ff35327d7aa24a96db4453b97531c00476760", size = 21472, upload-time = "2026-02-12T22:29:27.518Z" },
|
{ url = "https://files.pythonhosted.org/packages/b4/39/61981f3c5d123f6ceff73479e3ace1de81d90af95632e2fe3d094b0ac8a9/filelock-3.21.1-py3-none-any.whl", hash = "sha256:f3aa1887cdf3514b13a379b5835ff35327d7aa24a96db4453b97531c00476760", size = 21472, upload-time = "2026-02-12T22:29:27.518Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "greenlet"
|
|
||||||
version = "3.2.3"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752, upload-time = "2025-06-05T16:16:09.955Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d", size = 271992, upload-time = "2025-06-05T16:11:23.467Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b", size = 638820, upload-time = "2025-06-05T16:38:52.882Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d", size = 653046, upload-time = "2025-06-05T16:41:36.343Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264", size = 647701, upload-time = "2025-06-05T16:48:19.604Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688", size = 649747, upload-time = "2025-06-05T16:13:04.628Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb", size = 605461, upload-time = "2025-06-05T16:12:50.792Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c", size = 1121190, upload-time = "2025-06-05T16:36:48.59Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163", size = 1149055, upload-time = "2025-06-05T16:12:40.457Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849", size = 297817, upload-time = "2025-06-05T16:29:49.244Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b1/cf/f5c0b23309070ae93de75c90d29300751a5aacefc0a3ed1b1d8edb28f08b/greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad", size = 270732, upload-time = "2025-06-05T16:10:08.26Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/48/ae/91a957ba60482d3fecf9be49bc3948f341d706b52ddb9d83a70d42abd498/greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef", size = 639033, upload-time = "2025-06-05T16:38:53.983Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6f/df/20ffa66dd5a7a7beffa6451bdb7400d66251374ab40b99981478c69a67a8/greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3", size = 652999, upload-time = "2025-06-05T16:41:37.89Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/51/b4/ebb2c8cb41e521f1d72bf0465f2f9a2fd803f674a88db228887e6847077e/greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95", size = 647368, upload-time = "2025-06-05T16:48:21.467Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/8e/6a/1e1b5aa10dced4ae876a322155705257748108b7fd2e4fae3f2a091fe81a/greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb", size = 650037, upload-time = "2025-06-05T16:13:06.402Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/26/f2/ad51331a157c7015c675702e2d5230c243695c788f8f75feba1af32b3617/greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b", size = 608402, upload-time = "2025-06-05T16:12:51.91Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/26/bc/862bd2083e6b3aff23300900a956f4ea9a4059de337f5c8734346b9b34fc/greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0", size = 1119577, upload-time = "2025-06-05T16:36:49.787Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/86/94/1fc0cc068cfde885170e01de40a619b00eaa8f2916bf3541744730ffb4c3/greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36", size = 1147121, upload-time = "2025-06-05T16:12:42.527Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/27/1a/199f9587e8cb08a0658f9c30f3799244307614148ffe8b1e3aa22f324dea/greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3", size = 297603, upload-time = "2025-06-05T16:20:12.651Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d8/ca/accd7aa5280eb92b70ed9e8f7fd79dc50a2c21d8c73b9a0856f5b564e222/greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86", size = 271479, upload-time = "2025-06-05T16:10:47.525Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/55/71/01ed9895d9eb49223280ecc98a557585edfa56b3d0e965b9fa9f7f06b6d9/greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97", size = 683952, upload-time = "2025-06-05T16:38:55.125Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ea/61/638c4bdf460c3c678a0a1ef4c200f347dff80719597e53b5edb2fb27ab54/greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728", size = 696917, upload-time = "2025-06-05T16:41:38.959Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/cc/0bd1a7eb759d1f3e3cc2d1bc0f0b487ad3cc9f34d74da4b80f226fde4ec3/greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a", size = 692443, upload-time = "2025-06-05T16:48:23.113Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/10/b2a4b63d3f08362662e89c103f7fe28894a51ae0bc890fabf37d1d780e52/greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892", size = 692995, upload-time = "2025-06-05T16:13:07.972Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/c6/ad82f148a4e3ce9564056453a71529732baf5448ad53fc323e37efe34f66/greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141", size = 655320, upload-time = "2025-06-05T16:12:53.453Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236, upload-time = "2025-06-05T16:15:20.111Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h11"
|
name = "h11"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
@ -823,93 +790,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
|
{ url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kicad-mcp"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = { editable = "." }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "defusedxml" },
|
|
||||||
{ name = "fastmcp" },
|
|
||||||
{ name = "kicad-python" },
|
|
||||||
{ name = "mcp", extra = ["cli"] },
|
|
||||||
{ name = "pandas" },
|
|
||||||
{ name = "pyyaml" },
|
|
||||||
{ name = "requests" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dev-dependencies]
|
|
||||||
dev = [
|
|
||||||
{ name = "bandit" },
|
|
||||||
{ name = "mypy" },
|
|
||||||
{ name = "pre-commit" },
|
|
||||||
{ name = "pytest" },
|
|
||||||
{ name = "pytest-asyncio" },
|
|
||||||
{ name = "pytest-cov" },
|
|
||||||
{ name = "pytest-mock" },
|
|
||||||
{ name = "pytest-xdist" },
|
|
||||||
{ name = "ruff" },
|
|
||||||
]
|
|
||||||
docs = [
|
|
||||||
{ name = "myst-parser" },
|
|
||||||
{ name = "sphinx" },
|
|
||||||
{ name = "sphinx-rtd-theme" },
|
|
||||||
]
|
|
||||||
performance = [
|
|
||||||
{ name = "memory-profiler" },
|
|
||||||
{ name = "py-spy" },
|
|
||||||
]
|
|
||||||
security = [
|
|
||||||
{ name = "bandit" },
|
|
||||||
{ name = "safety" },
|
|
||||||
]
|
|
||||||
visualization = [
|
|
||||||
{ name = "cairosvg" },
|
|
||||||
{ name = "pillow" },
|
|
||||||
{ name = "playwright" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata]
|
|
||||||
requires-dist = [
|
|
||||||
{ name = "defusedxml", specifier = ">=0.7.1" },
|
|
||||||
{ name = "fastmcp", specifier = ">=2.14.5" },
|
|
||||||
{ name = "kicad-python", specifier = ">=0.5.0" },
|
|
||||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.26.0" },
|
|
||||||
{ name = "pandas", specifier = ">=2.3.3" },
|
|
||||||
{ name = "pyyaml", specifier = ">=6.0.3" },
|
|
||||||
{ name = "requests", specifier = ">=2.32.5" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
|
||||||
dev = [
|
|
||||||
{ name = "bandit", specifier = ">=1.9.3" },
|
|
||||||
{ name = "mypy", specifier = ">=1.19.1" },
|
|
||||||
{ name = "pre-commit", specifier = ">=4.5.1" },
|
|
||||||
{ name = "pytest", specifier = ">=8.4.2" },
|
|
||||||
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
|
|
||||||
{ name = "pytest-cov", specifier = ">=7.0.0" },
|
|
||||||
{ name = "pytest-mock", specifier = ">=3.15.1" },
|
|
||||||
{ name = "pytest-xdist", specifier = ">=3.8.0" },
|
|
||||||
{ name = "ruff", specifier = ">=0.15.1" },
|
|
||||||
]
|
|
||||||
docs = [
|
|
||||||
{ name = "myst-parser", specifier = ">=5.0.0" },
|
|
||||||
{ name = "sphinx", specifier = ">=9.1.0" },
|
|
||||||
{ name = "sphinx-rtd-theme", specifier = ">=3.1.0" },
|
|
||||||
]
|
|
||||||
performance = [
|
|
||||||
{ name = "memory-profiler", specifier = ">=0.61.0" },
|
|
||||||
{ name = "py-spy", specifier = ">=0.3.0" },
|
|
||||||
]
|
|
||||||
security = [
|
|
||||||
{ name = "bandit", specifier = ">=1.9.3" },
|
|
||||||
{ name = "safety", specifier = ">=3.7.0" },
|
|
||||||
]
|
|
||||||
visualization = [
|
|
||||||
{ name = "cairosvg", specifier = ">=2.8.2" },
|
|
||||||
{ name = "pillow", specifier = ">=12.1.1" },
|
|
||||||
{ name = "playwright", specifier = ">=1.58.0" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kicad-python"
|
name = "kicad-python"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -1095,6 +975,91 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d6/26/6cc45d156f44dbe1d5696d9e54042e4dcaf7b946c0b86df6a97d29706f32/marshmallow-4.0.0-py3-none-any.whl", hash = "sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203", size = 48420, upload-time = "2025-04-17T02:25:53.375Z" },
|
{ url = "https://files.pythonhosted.org/packages/d6/26/6cc45d156f44dbe1d5696d9e54042e4dcaf7b946c0b86df6a97d29706f32/marshmallow-4.0.0-py3-none-any.whl", hash = "sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203", size = 48420, upload-time = "2025-04-17T02:25:53.375Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mckicad"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { editable = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "defusedxml" },
|
||||||
|
{ name = "fastmcp" },
|
||||||
|
{ name = "kicad-python" },
|
||||||
|
{ name = "mcp", extra = ["cli"] },
|
||||||
|
{ name = "pandas" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
{ name = "requests" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "bandit" },
|
||||||
|
{ name = "mypy" },
|
||||||
|
{ name = "pre-commit" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "pytest-asyncio" },
|
||||||
|
{ name = "pytest-cov" },
|
||||||
|
{ name = "pytest-mock" },
|
||||||
|
{ name = "pytest-xdist" },
|
||||||
|
{ name = "ruff" },
|
||||||
|
]
|
||||||
|
docs = [
|
||||||
|
{ name = "myst-parser" },
|
||||||
|
{ name = "sphinx" },
|
||||||
|
{ name = "sphinx-rtd-theme" },
|
||||||
|
]
|
||||||
|
performance = [
|
||||||
|
{ name = "memory-profiler" },
|
||||||
|
{ name = "py-spy" },
|
||||||
|
]
|
||||||
|
security = [
|
||||||
|
{ name = "bandit" },
|
||||||
|
{ name = "safety" },
|
||||||
|
]
|
||||||
|
visualization = [
|
||||||
|
{ name = "cairosvg" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "defusedxml", specifier = ">=0.7.1" },
|
||||||
|
{ name = "fastmcp", specifier = ">=2.14.5" },
|
||||||
|
{ name = "kicad-python", specifier = ">=0.5.0" },
|
||||||
|
{ name = "mcp", extras = ["cli"], specifier = ">=1.26.0" },
|
||||||
|
{ name = "pandas", specifier = ">=2.3.3" },
|
||||||
|
{ name = "pyyaml", specifier = ">=6.0.3" },
|
||||||
|
{ name = "requests", specifier = ">=2.32.5" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata.requires-dev]
|
||||||
|
dev = [
|
||||||
|
{ name = "bandit", specifier = ">=1.9.3" },
|
||||||
|
{ name = "mypy", specifier = ">=1.19.1" },
|
||||||
|
{ name = "pre-commit", specifier = ">=4.5.1" },
|
||||||
|
{ name = "pytest", specifier = ">=8.4.2" },
|
||||||
|
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
|
||||||
|
{ name = "pytest-cov", specifier = ">=7.0.0" },
|
||||||
|
{ name = "pytest-mock", specifier = ">=3.15.1" },
|
||||||
|
{ name = "pytest-xdist", specifier = ">=3.8.0" },
|
||||||
|
{ name = "ruff", specifier = ">=0.15.1" },
|
||||||
|
]
|
||||||
|
docs = [
|
||||||
|
{ name = "myst-parser", specifier = ">=5.0.0" },
|
||||||
|
{ name = "sphinx", specifier = ">=9.1.0" },
|
||||||
|
{ name = "sphinx-rtd-theme", specifier = ">=3.1.0" },
|
||||||
|
]
|
||||||
|
performance = [
|
||||||
|
{ name = "memory-profiler", specifier = ">=0.61.0" },
|
||||||
|
{ name = "py-spy", specifier = ">=0.3.0" },
|
||||||
|
]
|
||||||
|
security = [
|
||||||
|
{ name = "bandit", specifier = ">=1.9.3" },
|
||||||
|
{ name = "safety", specifier = ">=3.7.0" },
|
||||||
|
]
|
||||||
|
visualization = [
|
||||||
|
{ name = "cairosvg", specifier = ">=2.8.2" },
|
||||||
|
{ name = "pillow", specifier = ">=12.1.1" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mcp"
|
name = "mcp"
|
||||||
version = "1.26.0"
|
version = "1.26.0"
|
||||||
@ -1567,25 +1532,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
|
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "playwright"
|
|
||||||
version = "1.58.0"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "greenlet" },
|
|
||||||
{ name = "pyee" },
|
|
||||||
]
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/c9/9c6061d5703267f1baae6a4647bfd1862e386fbfdb97d889f6f6ae9e3f64/playwright-1.58.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:96e3204aac292ee639edbfdef6298b4be2ea0a55a16b7068df91adac077cc606", size = 42251098, upload-time = "2026-01-30T15:09:24.028Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e0/40/59d34a756e02f8c670f0fee987d46f7ee53d05447d43cd114ca015cb168c/playwright-1.58.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:70c763694739d28df71ed578b9c8202bb83e8fe8fb9268c04dd13afe36301f71", size = 41039625, upload-time = "2026-01-30T15:09:27.558Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e1/ee/3ce6209c9c74a650aac9028c621f357a34ea5cd4d950700f8e2c4b7fe2c4/playwright-1.58.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:185e0132578733d02802dfddfbbc35f42be23a45ff49ccae5081f25952238117", size = 42251098, upload-time = "2026-01-30T15:09:30.461Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f1/af/009958cbf23fac551a940d34e3206e6c7eed2b8c940d0c3afd1feb0b0589/playwright-1.58.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:c95568ba1eda83812598c1dc9be60b4406dffd60b149bc1536180ad108723d6b", size = 46235268, upload-time = "2026-01-30T15:09:33.787Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d9/a6/0e66ad04b6d3440dae73efb39540c5685c5fc95b17c8b29340b62abbd952/playwright-1.58.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f9999948f1ab541d98812de25e3a8c410776aa516d948807140aff797b4bffa", size = 45964214, upload-time = "2026-01-30T15:09:36.751Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/0e/4b/236e60ab9f6d62ed0fd32150d61f1f494cefbf02304c0061e78ed80c1c32/playwright-1.58.0-py3-none-win32.whl", hash = "sha256:1e03be090e75a0fabbdaeab65ce17c308c425d879fa48bb1d7986f96bfad0b99", size = 36815998, upload-time = "2026-01-30T15:09:39.627Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/41/f8/5ec599c5e59d2f2f336a05b4f318e733077cd5044f24adb6f86900c3e6a7/playwright-1.58.0-py3-none-win_amd64.whl", hash = "sha256:a2bf639d0ce33b3ba38de777e08697b0d8f3dc07ab6802e4ac53fb65e3907af8", size = 36816005, upload-time = "2026-01-30T15:09:42.449Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/c8/c4/cc0229fea55c87d6c9c67fe44a21e2cd28d1d558a5478ed4d617e9fb0c93/playwright-1.58.0-py3-none-win_arm64.whl", hash = "sha256:32ffe5c303901a13a0ecab91d1c3f74baf73b84f4bedbb6b935f5bc11cc98e1b", size = 33085919, upload-time = "2026-01-30T15:09:45.71Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pluggy"
|
name = "pluggy"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@ -1812,18 +1758,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/c9/c7/68f2553819965326f968375f02597d49efe71b309ba9d8fef539aeb51c48/pydocket-0.17.7-py3-none-any.whl", hash = "sha256:d1e0921ac02026c4a0140fc72a3848545f3e91e6e74c6e32c588489017c130b2", size = 94608, upload-time = "2026-02-11T21:01:30.111Z" },
|
{ url = "https://files.pythonhosted.org/packages/c9/c7/68f2553819965326f968375f02597d49efe71b309ba9d8fef539aeb51c48/pydocket-0.17.7-py3-none-any.whl", hash = "sha256:d1e0921ac02026c4a0140fc72a3848545f3e91e6e74c6e32c588489017c130b2", size = 94608, upload-time = "2026-02-11T21:01:30.111Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyee"
|
|
||||||
version = "13.0.0"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "typing-extensions" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
version = "2.19.2"
|
version = "2.19.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user