chore: set up pre-commit framework with general hooks
Add .pre-commit-config.yaml with pre-commit-hooks v5.0.0 (trailing whitespace, end-of-file fixer, yaml/json checks, large file guard, merge conflict detection). Add minimal pyproject.toml. Auto-fix trailing whitespace and missing end-of-file newlines across the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,71 +1,11 @@
|
|||||||
# Pre-commit hooks configuration
|
repos:
|
||||||
# See https://pre-commit.com for more information
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v5.0.0
|
||||||
repos:
|
hooks:
|
||||||
# Python code formatting
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/psf/black
|
- id: end-of-file-fixer
|
||||||
rev: 23.7.0
|
- id: check-yaml
|
||||||
hooks:
|
- id: check-json
|
||||||
- id: black
|
- id: check-added-large-files
|
||||||
language_version: python3
|
args: ['--maxkb=500']
|
||||||
files: ^python/
|
- id: check-merge-conflict
|
||||||
|
|
||||||
# Python import sorting
|
|
||||||
- repo: https://github.com/pycqa/isort
|
|
||||||
rev: 5.12.0
|
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
files: ^python/
|
|
||||||
args: ["--profile", "black"]
|
|
||||||
|
|
||||||
# Python type checking
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
||||||
rev: v1.5.0
|
|
||||||
hooks:
|
|
||||||
- id: mypy
|
|
||||||
files: ^python/
|
|
||||||
args: [--ignore-missing-imports]
|
|
||||||
|
|
||||||
# Python linting
|
|
||||||
- repo: https://github.com/pycqa/flake8
|
|
||||||
rev: 6.1.0
|
|
||||||
hooks:
|
|
||||||
- id: flake8
|
|
||||||
files: ^python/
|
|
||||||
args: [--max-line-length=100, --extend-ignore=E203]
|
|
||||||
|
|
||||||
# TypeScript/JavaScript formatting
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
|
||||||
rev: v3.0.3
|
|
||||||
hooks:
|
|
||||||
- id: prettier
|
|
||||||
types_or: [javascript, typescript, json, yaml, markdown]
|
|
||||||
files: \.(ts|js|json|ya?ml|md)$
|
|
||||||
|
|
||||||
# General file checks
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.4.0
|
|
||||||
hooks:
|
|
||||||
- id: trailing-whitespace
|
|
||||||
- id: end-of-file-fixer
|
|
||||||
- id: check-yaml
|
|
||||||
- id: check-json
|
|
||||||
- id: check-added-large-files
|
|
||||||
args: [--maxkb=500]
|
|
||||||
- id: check-merge-conflict
|
|
||||||
- id: detect-private-key
|
|
||||||
|
|
||||||
# Python security checks
|
|
||||||
- repo: https://github.com/PyCQA/bandit
|
|
||||||
rev: 1.7.5
|
|
||||||
hooks:
|
|
||||||
- id: bandit
|
|
||||||
args: [-c, pyproject.toml]
|
|
||||||
files: ^python/
|
|
||||||
|
|
||||||
# Markdown linting
|
|
||||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
|
||||||
rev: v0.37.0
|
|
||||||
hooks:
|
|
||||||
- id: markdownlint
|
|
||||||
args: [--fix]
|
|
||||||
|
|||||||
@@ -986,9 +986,3 @@ If you use this project in your research or publication, please cite:
|
|||||||
version = {2.2.3}
|
version = {2.2.3}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -106,12 +106,12 @@ export const toolCategories: ToolCategory[] = [
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
output_dir: {
|
output_dir: {
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "Output directory path"
|
description: "Output directory path"
|
||||||
},
|
},
|
||||||
layers: {
|
layers: {
|
||||||
type: "array",
|
type: "array",
|
||||||
items: { type: "string" },
|
items: { type: "string" },
|
||||||
description: "Layers to export (default: all copper + silkscreen + mask)"
|
description: "Layers to export (default: all copper + silkscreen + mask)"
|
||||||
},
|
},
|
||||||
@@ -160,9 +160,9 @@ export const toolCategories: ToolCategory[] = [
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
report_all: {
|
report_all: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
description: "Report all violations or stop at first"
|
description: "Report all violations or stop at first"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -313,17 +313,17 @@ These are the tools that enable discovery and execution.
|
|||||||
```typescript
|
```typescript
|
||||||
// src/tools/router.ts
|
// src/tools/router.ts
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getAllCategories,
|
getAllCategories,
|
||||||
getCategory,
|
getCategory,
|
||||||
getTool,
|
getTool,
|
||||||
searchTools
|
searchTools
|
||||||
} from "./registry.js";
|
} from "./registry.js";
|
||||||
|
|
||||||
export const routerTools = {
|
export const routerTools = {
|
||||||
list_tool_categories: {
|
list_tool_categories: {
|
||||||
name: "list_tool_categories",
|
name: "list_tool_categories",
|
||||||
description:
|
description:
|
||||||
"List all available tool categories. Use this to discover what operations " +
|
"List all available tool categories. Use this to discover what operations " +
|
||||||
"are available beyond the basic tools exposed directly.",
|
"are available beyond the basic tools exposed directly.",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
@@ -475,8 +475,8 @@ export const directTools: ToolDefinition[] = [
|
|||||||
properties: {
|
properties: {
|
||||||
name: { type: "string", description: "Project name" },
|
name: { type: "string", description: "Project name" },
|
||||||
path: { type: "string", description: "Directory path for project" },
|
path: { type: "string", description: "Directory path for project" },
|
||||||
template: {
|
template: {
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "Optional template to use",
|
description: "Optional template to use",
|
||||||
enum: ["blank", "arduino", "raspberry-pi"]
|
enum: ["blank", "arduino", "raspberry-pi"]
|
||||||
}
|
}
|
||||||
@@ -804,19 +804,19 @@ Include:
|
|||||||
const categories = [
|
const categories = [
|
||||||
// Core operations (might be direct tools instead)
|
// Core operations (might be direct tools instead)
|
||||||
{ name: "project", description: "Project lifecycle: create, open, save, close" },
|
{ name: "project", description: "Project lifecycle: create, open, save, close" },
|
||||||
|
|
||||||
// Domain-specific operations
|
// Domain-specific operations
|
||||||
{ name: "analysis", description: "Analyze and inspect: find patterns, validate, check" },
|
{ name: "analysis", description: "Analyze and inspect: find patterns, validate, check" },
|
||||||
{ name: "modification", description: "Modify and transform: edit, rename, restructure" },
|
{ name: "modification", description: "Modify and transform: edit, rename, restructure" },
|
||||||
{ name: "navigation", description: "Navigate and search: find, list, filter, locate" },
|
{ name: "navigation", description: "Navigate and search: find, list, filter, locate" },
|
||||||
|
|
||||||
// Output operations
|
// Output operations
|
||||||
{ name: "export", description: "Export and generate: reports, files, documentation" },
|
{ name: "export", description: "Export and generate: reports, files, documentation" },
|
||||||
{ name: "import", description: "Import from external sources: files, formats, APIs" },
|
{ name: "import", description: "Import from external sources: files, formats, APIs" },
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
{ name: "config", description: "Configuration and settings: preferences, rules, templates" },
|
{ name: "config", description: "Configuration and settings: preferences, rules, templates" },
|
||||||
|
|
||||||
// Advanced/specialized
|
// Advanced/specialized
|
||||||
{ name: "advanced", description: "Advanced operations: batch processing, automation, scripting" },
|
{ name: "advanced", description: "Advanced operations: batch processing, automation, scripting" },
|
||||||
];
|
];
|
||||||
@@ -962,11 +962,11 @@ Claude: "I've added length tuning meanders to match the trace lengths"
|
|||||||
// tests/router.test.ts
|
// tests/router.test.ts
|
||||||
|
|
||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import {
|
import {
|
||||||
searchTools,
|
searchTools,
|
||||||
getCategory,
|
getCategory,
|
||||||
getTool,
|
getTool,
|
||||||
getAllCategories
|
getAllCategories
|
||||||
} from "../src/tools/registry.js";
|
} from "../src/tools/registry.js";
|
||||||
import { routerTools } from "../src/tools/router.js";
|
import { routerTools } from "../src/tools/router.js";
|
||||||
|
|
||||||
@@ -997,16 +997,16 @@ describe("Router Tools", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("get_category_tools returns tools for valid category", async () => {
|
it("get_category_tools returns tools for valid category", async () => {
|
||||||
const result = await routerTools.get_category_tools.handler({
|
const result = await routerTools.get_category_tools.handler({
|
||||||
category: "export"
|
category: "export"
|
||||||
});
|
});
|
||||||
expect(result.tools).toBeDefined();
|
expect(result.tools).toBeDefined();
|
||||||
expect(result.tools.length).toBeGreaterThan(0);
|
expect(result.tools.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("get_category_tools returns error for invalid category", async () => {
|
it("get_category_tools returns error for invalid category", async () => {
|
||||||
const result = await routerTools.get_category_tools.handler({
|
const result = await routerTools.get_category_tools.handler({
|
||||||
category: "nonexistent"
|
category: "nonexistent"
|
||||||
});
|
});
|
||||||
expect(result.error).toBeDefined();
|
expect(result.error).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|||||||
4
pyproject.toml
Normal file
4
pyproject.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[project]
|
||||||
|
name = "kicad-mcp-server"
|
||||||
|
version = "2.1.0"
|
||||||
|
requires-python = ">=3.10"
|
||||||
@@ -20,57 +20,57 @@ class BoardCommands:
|
|||||||
def __init__(self, board: Optional[pcbnew.BOARD] = None):
|
def __init__(self, board: Optional[pcbnew.BOARD] = None):
|
||||||
"""Initialize with optional board instance"""
|
"""Initialize with optional board instance"""
|
||||||
self.board = board
|
self.board = board
|
||||||
|
|
||||||
# Initialize specialized command classes
|
# Initialize specialized command classes
|
||||||
self.size_commands = BoardSizeCommands(board)
|
self.size_commands = BoardSizeCommands(board)
|
||||||
self.layer_commands = BoardLayerCommands(board)
|
self.layer_commands = BoardLayerCommands(board)
|
||||||
self.outline_commands = BoardOutlineCommands(board)
|
self.outline_commands = BoardOutlineCommands(board)
|
||||||
self.view_commands = BoardViewCommands(board)
|
self.view_commands = BoardViewCommands(board)
|
||||||
|
|
||||||
# Delegate board size commands
|
# Delegate board size commands
|
||||||
def set_board_size(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def set_board_size(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Set the size of the PCB board"""
|
"""Set the size of the PCB board"""
|
||||||
self.size_commands.board = self.board
|
self.size_commands.board = self.board
|
||||||
return self.size_commands.set_board_size(params)
|
return self.size_commands.set_board_size(params)
|
||||||
|
|
||||||
# Delegate layer commands
|
# Delegate layer commands
|
||||||
def add_layer(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def add_layer(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Add a new layer to the PCB"""
|
"""Add a new layer to the PCB"""
|
||||||
self.layer_commands.board = self.board
|
self.layer_commands.board = self.board
|
||||||
return self.layer_commands.add_layer(params)
|
return self.layer_commands.add_layer(params)
|
||||||
|
|
||||||
def set_active_layer(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def set_active_layer(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Set the active layer for PCB operations"""
|
"""Set the active layer for PCB operations"""
|
||||||
self.layer_commands.board = self.board
|
self.layer_commands.board = self.board
|
||||||
return self.layer_commands.set_active_layer(params)
|
return self.layer_commands.set_active_layer(params)
|
||||||
|
|
||||||
def get_layer_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def get_layer_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Get a list of all layers in the PCB"""
|
"""Get a list of all layers in the PCB"""
|
||||||
self.layer_commands.board = self.board
|
self.layer_commands.board = self.board
|
||||||
return self.layer_commands.get_layer_list(params)
|
return self.layer_commands.get_layer_list(params)
|
||||||
|
|
||||||
# Delegate board outline commands
|
# Delegate board outline commands
|
||||||
def add_board_outline(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def add_board_outline(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Add a board outline to the PCB"""
|
"""Add a board outline to the PCB"""
|
||||||
self.outline_commands.board = self.board
|
self.outline_commands.board = self.board
|
||||||
return self.outline_commands.add_board_outline(params)
|
return self.outline_commands.add_board_outline(params)
|
||||||
|
|
||||||
def add_mounting_hole(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def add_mounting_hole(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Add a mounting hole to the PCB"""
|
"""Add a mounting hole to the PCB"""
|
||||||
self.outline_commands.board = self.board
|
self.outline_commands.board = self.board
|
||||||
return self.outline_commands.add_mounting_hole(params)
|
return self.outline_commands.add_mounting_hole(params)
|
||||||
|
|
||||||
def add_text(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def add_text(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Add text annotation to the PCB"""
|
"""Add text annotation to the PCB"""
|
||||||
self.outline_commands.board = self.board
|
self.outline_commands.board = self.board
|
||||||
return self.outline_commands.add_text(params)
|
return self.outline_commands.add_text(params)
|
||||||
|
|
||||||
# Delegate view commands
|
# Delegate view commands
|
||||||
def get_board_info(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def get_board_info(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Get information about the current board"""
|
"""Get information about the current board"""
|
||||||
self.view_commands.board = self.board
|
self.view_commands.board = self.board
|
||||||
return self.view_commands.get_board_info(params)
|
return self.view_commands.get_board_info(params)
|
||||||
|
|
||||||
def get_board_2d_view(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def get_board_2d_view(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Get a 2D image of the PCB"""
|
"""Get a 2D image of the PCB"""
|
||||||
self.view_commands.board = self.board
|
self.view_commands.board = self.board
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class BoardLayerCommands:
|
|||||||
# Set layer properties
|
# Set layer properties
|
||||||
layer_stack.SetLayerName(layer_id, name)
|
layer_stack.SetLayerName(layer_id, name)
|
||||||
layer_stack.SetLayerType(layer_id, self._get_layer_type(layer_type))
|
layer_stack.SetLayerType(layer_id, self._get_layer_type(layer_type))
|
||||||
|
|
||||||
# Enable the layer
|
# Enable the layer
|
||||||
self.board.SetLayerEnabled(layer_id, True)
|
self.board.SetLayerEnabled(layer_id, True)
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ class BoardLayerCommands:
|
|||||||
"message": "Failed to get layer list",
|
"message": "Failed to get layer list",
|
||||||
"errorDetails": str(e)
|
"errorDetails": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_layer_type(self, type_name: str) -> int:
|
def _get_layer_type(self, type_name: str) -> int:
|
||||||
"""Convert layer type name to KiCAD layer type constant"""
|
"""Convert layer type name to KiCAD layer type constant"""
|
||||||
type_map = {
|
type_map = {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class BoardViewCommands:
|
|||||||
|
|
||||||
# Create plot controller
|
# Create plot controller
|
||||||
plotter = pcbnew.PLOT_CONTROLLER(self.board)
|
plotter = pcbnew.PLOT_CONTROLLER(self.board)
|
||||||
|
|
||||||
# Set up plot options
|
# Set up plot options
|
||||||
plot_opts = plotter.GetPlotOptions()
|
plot_opts = plotter.GetPlotOptions()
|
||||||
plot_opts.SetOutputDirectory(os.path.dirname(self.board.GetFileName()))
|
plot_opts.SetOutputDirectory(os.path.dirname(self.board.GetFileName()))
|
||||||
@@ -100,7 +100,7 @@ class BoardViewCommands:
|
|||||||
plot_opts.SetPlotFrameRef(False)
|
plot_opts.SetPlotFrameRef(False)
|
||||||
plot_opts.SetPlotValue(True)
|
plot_opts.SetPlotValue(True)
|
||||||
plot_opts.SetPlotReference(True)
|
plot_opts.SetPlotReference(True)
|
||||||
|
|
||||||
# Plot to SVG first (for vector output)
|
# Plot to SVG first (for vector output)
|
||||||
# Note: KiCAD 9.0 prepends the project name to the filename, so we use GetPlotFileName() to get the actual path
|
# Note: KiCAD 9.0 prepends the project name to the filename, so we use GetPlotFileName() to get the actual path
|
||||||
plotter.OpenPlotfile("temp_view", pcbnew.PLOT_FORMAT_SVG, "Temporary View")
|
plotter.OpenPlotfile("temp_view", pcbnew.PLOT_FORMAT_SVG, "Temporary View")
|
||||||
@@ -139,7 +139,7 @@ class BoardViewCommands:
|
|||||||
from cairosvg import svg2png
|
from cairosvg import svg2png
|
||||||
png_data = svg2png(url=temp_svg, output_width=width, output_height=height)
|
png_data = svg2png(url=temp_svg, output_width=width, output_height=height)
|
||||||
os.remove(temp_svg)
|
os.remove(temp_svg)
|
||||||
|
|
||||||
if format == "jpg":
|
if format == "jpg":
|
||||||
# Convert PNG to JPG
|
# Convert PNG to JPG
|
||||||
img = Image.open(io.BytesIO(png_data))
|
img = Image.open(io.BytesIO(png_data))
|
||||||
@@ -165,7 +165,7 @@ class BoardViewCommands:
|
|||||||
"message": "Failed to get board 2D view",
|
"message": "Failed to get board 2D view",
|
||||||
"errorDetails": str(e)
|
"errorDetails": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_layer_type_name(self, type_id: int) -> str:
|
def _get_layer_type_name(self, type_id: int) -> str:
|
||||||
"""Convert KiCAD layer type constant to name"""
|
"""Convert KiCAD layer type constant to name"""
|
||||||
type_map = {
|
type_map = {
|
||||||
|
|||||||
@@ -752,14 +752,14 @@ class ComponentCommands:
|
|||||||
count = params.get("count")
|
count = params.get("count")
|
||||||
reference_prefix = params.get("referencePrefix", "U")
|
reference_prefix = params.get("referencePrefix", "U")
|
||||||
value = params.get("value")
|
value = params.get("value")
|
||||||
|
|
||||||
if not component_id or not count:
|
if not component_id or not count:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": "Missing parameters",
|
"message": "Missing parameters",
|
||||||
"errorDetails": "componentId and count are required"
|
"errorDetails": "componentId and count are required"
|
||||||
}
|
}
|
||||||
|
|
||||||
if pattern == "grid":
|
if pattern == "grid":
|
||||||
start_position = params.get("startPosition")
|
start_position = params.get("startPosition")
|
||||||
rows = params.get("rows")
|
rows = params.get("rows")
|
||||||
@@ -768,21 +768,21 @@ class ComponentCommands:
|
|||||||
spacing_y = params.get("spacingY")
|
spacing_y = params.get("spacingY")
|
||||||
rotation = params.get("rotation", 0)
|
rotation = params.get("rotation", 0)
|
||||||
layer = params.get("layer", "F.Cu")
|
layer = params.get("layer", "F.Cu")
|
||||||
|
|
||||||
if not start_position or not rows or not columns or not spacing_x or not spacing_y:
|
if not start_position or not rows or not columns or not spacing_x or not spacing_y:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": "Missing grid parameters",
|
"message": "Missing grid parameters",
|
||||||
"errorDetails": "For grid pattern, startPosition, rows, columns, spacingX, and spacingY are required"
|
"errorDetails": "For grid pattern, startPosition, rows, columns, spacingX, and spacingY are required"
|
||||||
}
|
}
|
||||||
|
|
||||||
if rows * columns != count:
|
if rows * columns != count:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": "Invalid grid parameters",
|
"message": "Invalid grid parameters",
|
||||||
"errorDetails": "rows * columns must equal count"
|
"errorDetails": "rows * columns must equal count"
|
||||||
}
|
}
|
||||||
|
|
||||||
placed_components = self._place_grid_array(
|
placed_components = self._place_grid_array(
|
||||||
component_id,
|
component_id,
|
||||||
start_position,
|
start_position,
|
||||||
@@ -795,7 +795,7 @@ class ComponentCommands:
|
|||||||
rotation,
|
rotation,
|
||||||
layer
|
layer
|
||||||
)
|
)
|
||||||
|
|
||||||
elif pattern == "circular":
|
elif pattern == "circular":
|
||||||
center = params.get("center")
|
center = params.get("center")
|
||||||
radius = params.get("radius")
|
radius = params.get("radius")
|
||||||
@@ -803,14 +803,14 @@ class ComponentCommands:
|
|||||||
angle_step = params.get("angleStep")
|
angle_step = params.get("angleStep")
|
||||||
rotation_offset = params.get("rotationOffset", 0)
|
rotation_offset = params.get("rotationOffset", 0)
|
||||||
layer = params.get("layer", "F.Cu")
|
layer = params.get("layer", "F.Cu")
|
||||||
|
|
||||||
if not center or not radius or not angle_step:
|
if not center or not radius or not angle_step:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": "Missing circular parameters",
|
"message": "Missing circular parameters",
|
||||||
"errorDetails": "For circular pattern, center, radius, and angleStep are required"
|
"errorDetails": "For circular pattern, center, radius, and angleStep are required"
|
||||||
}
|
}
|
||||||
|
|
||||||
placed_components = self._place_circular_array(
|
placed_components = self._place_circular_array(
|
||||||
component_id,
|
component_id,
|
||||||
center,
|
center,
|
||||||
@@ -823,7 +823,7 @@ class ComponentCommands:
|
|||||||
rotation_offset,
|
rotation_offset,
|
||||||
layer
|
layer
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
@@ -844,7 +844,7 @@ class ComponentCommands:
|
|||||||
"message": "Failed to place component array",
|
"message": "Failed to place component array",
|
||||||
"errorDetails": str(e)
|
"errorDetails": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
def align_components(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def align_components(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Align multiple components along a line or distribute them evenly"""
|
"""Align multiple components along a line or distribute them evenly"""
|
||||||
try:
|
try:
|
||||||
@@ -859,14 +859,14 @@ class ComponentCommands:
|
|||||||
alignment = params.get("alignment", "horizontal") # horizontal, vertical, or edge
|
alignment = params.get("alignment", "horizontal") # horizontal, vertical, or edge
|
||||||
distribution = params.get("distribution", "none") # none, equal, or spacing
|
distribution = params.get("distribution", "none") # none, equal, or spacing
|
||||||
spacing = params.get("spacing")
|
spacing = params.get("spacing")
|
||||||
|
|
||||||
if not references or len(references) < 2:
|
if not references or len(references) < 2:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": "Missing references",
|
"message": "Missing references",
|
||||||
"errorDetails": "At least two component references are required"
|
"errorDetails": "At least two component references are required"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Find all referenced components
|
# Find all referenced components
|
||||||
components = []
|
components = []
|
||||||
for ref in references:
|
for ref in references:
|
||||||
@@ -878,7 +878,7 @@ class ComponentCommands:
|
|||||||
"errorDetails": f"Could not find component: {ref}"
|
"errorDetails": f"Could not find component: {ref}"
|
||||||
}
|
}
|
||||||
components.append(module)
|
components.append(module)
|
||||||
|
|
||||||
# Perform alignment based on selected option
|
# Perform alignment based on selected option
|
||||||
if alignment == "horizontal":
|
if alignment == "horizontal":
|
||||||
self._align_components_horizontally(components, distribution, spacing)
|
self._align_components_horizontally(components, distribution, spacing)
|
||||||
@@ -929,7 +929,7 @@ class ComponentCommands:
|
|||||||
"message": "Failed to align components",
|
"message": "Failed to align components",
|
||||||
"errorDetails": str(e)
|
"errorDetails": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
def duplicate_component(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def duplicate_component(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Duplicate an existing component"""
|
"""Duplicate an existing component"""
|
||||||
try:
|
try:
|
||||||
@@ -944,14 +944,14 @@ class ComponentCommands:
|
|||||||
new_reference = params.get("newReference")
|
new_reference = params.get("newReference")
|
||||||
position = params.get("position")
|
position = params.get("position")
|
||||||
rotation = params.get("rotation")
|
rotation = params.get("rotation")
|
||||||
|
|
||||||
if not reference or not new_reference:
|
if not reference or not new_reference:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": "Missing parameters",
|
"message": "Missing parameters",
|
||||||
"errorDetails": "reference and newReference are required"
|
"errorDetails": "reference and newReference are required"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Find the source component
|
# Find the source component
|
||||||
source = self.board.FindFootprintByReference(reference)
|
source = self.board.FindFootprintByReference(reference)
|
||||||
if not source:
|
if not source:
|
||||||
@@ -960,7 +960,7 @@ class ComponentCommands:
|
|||||||
"message": "Component not found",
|
"message": "Component not found",
|
||||||
"errorDetails": f"Could not find component: {reference}"
|
"errorDetails": f"Could not find component: {reference}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if new reference already exists
|
# Check if new reference already exists
|
||||||
if self.board.FindFootprintByReference(new_reference):
|
if self.board.FindFootprintByReference(new_reference):
|
||||||
return {
|
return {
|
||||||
@@ -968,7 +968,7 @@ class ComponentCommands:
|
|||||||
"message": "Reference already exists",
|
"message": "Reference already exists",
|
||||||
"errorDetails": f"A component with reference {new_reference} already exists"
|
"errorDetails": f"A component with reference {new_reference} already exists"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create new footprint with the same properties
|
# Create new footprint with the same properties
|
||||||
new_module = pcbnew.FOOTPRINT(self.board)
|
new_module = pcbnew.FOOTPRINT(self.board)
|
||||||
# For KiCAD 9.x compatibility, use SetFPID instead of SetFootprintName
|
# For KiCAD 9.x compatibility, use SetFPID instead of SetFootprintName
|
||||||
@@ -976,13 +976,13 @@ class ComponentCommands:
|
|||||||
new_module.SetValue(source.GetValue())
|
new_module.SetValue(source.GetValue())
|
||||||
new_module.SetReference(new_reference)
|
new_module.SetReference(new_reference)
|
||||||
new_module.SetLayer(source.GetLayer())
|
new_module.SetLayer(source.GetLayer())
|
||||||
|
|
||||||
# Copy pads and other items
|
# Copy pads and other items
|
||||||
for pad in source.Pads():
|
for pad in source.Pads():
|
||||||
new_pad = pcbnew.PAD(new_module)
|
new_pad = pcbnew.PAD(new_module)
|
||||||
new_pad.Copy(pad)
|
new_pad.Copy(pad)
|
||||||
new_module.Add(new_pad)
|
new_module.Add(new_pad)
|
||||||
|
|
||||||
# Set position if provided, otherwise use offset from original
|
# Set position if provided, otherwise use offset from original
|
||||||
if position:
|
if position:
|
||||||
scale = 1000000 if position.get("unit", "mm") == "mm" else 25400000
|
scale = 1000000 if position.get("unit", "mm") == "mm" else 25400000
|
||||||
@@ -993,17 +993,17 @@ class ComponentCommands:
|
|||||||
# Offset by 5mm
|
# Offset by 5mm
|
||||||
source_pos = source.GetPosition()
|
source_pos = source.GetPosition()
|
||||||
new_module.SetPosition(pcbnew.VECTOR2I(source_pos.x + 5000000, source_pos.y))
|
new_module.SetPosition(pcbnew.VECTOR2I(source_pos.x + 5000000, source_pos.y))
|
||||||
|
|
||||||
# Set rotation if provided, otherwise use same as original
|
# Set rotation if provided, otherwise use same as original
|
||||||
if rotation is not None:
|
if rotation is not None:
|
||||||
rotation_angle = pcbnew.EDA_ANGLE(rotation, pcbnew.DEGREES_T)
|
rotation_angle = pcbnew.EDA_ANGLE(rotation, pcbnew.DEGREES_T)
|
||||||
new_module.SetOrientation(rotation_angle)
|
new_module.SetOrientation(rotation_angle)
|
||||||
else:
|
else:
|
||||||
new_module.SetOrientation(source.GetOrientation())
|
new_module.SetOrientation(source.GetOrientation())
|
||||||
|
|
||||||
# Add to board
|
# Add to board
|
||||||
self.board.Add(new_module)
|
self.board.Add(new_module)
|
||||||
|
|
||||||
# Get final position in mm
|
# Get final position in mm
|
||||||
pos = new_module.GetPosition()
|
pos = new_module.GetPosition()
|
||||||
|
|
||||||
@@ -1031,32 +1031,32 @@ class ComponentCommands:
|
|||||||
"message": "Failed to duplicate component",
|
"message": "Failed to duplicate component",
|
||||||
"errorDetails": str(e)
|
"errorDetails": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
def _place_grid_array(self, component_id: str, start_position: Dict[str, Any],
|
def _place_grid_array(self, component_id: str, start_position: Dict[str, Any],
|
||||||
rows: int, columns: int, spacing_x: float, spacing_y: float,
|
rows: int, columns: int, spacing_x: float, spacing_y: float,
|
||||||
reference_prefix: str, value: str, rotation: float, layer: str) -> List[Dict[str, Any]]:
|
reference_prefix: str, value: str, rotation: float, layer: str) -> List[Dict[str, Any]]:
|
||||||
"""Place components in a grid pattern and return the list of placed components"""
|
"""Place components in a grid pattern and return the list of placed components"""
|
||||||
placed = []
|
placed = []
|
||||||
|
|
||||||
# Convert spacing to nm
|
# Convert spacing to nm
|
||||||
unit = start_position.get("unit", "mm")
|
unit = start_position.get("unit", "mm")
|
||||||
scale = 1000000 if unit == "mm" else 25400000 # mm or inch to nm
|
scale = 1000000 if unit == "mm" else 25400000 # mm or inch to nm
|
||||||
spacing_x_nm = int(spacing_x * scale)
|
spacing_x_nm = int(spacing_x * scale)
|
||||||
spacing_y_nm = int(spacing_y * scale)
|
spacing_y_nm = int(spacing_y * scale)
|
||||||
|
|
||||||
# Get layer ID
|
# Get layer ID
|
||||||
layer_id = self.board.GetLayerID(layer)
|
layer_id = self.board.GetLayerID(layer)
|
||||||
|
|
||||||
for row in range(rows):
|
for row in range(rows):
|
||||||
for col in range(columns):
|
for col in range(columns):
|
||||||
# Calculate position
|
# Calculate position
|
||||||
x = start_position["x"] + (col * spacing_x)
|
x = start_position["x"] + (col * spacing_x)
|
||||||
y = start_position["y"] + (row * spacing_y)
|
y = start_position["y"] + (row * spacing_y)
|
||||||
|
|
||||||
# Generate reference
|
# Generate reference
|
||||||
index = row * columns + col + 1
|
index = row * columns + col + 1
|
||||||
component_reference = f"{reference_prefix}{index}"
|
component_reference = f"{reference_prefix}{index}"
|
||||||
|
|
||||||
# Place component
|
# Place component
|
||||||
result = self.place_component({
|
result = self.place_component({
|
||||||
"componentId": component_id,
|
"componentId": component_id,
|
||||||
@@ -1066,37 +1066,37 @@ class ComponentCommands:
|
|||||||
"rotation": rotation,
|
"rotation": rotation,
|
||||||
"layer": layer
|
"layer": layer
|
||||||
})
|
})
|
||||||
|
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
placed.append(result["component"])
|
placed.append(result["component"])
|
||||||
|
|
||||||
return placed
|
return placed
|
||||||
|
|
||||||
def _place_circular_array(self, component_id: str, center: Dict[str, Any],
|
def _place_circular_array(self, component_id: str, center: Dict[str, Any],
|
||||||
radius: float, count: int, angle_start: float,
|
radius: float, count: int, angle_start: float,
|
||||||
angle_step: float, reference_prefix: str,
|
angle_step: float, reference_prefix: str,
|
||||||
value: str, rotation_offset: float, layer: str) -> List[Dict[str, Any]]:
|
value: str, rotation_offset: float, layer: str) -> List[Dict[str, Any]]:
|
||||||
"""Place components in a circular pattern and return the list of placed components"""
|
"""Place components in a circular pattern and return the list of placed components"""
|
||||||
placed = []
|
placed = []
|
||||||
|
|
||||||
# Get unit
|
# Get unit
|
||||||
unit = center.get("unit", "mm")
|
unit = center.get("unit", "mm")
|
||||||
|
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
# Calculate angle for this component
|
# Calculate angle for this component
|
||||||
angle = angle_start + (i * angle_step)
|
angle = angle_start + (i * angle_step)
|
||||||
angle_rad = math.radians(angle)
|
angle_rad = math.radians(angle)
|
||||||
|
|
||||||
# Calculate position
|
# Calculate position
|
||||||
x = center["x"] + (radius * math.cos(angle_rad))
|
x = center["x"] + (radius * math.cos(angle_rad))
|
||||||
y = center["y"] + (radius * math.sin(angle_rad))
|
y = center["y"] + (radius * math.sin(angle_rad))
|
||||||
|
|
||||||
# Generate reference
|
# Generate reference
|
||||||
component_reference = f"{reference_prefix}{i+1}"
|
component_reference = f"{reference_prefix}{i+1}"
|
||||||
|
|
||||||
# Calculate rotation (pointing outward from center)
|
# Calculate rotation (pointing outward from center)
|
||||||
component_rotation = angle + rotation_offset
|
component_rotation = angle + rotation_offset
|
||||||
|
|
||||||
# Place component
|
# Place component
|
||||||
result = self.place_component({
|
result = self.place_component({
|
||||||
"componentId": component_id,
|
"componentId": component_id,
|
||||||
@@ -1106,114 +1106,114 @@ class ComponentCommands:
|
|||||||
"rotation": component_rotation,
|
"rotation": component_rotation,
|
||||||
"layer": layer
|
"layer": layer
|
||||||
})
|
})
|
||||||
|
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
placed.append(result["component"])
|
placed.append(result["component"])
|
||||||
|
|
||||||
return placed
|
return placed
|
||||||
|
|
||||||
def _align_components_horizontally(self, components: List[pcbnew.FOOTPRINT],
|
def _align_components_horizontally(self, components: List[pcbnew.FOOTPRINT],
|
||||||
distribution: str, spacing: Optional[float]) -> None:
|
distribution: str, spacing: Optional[float]) -> None:
|
||||||
"""Align components horizontally and optionally distribute them"""
|
"""Align components horizontally and optionally distribute them"""
|
||||||
if not components:
|
if not components:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Find the average Y coordinate
|
# Find the average Y coordinate
|
||||||
y_sum = sum(module.GetPosition().y for module in components)
|
y_sum = sum(module.GetPosition().y for module in components)
|
||||||
y_avg = y_sum // len(components)
|
y_avg = y_sum // len(components)
|
||||||
|
|
||||||
# Sort components by X position
|
# Sort components by X position
|
||||||
components.sort(key=lambda m: m.GetPosition().x)
|
components.sort(key=lambda m: m.GetPosition().x)
|
||||||
|
|
||||||
# Set Y coordinate for all components
|
# Set Y coordinate for all components
|
||||||
for module in components:
|
for module in components:
|
||||||
pos = module.GetPosition()
|
pos = module.GetPosition()
|
||||||
module.SetPosition(pcbnew.VECTOR2I(pos.x, y_avg))
|
module.SetPosition(pcbnew.VECTOR2I(pos.x, y_avg))
|
||||||
|
|
||||||
# Handle distribution if requested
|
# Handle distribution if requested
|
||||||
if distribution == "equal" and len(components) > 1:
|
if distribution == "equal" and len(components) > 1:
|
||||||
# Get leftmost and rightmost X coordinates
|
# Get leftmost and rightmost X coordinates
|
||||||
x_min = components[0].GetPosition().x
|
x_min = components[0].GetPosition().x
|
||||||
x_max = components[-1].GetPosition().x
|
x_max = components[-1].GetPosition().x
|
||||||
|
|
||||||
# Calculate equal spacing
|
# Calculate equal spacing
|
||||||
total_space = x_max - x_min
|
total_space = x_max - x_min
|
||||||
spacing_nm = total_space // (len(components) - 1)
|
spacing_nm = total_space // (len(components) - 1)
|
||||||
|
|
||||||
# Set X positions with equal spacing
|
# Set X positions with equal spacing
|
||||||
for i in range(1, len(components) - 1):
|
for i in range(1, len(components) - 1):
|
||||||
pos = components[i].GetPosition()
|
pos = components[i].GetPosition()
|
||||||
new_x = x_min + (i * spacing_nm)
|
new_x = x_min + (i * spacing_nm)
|
||||||
components[i].SetPosition(pcbnew.VECTOR2I(new_x, pos.y))
|
components[i].SetPosition(pcbnew.VECTOR2I(new_x, pos.y))
|
||||||
|
|
||||||
elif distribution == "spacing" and spacing is not None:
|
elif distribution == "spacing" and spacing is not None:
|
||||||
# Convert spacing to nanometers
|
# Convert spacing to nanometers
|
||||||
spacing_nm = int(spacing * 1000000) # assuming mm
|
spacing_nm = int(spacing * 1000000) # assuming mm
|
||||||
|
|
||||||
# Set X positions with the specified spacing
|
# Set X positions with the specified spacing
|
||||||
x_current = components[0].GetPosition().x
|
x_current = components[0].GetPosition().x
|
||||||
for i in range(1, len(components)):
|
for i in range(1, len(components)):
|
||||||
pos = components[i].GetPosition()
|
pos = components[i].GetPosition()
|
||||||
x_current += spacing_nm
|
x_current += spacing_nm
|
||||||
components[i].SetPosition(pcbnew.VECTOR2I(x_current, pos.y))
|
components[i].SetPosition(pcbnew.VECTOR2I(x_current, pos.y))
|
||||||
|
|
||||||
def _align_components_vertically(self, components: List[pcbnew.FOOTPRINT],
|
def _align_components_vertically(self, components: List[pcbnew.FOOTPRINT],
|
||||||
distribution: str, spacing: Optional[float]) -> None:
|
distribution: str, spacing: Optional[float]) -> None:
|
||||||
"""Align components vertically and optionally distribute them"""
|
"""Align components vertically and optionally distribute them"""
|
||||||
if not components:
|
if not components:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Find the average X coordinate
|
# Find the average X coordinate
|
||||||
x_sum = sum(module.GetPosition().x for module in components)
|
x_sum = sum(module.GetPosition().x for module in components)
|
||||||
x_avg = x_sum // len(components)
|
x_avg = x_sum // len(components)
|
||||||
|
|
||||||
# Sort components by Y position
|
# Sort components by Y position
|
||||||
components.sort(key=lambda m: m.GetPosition().y)
|
components.sort(key=lambda m: m.GetPosition().y)
|
||||||
|
|
||||||
# Set X coordinate for all components
|
# Set X coordinate for all components
|
||||||
for module in components:
|
for module in components:
|
||||||
pos = module.GetPosition()
|
pos = module.GetPosition()
|
||||||
module.SetPosition(pcbnew.VECTOR2I(x_avg, pos.y))
|
module.SetPosition(pcbnew.VECTOR2I(x_avg, pos.y))
|
||||||
|
|
||||||
# Handle distribution if requested
|
# Handle distribution if requested
|
||||||
if distribution == "equal" and len(components) > 1:
|
if distribution == "equal" and len(components) > 1:
|
||||||
# Get topmost and bottommost Y coordinates
|
# Get topmost and bottommost Y coordinates
|
||||||
y_min = components[0].GetPosition().y
|
y_min = components[0].GetPosition().y
|
||||||
y_max = components[-1].GetPosition().y
|
y_max = components[-1].GetPosition().y
|
||||||
|
|
||||||
# Calculate equal spacing
|
# Calculate equal spacing
|
||||||
total_space = y_max - y_min
|
total_space = y_max - y_min
|
||||||
spacing_nm = total_space // (len(components) - 1)
|
spacing_nm = total_space // (len(components) - 1)
|
||||||
|
|
||||||
# Set Y positions with equal spacing
|
# Set Y positions with equal spacing
|
||||||
for i in range(1, len(components) - 1):
|
for i in range(1, len(components) - 1):
|
||||||
pos = components[i].GetPosition()
|
pos = components[i].GetPosition()
|
||||||
new_y = y_min + (i * spacing_nm)
|
new_y = y_min + (i * spacing_nm)
|
||||||
components[i].SetPosition(pcbnew.VECTOR2I(pos.x, new_y))
|
components[i].SetPosition(pcbnew.VECTOR2I(pos.x, new_y))
|
||||||
|
|
||||||
elif distribution == "spacing" and spacing is not None:
|
elif distribution == "spacing" and spacing is not None:
|
||||||
# Convert spacing to nanometers
|
# Convert spacing to nanometers
|
||||||
spacing_nm = int(spacing * 1000000) # assuming mm
|
spacing_nm = int(spacing * 1000000) # assuming mm
|
||||||
|
|
||||||
# Set Y positions with the specified spacing
|
# Set Y positions with the specified spacing
|
||||||
y_current = components[0].GetPosition().y
|
y_current = components[0].GetPosition().y
|
||||||
for i in range(1, len(components)):
|
for i in range(1, len(components)):
|
||||||
pos = components[i].GetPosition()
|
pos = components[i].GetPosition()
|
||||||
y_current += spacing_nm
|
y_current += spacing_nm
|
||||||
components[i].SetPosition(pcbnew.VECTOR2I(pos.x, y_current))
|
components[i].SetPosition(pcbnew.VECTOR2I(pos.x, y_current))
|
||||||
|
|
||||||
def _align_components_to_edge(self, components: List[pcbnew.FOOTPRINT], edge: str) -> None:
|
def _align_components_to_edge(self, components: List[pcbnew.FOOTPRINT], edge: str) -> None:
|
||||||
"""Align components to the specified edge of the board"""
|
"""Align components to the specified edge of the board"""
|
||||||
if not components:
|
if not components:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get board bounds
|
# Get board bounds
|
||||||
board_box = self.board.GetBoardEdgesBoundingBox()
|
board_box = self.board.GetBoardEdgesBoundingBox()
|
||||||
left = board_box.GetLeft()
|
left = board_box.GetLeft()
|
||||||
right = board_box.GetRight()
|
right = board_box.GetRight()
|
||||||
top = board_box.GetTop()
|
top = board_box.GetTop()
|
||||||
bottom = board_box.GetBottom()
|
bottom = board_box.GetBottom()
|
||||||
|
|
||||||
# Align based on specified edge
|
# Align based on specified edge
|
||||||
if edge == "left":
|
if edge == "left":
|
||||||
for module in components:
|
for module in components:
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ class LibraryCommands:
|
|||||||
if path == library_path:
|
if path == library_path:
|
||||||
library_nickname = nick
|
library_nickname = nick
|
||||||
break
|
break
|
||||||
|
|
||||||
# Minimal info — always returned even if the parser fails
|
# Minimal info — always returned even if the parser fails
|
||||||
info: Dict = {
|
info: Dict = {
|
||||||
"library": library_nickname,
|
"library": library_nickname,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class LibraryManager:
|
|||||||
# Extract library names from paths
|
# Extract library names from paths
|
||||||
library_names = [os.path.splitext(os.path.basename(lib))[0] for lib in libraries]
|
library_names = [os.path.splitext(os.path.basename(lib))[0] for lib in libraries]
|
||||||
print(f"Found {len(library_names)} libraries: {', '.join(library_names[:10])}{'...' if len(library_names) > 10 else ''}")
|
print(f"Found {len(library_names)} libraries: {', '.join(library_names[:10])}{'...' if len(library_names) > 10 else ''}")
|
||||||
|
|
||||||
# Return both full paths and library names
|
# Return both full paths and library names
|
||||||
return {"paths": libraries, "names": library_names}
|
return {"paths": libraries, "names": library_names}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class LibraryManager:
|
|||||||
# without loading each one. We might need to implement this using KiCAD's Python API
|
# without loading each one. We might need to implement this using KiCAD's Python API
|
||||||
# directly, or by using a different approach.
|
# directly, or by using a different approach.
|
||||||
# For now, this is a placeholder implementation.
|
# For now, this is a placeholder implementation.
|
||||||
|
|
||||||
# A potential approach would be to load the library file using KiCAD's Python API
|
# A potential approach would be to load the library file using KiCAD's Python API
|
||||||
# or by parsing the library file format.
|
# or by parsing the library file format.
|
||||||
# KiCAD symbol libraries are .kicad_sym files which are S-expression format
|
# KiCAD symbol libraries are .kicad_sym files which are S-expression format
|
||||||
@@ -73,23 +73,23 @@ class LibraryManager:
|
|||||||
# 1. Getting a list of all libraries using list_available_libraries
|
# 1. Getting a list of all libraries using list_available_libraries
|
||||||
# 2. For each library, getting a list of all symbols
|
# 2. For each library, getting a list of all symbols
|
||||||
# 3. Filtering symbols based on the query
|
# 3. Filtering symbols based on the query
|
||||||
|
|
||||||
# For now, this is a placeholder implementation
|
# For now, this is a placeholder implementation
|
||||||
libraries = LibraryManager.list_available_libraries(search_paths)
|
libraries = LibraryManager.list_available_libraries(search_paths)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
print(f"Searched for symbols matching '{query}'. This requires advanced implementation.")
|
print(f"Searched for symbols matching '{query}'. This requires advanced implementation.")
|
||||||
return results
|
return results
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error searching for symbols matching '{query}': {e}")
|
print(f"Error searching for symbols matching '{query}': {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_symbol_for_component_type(component_type, search_paths=None):
|
def get_default_symbol_for_component_type(component_type, search_paths=None):
|
||||||
"""Get a recommended default symbol for a given component type"""
|
"""Get a recommended default symbol for a given component type"""
|
||||||
# This method provides a simplified way to get a symbol for common component types
|
# This method provides a simplified way to get a symbol for common component types
|
||||||
# It's useful when the user doesn't specify a particular library/symbol
|
# It's useful when the user doesn't specify a particular library/symbol
|
||||||
|
|
||||||
# Define common mappings from component type to library/symbol
|
# Define common mappings from component type to library/symbol
|
||||||
common_mappings = {
|
common_mappings = {
|
||||||
"resistor": {"library": "Device", "symbol": "R"},
|
"resistor": {"library": "Device", "symbol": "R"},
|
||||||
@@ -103,19 +103,19 @@ class LibraryManager:
|
|||||||
"microcontroller": {"library": "MCU_Module", "symbol": "Arduino_UNO_R3"},
|
"microcontroller": {"library": "MCU_Module", "symbol": "Arduino_UNO_R3"},
|
||||||
# Add more common components as needed
|
# Add more common components as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
# Normalize input to lowercase
|
# Normalize input to lowercase
|
||||||
component_type_lower = component_type.lower()
|
component_type_lower = component_type.lower()
|
||||||
|
|
||||||
# Try direct match first
|
# Try direct match first
|
||||||
if component_type_lower in common_mappings:
|
if component_type_lower in common_mappings:
|
||||||
return common_mappings[component_type_lower]
|
return common_mappings[component_type_lower]
|
||||||
|
|
||||||
# Try partial matches
|
# Try partial matches
|
||||||
for key, value in common_mappings.items():
|
for key, value in common_mappings.items():
|
||||||
if component_type_lower in key or key in component_type_lower:
|
if component_type_lower in key or key in component_type_lower:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# Default fallback
|
# Default fallback
|
||||||
return {"library": "Device", "symbol": "R"}
|
return {"library": "Device", "symbol": "R"}
|
||||||
|
|
||||||
@@ -127,15 +127,15 @@ if __name__ == '__main__':
|
|||||||
first_lib = libraries["paths"][0]
|
first_lib = libraries["paths"][0]
|
||||||
lib_name = libraries["names"][0]
|
lib_name = libraries["names"][0]
|
||||||
print(f"Testing with first library: {lib_name} ({first_lib})")
|
print(f"Testing with first library: {lib_name} ({first_lib})")
|
||||||
|
|
||||||
# List symbols in the first library
|
# List symbols in the first library
|
||||||
symbols = LibraryManager.list_library_symbols(first_lib)
|
symbols = LibraryManager.list_library_symbols(first_lib)
|
||||||
# This will report that it requires advanced implementation
|
# This will report that it requires advanced implementation
|
||||||
|
|
||||||
# Get default symbol for a component type
|
# Get default symbol for a component type
|
||||||
resistor_sym = LibraryManager.get_default_symbol_for_component_type("resistor")
|
resistor_sym = LibraryManager.get_default_symbol_for_component_type("resistor")
|
||||||
print(f"Default symbol for resistor: {resistor_sym['library']}/{resistor_sym['symbol']}")
|
print(f"Default symbol for resistor: {resistor_sym['library']}/{resistor_sym['symbol']}")
|
||||||
|
|
||||||
# Try a partial match
|
# Try a partial match
|
||||||
cap_sym = LibraryManager.get_default_symbol_for_component_type("cap")
|
cap_sym = LibraryManager.get_default_symbol_for_component_type("cap")
|
||||||
print(f"Default symbol for 'cap': {cap_sym['library']}/{cap_sym['symbol']}")
|
print(f"Default symbol for 'cap': {cap_sym['library']}/{cap_sym['symbol']}")
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export type Config = z.infer<typeof ConfigSchema>;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load configuration from file
|
* Load configuration from file
|
||||||
*
|
*
|
||||||
* @param configPath Path to the configuration file (optional)
|
* @param configPath Path to the configuration file (optional)
|
||||||
* @returns Loaded and validated configuration
|
* @returns Loaded and validated configuration
|
||||||
*/
|
*/
|
||||||
@@ -44,22 +44,22 @@ export async function loadConfig(configPath?: string): Promise<Config> {
|
|||||||
try {
|
try {
|
||||||
// Determine which config file to load
|
// Determine which config file to load
|
||||||
const filePath = configPath || DEFAULT_CONFIG_PATH;
|
const filePath = configPath || DEFAULT_CONFIG_PATH;
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
if (!existsSync(filePath)) {
|
if (!existsSync(filePath)) {
|
||||||
logger.warn(`Configuration file not found: ${filePath}, using defaults`);
|
logger.warn(`Configuration file not found: ${filePath}, using defaults`);
|
||||||
return ConfigSchema.parse({});
|
return ConfigSchema.parse({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and parse configuration
|
// Read and parse configuration
|
||||||
const configData = await readFile(filePath, 'utf-8');
|
const configData = await readFile(filePath, 'utf-8');
|
||||||
const config = JSON.parse(configData);
|
const config = JSON.parse(configData);
|
||||||
|
|
||||||
// Validate configuration
|
// Validate configuration
|
||||||
return ConfigSchema.parse(config);
|
return ConfigSchema.parse(config);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error loading configuration: ${error}`);
|
logger.error(`Error loading configuration: ${error}`);
|
||||||
|
|
||||||
// Return default configuration
|
// Return default configuration
|
||||||
return ConfigSchema.parse({});
|
return ConfigSchema.parse({});
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/index.ts
24
src/index.ts
@@ -21,27 +21,27 @@ async function main() {
|
|||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const options = parseCommandLineArgs(args);
|
const options = parseCommandLineArgs(args);
|
||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
const config = await loadConfig(options.configPath);
|
const config = await loadConfig(options.configPath);
|
||||||
|
|
||||||
// Path to the Python script that interfaces with KiCAD
|
// Path to the Python script that interfaces with KiCAD
|
||||||
const kicadScriptPath = join(dirname(__dirname), 'python', 'kicad_interface.py');
|
const kicadScriptPath = join(dirname(__dirname), 'python', 'kicad_interface.py');
|
||||||
|
|
||||||
// Create the server
|
// Create the server
|
||||||
const server = new KiCADMcpServer(
|
const server = new KiCADMcpServer(
|
||||||
kicadScriptPath,
|
kicadScriptPath,
|
||||||
config.logLevel
|
config.logLevel
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
await server.start();
|
await server.start();
|
||||||
|
|
||||||
// Setup graceful shutdown
|
// Setup graceful shutdown
|
||||||
setupGracefulShutdown(server);
|
setupGracefulShutdown(server);
|
||||||
|
|
||||||
logger.info('KiCAD MCP server started with STDIO transport');
|
logger.info('KiCAD MCP server started with STDIO transport');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to start KiCAD MCP server: ${error}`);
|
logger.error(`Failed to start KiCAD MCP server: ${error}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -53,14 +53,14 @@ async function main() {
|
|||||||
*/
|
*/
|
||||||
function parseCommandLineArgs(args: string[]) {
|
function parseCommandLineArgs(args: string[]) {
|
||||||
let configPath = undefined;
|
let configPath = undefined;
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
if (args[i] === '--config' && i + 1 < args.length) {
|
if (args[i] === '--config' && i + 1 < args.length) {
|
||||||
configPath = args[i + 1];
|
configPath = args[i + 1];
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { configPath };
|
return { configPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,18 +73,18 @@ function setupGracefulShutdown(server: KiCADMcpServer) {
|
|||||||
logger.info('Received SIGINT signal. Shutting down...');
|
logger.info('Received SIGINT signal. Shutting down...');
|
||||||
await shutdownServer(server);
|
await shutdownServer(server);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
process.on('SIGTERM', async () => {
|
||||||
logger.info('Received SIGTERM signal. Shutting down...');
|
logger.info('Received SIGTERM signal. Shutting down...');
|
||||||
await shutdownServer(server);
|
await shutdownServer(server);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle uncaught exceptions
|
// Handle uncaught exceptions
|
||||||
process.on('uncaughtException', async (error) => {
|
process.on('uncaughtException', async (error) => {
|
||||||
logger.error(`Uncaught exception: ${error}`);
|
logger.error(`Uncaught exception: ${error}`);
|
||||||
await shutdownServer(server);
|
await shutdownServer(server);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle unhandled promise rejections
|
// Handle unhandled promise rejections
|
||||||
process.on('unhandledRejection', async (reason) => {
|
process.on('unhandledRejection', async (reason) => {
|
||||||
logger.error(`Unhandled promise rejection: ${reason}`);
|
logger.error(`Unhandled promise rejection: ${reason}`);
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ class KiCADServer {
|
|||||||
// Set absolute path to the Python KiCAD interface script
|
// Set absolute path to the Python KiCAD interface script
|
||||||
// Using a hardcoded path to avoid cwd() issues when running from Cline
|
// Using a hardcoded path to avoid cwd() issues when running from Cline
|
||||||
this.kicadScriptPath = 'c:/repo/KiCAD-MCP/python/kicad_interface.py';
|
this.kicadScriptPath = 'c:/repo/KiCAD-MCP/python/kicad_interface.py';
|
||||||
|
|
||||||
// Check if script exists
|
// Check if script exists
|
||||||
if (!existsSync(this.kicadScriptPath)) {
|
if (!existsSync(this.kicadScriptPath)) {
|
||||||
throw new Error(`KiCAD interface script not found: ${this.kicadScriptPath}`);
|
throw new Error(`KiCAD interface script not found: ${this.kicadScriptPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the server
|
// Initialize the server
|
||||||
this.server = new Server(
|
this.server = new Server(
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ class KiCADServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize handler with direct pass-through to Python KiCAD interface
|
// Initialize handler with direct pass-through to Python KiCAD interface
|
||||||
// We don't register TypeScript tools since we'll handle everything in Python
|
// We don't register TypeScript tools since we'll handle everything in Python
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ class KiCADServer {
|
|||||||
properties: {}
|
properties: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Board tools
|
// Board tools
|
||||||
{
|
{
|
||||||
name: 'set_board_size',
|
name: 'set_board_size',
|
||||||
@@ -129,7 +129,7 @@ class KiCADServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Component tools
|
// Component tools
|
||||||
{
|
{
|
||||||
name: 'place_component',
|
name: 'place_component',
|
||||||
@@ -147,7 +147,7 @@ class KiCADServer {
|
|||||||
required: ['componentId', 'position']
|
required: ['componentId', 'position']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Routing tools
|
// Routing tools
|
||||||
{
|
{
|
||||||
name: 'add_net',
|
name: 'add_net',
|
||||||
@@ -176,7 +176,7 @@ class KiCADServer {
|
|||||||
required: ['start', 'end']
|
required: ['start', 'end']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Schematic tools
|
// Schematic tools
|
||||||
{
|
{
|
||||||
name: 'create_schematic',
|
name: 'create_schematic',
|
||||||
@@ -209,8 +209,8 @@ class KiCADServer {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
schematicPath: { type: 'string', description: 'Path to the schematic file' },
|
schematicPath: { type: 'string', description: 'Path to the schematic file' },
|
||||||
component: {
|
component: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
description: 'Component definition',
|
description: 'Component definition',
|
||||||
properties: {
|
properties: {
|
||||||
type: { type: 'string', description: 'Component type (e.g., R, C, LED)' },
|
type: { type: 'string', description: 'Component type (e.g., R, C, LED)' },
|
||||||
@@ -235,15 +235,15 @@ class KiCADServer {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
schematicPath: { type: 'string', description: 'Path to the schematic file' },
|
schematicPath: { type: 'string', description: 'Path to the schematic file' },
|
||||||
startPoint: {
|
startPoint: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
description: 'Starting point coordinates [x, y]',
|
description: 'Starting point coordinates [x, y]',
|
||||||
items: { type: 'number' },
|
items: { type: 'number' },
|
||||||
minItems: 2,
|
minItems: 2,
|
||||||
maxItems: 2
|
maxItems: 2
|
||||||
},
|
},
|
||||||
endPoint: {
|
endPoint: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
description: 'Ending point coordinates [x, y]',
|
description: 'Ending point coordinates [x, y]',
|
||||||
items: { type: 'number' },
|
items: { type: 'number' },
|
||||||
minItems: 2,
|
minItems: 2,
|
||||||
@@ -259,8 +259,8 @@ class KiCADServer {
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
searchPaths: {
|
searchPaths: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
description: 'Optional search paths for libraries',
|
description: 'Optional search paths for libraries',
|
||||||
items: { type: 'string' }
|
items: { type: 'string' }
|
||||||
}
|
}
|
||||||
@@ -286,7 +286,7 @@ class KiCADServer {
|
|||||||
this.server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
|
this.server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
|
||||||
const toolName = request.params.name;
|
const toolName = request.params.name;
|
||||||
const args = request.params.arguments || {};
|
const args = request.params.arguments || {};
|
||||||
|
|
||||||
// Pass all commands directly to KiCAD Python interface
|
// Pass all commands directly to KiCAD Python interface
|
||||||
try {
|
try {
|
||||||
return await this.callKicadScript(toolName, args);
|
return await this.callKicadScript(toolName, args);
|
||||||
@@ -300,11 +300,11 @@ class KiCADServer {
|
|||||||
async start() {
|
async start() {
|
||||||
try {
|
try {
|
||||||
console.error('Starting KiCAD MCP server...');
|
console.error('Starting KiCAD MCP server...');
|
||||||
|
|
||||||
// Start the Python process for KiCAD scripting
|
// Start the Python process for KiCAD scripting
|
||||||
console.error(`Starting Python process with script: ${this.kicadScriptPath}`);
|
console.error(`Starting Python process with script: ${this.kicadScriptPath}`);
|
||||||
const pythonExe = 'C:\\Program Files\\KiCad\\9.0\\bin\\python.exe';
|
const pythonExe = 'C:\\Program Files\\KiCad\\9.0\\bin\\python.exe';
|
||||||
|
|
||||||
console.error(`Using Python executable: ${pythonExe}`);
|
console.error(`Using Python executable: ${pythonExe}`);
|
||||||
this.pythonProcess = spawn(pythonExe, [this.kicadScriptPath], {
|
this.pythonProcess = spawn(pythonExe, [this.kicadScriptPath], {
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
@@ -313,30 +313,30 @@ class KiCADServer {
|
|||||||
PYTHONPATH: 'C:/Program Files/KiCad/9.0/lib/python3/dist-packages'
|
PYTHONPATH: 'C:/Program Files/KiCad/9.0/lib/python3/dist-packages'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for process exit
|
// Listen for process exit
|
||||||
this.pythonProcess.on('exit', (code, signal) => {
|
this.pythonProcess.on('exit', (code, signal) => {
|
||||||
console.error(`Python process exited with code ${code} and signal ${signal}`);
|
console.error(`Python process exited with code ${code} and signal ${signal}`);
|
||||||
this.pythonProcess = null;
|
this.pythonProcess = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for process errors
|
// Listen for process errors
|
||||||
this.pythonProcess.on('error', (err) => {
|
this.pythonProcess.on('error', (err) => {
|
||||||
console.error(`Python process error: ${err.message}`);
|
console.error(`Python process error: ${err.message}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up error logging for stderr
|
// Set up error logging for stderr
|
||||||
if (this.pythonProcess.stderr) {
|
if (this.pythonProcess.stderr) {
|
||||||
this.pythonProcess.stderr.on('data', (data: Buffer) => {
|
this.pythonProcess.stderr.on('data', (data: Buffer) => {
|
||||||
console.error(`Python stderr: ${data.toString()}`);
|
console.error(`Python stderr: ${data.toString()}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to transport
|
// Connect to transport
|
||||||
const transport = new StdioServerTransport();
|
const transport = new StdioServerTransport();
|
||||||
await this.server.connect(transport);
|
await this.server.connect(transport);
|
||||||
console.error('KiCAD MCP server running');
|
console.error('KiCAD MCP server running');
|
||||||
|
|
||||||
// Keep the process running
|
// Keep the process running
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
if (this.pythonProcess) {
|
if (this.pythonProcess) {
|
||||||
@@ -345,7 +345,7 @@ class KiCADServer {
|
|||||||
this.server.close().catch(console.error);
|
this.server.close().catch(console.error);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
console.error('Failed to start MCP server:', error.message);
|
console.error('Failed to start MCP server:', error.message);
|
||||||
@@ -364,73 +364,73 @@ class KiCADServer {
|
|||||||
reject(new Error("Python process for KiCAD scripting is not running"));
|
reject(new Error("Python process for KiCAD scripting is not running"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add request to queue
|
// Add request to queue
|
||||||
this.requestQueue.push({
|
this.requestQueue.push({
|
||||||
request: { command, params },
|
request: { command, params },
|
||||||
resolve,
|
resolve,
|
||||||
reject
|
reject
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process the queue if not already processing
|
// Process the queue if not already processing
|
||||||
if (!this.processingRequest) {
|
if (!this.processingRequest) {
|
||||||
this.processNextRequest();
|
this.processNextRequest();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private processNextRequest(): void {
|
private processNextRequest(): void {
|
||||||
// If no more requests or already processing, return
|
// If no more requests or already processing, return
|
||||||
if (this.requestQueue.length === 0 || this.processingRequest) {
|
if (this.requestQueue.length === 0 || this.processingRequest) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set processing flag
|
// Set processing flag
|
||||||
this.processingRequest = true;
|
this.processingRequest = true;
|
||||||
|
|
||||||
// Get the next request
|
// Get the next request
|
||||||
const { request, resolve, reject } = this.requestQueue.shift()!;
|
const { request, resolve, reject } = this.requestQueue.shift()!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.error(`Processing KiCAD command: ${request.command}`);
|
console.error(`Processing KiCAD command: ${request.command}`);
|
||||||
|
|
||||||
// Format the command and parameters as JSON
|
// Format the command and parameters as JSON
|
||||||
const requestStr = JSON.stringify(request);
|
const requestStr = JSON.stringify(request);
|
||||||
|
|
||||||
// Set up response handling
|
// Set up response handling
|
||||||
let responseData = '';
|
let responseData = '';
|
||||||
|
|
||||||
// Clear any previous listeners
|
// Clear any previous listeners
|
||||||
if (this.pythonProcess?.stdout) {
|
if (this.pythonProcess?.stdout) {
|
||||||
this.pythonProcess.stdout.removeAllListeners('data');
|
this.pythonProcess.stdout.removeAllListeners('data');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up new listeners
|
// Set up new listeners
|
||||||
if (this.pythonProcess?.stdout) {
|
if (this.pythonProcess?.stdout) {
|
||||||
this.pythonProcess.stdout.on('data', (data: Buffer) => {
|
this.pythonProcess.stdout.on('data', (data: Buffer) => {
|
||||||
const chunk = data.toString();
|
const chunk = data.toString();
|
||||||
console.error(`Received data chunk: ${chunk.length} bytes`);
|
console.error(`Received data chunk: ${chunk.length} bytes`);
|
||||||
responseData += chunk;
|
responseData += chunk;
|
||||||
|
|
||||||
// Check if we have a complete response
|
// Check if we have a complete response
|
||||||
try {
|
try {
|
||||||
// Try to parse the response as JSON
|
// Try to parse the response as JSON
|
||||||
const result = JSON.parse(responseData);
|
const result = JSON.parse(responseData);
|
||||||
|
|
||||||
// If we get here, we have a valid JSON response
|
// If we get here, we have a valid JSON response
|
||||||
console.error(`Completed KiCAD command: ${request.command} with result: ${JSON.stringify(result)}`);
|
console.error(`Completed KiCAD command: ${request.command} with result: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
// Reset processing flag
|
// Reset processing flag
|
||||||
this.processingRequest = false;
|
this.processingRequest = false;
|
||||||
|
|
||||||
// Process next request if any
|
// Process next request if any
|
||||||
setTimeout(() => this.processNextRequest(), 0);
|
setTimeout(() => this.processNextRequest(), 0);
|
||||||
|
|
||||||
// Clear listeners
|
// Clear listeners
|
||||||
if (this.pythonProcess?.stdout) {
|
if (this.pythonProcess?.stdout) {
|
||||||
this.pythonProcess.stdout.removeAllListeners('data');
|
this.pythonProcess.stdout.removeAllListeners('data');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve with the expected MCP tool response format
|
// Resolve with the expected MCP tool response format
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
resolve({
|
resolve({
|
||||||
@@ -457,38 +457,38 @@ class KiCADServer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a timeout
|
// Set a timeout
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
console.error(`Command timeout: ${request.command}`);
|
console.error(`Command timeout: ${request.command}`);
|
||||||
|
|
||||||
// Clear listeners
|
// Clear listeners
|
||||||
if (this.pythonProcess?.stdout) {
|
if (this.pythonProcess?.stdout) {
|
||||||
this.pythonProcess.stdout.removeAllListeners('data');
|
this.pythonProcess.stdout.removeAllListeners('data');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset processing flag
|
// Reset processing flag
|
||||||
this.processingRequest = false;
|
this.processingRequest = false;
|
||||||
|
|
||||||
// Process next request
|
// Process next request
|
||||||
setTimeout(() => this.processNextRequest(), 0);
|
setTimeout(() => this.processNextRequest(), 0);
|
||||||
|
|
||||||
// Reject the promise
|
// Reject the promise
|
||||||
reject(new Error(`Command timeout: ${request.command}`));
|
reject(new Error(`Command timeout: ${request.command}`));
|
||||||
}, 30000); // 30 seconds timeout
|
}, 30000); // 30 seconds timeout
|
||||||
|
|
||||||
// Write the request to the Python process
|
// Write the request to the Python process
|
||||||
console.error(`Sending request: ${requestStr}`);
|
console.error(`Sending request: ${requestStr}`);
|
||||||
this.pythonProcess?.stdin?.write(requestStr + '\n');
|
this.pythonProcess?.stdin?.write(requestStr + '\n');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error processing request: ${error}`);
|
console.error(`Error processing request: ${error}`);
|
||||||
|
|
||||||
// Reset processing flag
|
// Reset processing flag
|
||||||
this.processingRequest = false;
|
this.processingRequest = false;
|
||||||
|
|
||||||
// Process next request
|
// Process next request
|
||||||
setTimeout(() => this.processNextRequest(), 0);
|
setTimeout(() => this.processNextRequest(), 0);
|
||||||
|
|
||||||
// Reject the promise
|
// Reject the promise
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const DEFAULT_LOG_DIR = join(os.homedir(), '.kicad-mcp', 'logs');
|
|||||||
class Logger {
|
class Logger {
|
||||||
private logLevel: LogLevel = 'info';
|
private logLevel: LogLevel = 'info';
|
||||||
private logDir: string = DEFAULT_LOG_DIR;
|
private logDir: string = DEFAULT_LOG_DIR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the log level
|
* Set the log level
|
||||||
* @param level Log level to set
|
* @param level Log level to set
|
||||||
@@ -26,20 +26,20 @@ class Logger {
|
|||||||
setLogLevel(level: LogLevel): void {
|
setLogLevel(level: LogLevel): void {
|
||||||
this.logLevel = level;
|
this.logLevel = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the log directory
|
* Set the log directory
|
||||||
* @param dir Directory to store log files
|
* @param dir Directory to store log files
|
||||||
*/
|
*/
|
||||||
setLogDir(dir: string): void {
|
setLogDir(dir: string): void {
|
||||||
this.logDir = dir;
|
this.logDir = dir;
|
||||||
|
|
||||||
// Ensure log directory exists
|
// Ensure log directory exists
|
||||||
if (!existsSync(this.logDir)) {
|
if (!existsSync(this.logDir)) {
|
||||||
mkdirSync(this.logDir, { recursive: true });
|
mkdirSync(this.logDir, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log an error message
|
* Log an error message
|
||||||
* @param message Message to log
|
* @param message Message to log
|
||||||
@@ -47,7 +47,7 @@ class Logger {
|
|||||||
error(message: string): void {
|
error(message: string): void {
|
||||||
this.log('error', message);
|
this.log('error', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a warning message
|
* Log a warning message
|
||||||
* @param message Message to log
|
* @param message Message to log
|
||||||
@@ -57,7 +57,7 @@ class Logger {
|
|||||||
this.log('warn', message);
|
this.log('warn', message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log an info message
|
* Log an info message
|
||||||
* @param message Message to log
|
* @param message Message to log
|
||||||
@@ -67,7 +67,7 @@ class Logger {
|
|||||||
this.log('info', message);
|
this.log('info', message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a debug message
|
* Log a debug message
|
||||||
* @param message Message to log
|
* @param message Message to log
|
||||||
@@ -77,7 +77,7 @@ class Logger {
|
|||||||
this.log('debug', message);
|
this.log('debug', message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message with the specified level
|
* Log a message with the specified level
|
||||||
* @param level Log level
|
* @param level Log level
|
||||||
@@ -93,14 +93,14 @@ class Logger {
|
|||||||
// Log to console.error (stderr) only - stdout is reserved for MCP protocol
|
// Log to console.error (stderr) only - stdout is reserved for MCP protocol
|
||||||
// All log levels go to stderr to avoid corrupting STDIO MCP transport
|
// All log levels go to stderr to avoid corrupting STDIO MCP transport
|
||||||
console.error(formattedMessage);
|
console.error(formattedMessage);
|
||||||
|
|
||||||
// Log to file
|
// Log to file
|
||||||
try {
|
try {
|
||||||
// Ensure log directory exists
|
// Ensure log directory exists
|
||||||
if (!existsSync(this.logDir)) {
|
if (!existsSync(this.logDir)) {
|
||||||
mkdirSync(this.logDir, { recursive: true });
|
mkdirSync(this.logDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const logFile = join(this.logDir, `kicad-mcp-${new Date().toISOString().split('T')[0]}.log`);
|
const logFile = join(this.logDir, `kicad-mcp-${new Date().toISOString().split('T')[0]}.log`);
|
||||||
appendFileSync(logFile, formattedMessage + '\n');
|
appendFileSync(logFile, formattedMessage + '\n');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Component prompts for KiCAD MCP server
|
* Component prompts for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These prompts guide the LLM in providing assistance with component-related tasks
|
* These prompts guide the LLM in providing assistance with component-related tasks
|
||||||
* in KiCAD PCB design.
|
* in KiCAD PCB design.
|
||||||
*/
|
*/
|
||||||
@@ -11,7 +11,7 @@ import { logger } from '../logger.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register component prompts with the MCP server
|
* Register component prompts with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
*/
|
*/
|
||||||
export function registerComponentPrompts(server: McpServer): void {
|
export function registerComponentPrompts(server: McpServer): void {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Design prompts for KiCAD MCP server
|
* Design prompts for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These prompts guide the LLM in providing assistance with general PCB design tasks
|
* These prompts guide the LLM in providing assistance with general PCB design tasks
|
||||||
* in KiCAD.
|
* in KiCAD.
|
||||||
*/
|
*/
|
||||||
@@ -11,7 +11,7 @@ import { logger } from '../logger.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register design prompts with the MCP server
|
* Register design prompts with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
*/
|
*/
|
||||||
export function registerDesignPrompts(server: McpServer): void {
|
export function registerDesignPrompts(server: McpServer): void {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Routing prompts for KiCAD MCP server
|
* Routing prompts for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These prompts guide the LLM in providing assistance with routing-related tasks
|
* These prompts guide the LLM in providing assistance with routing-related tasks
|
||||||
* in KiCAD PCB design.
|
* in KiCAD PCB design.
|
||||||
*/
|
*/
|
||||||
@@ -11,7 +11,7 @@ import { logger } from '../logger.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register routing prompts with the MCP server
|
* Register routing prompts with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
*/
|
*/
|
||||||
export function registerRoutingPrompts(server: McpServer): void {
|
export function registerRoutingPrompts(server: McpServer): void {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Board resources for KiCAD MCP server
|
* Board resources for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These resources provide information about the PCB board
|
* These resources provide information about the PCB board
|
||||||
* to the LLM, enabling better context-aware assistance.
|
* to the LLM, enabling better context-aware assistance.
|
||||||
*/
|
*/
|
||||||
@@ -15,7 +15,7 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register board resources with the MCP server
|
* Register board resources with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +31,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving board information');
|
logger.debug('Retrieving board information');
|
||||||
const result = await callKicadScript("get_board_info", {});
|
const result = await callKicadScript("get_board_info", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve board information: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve board information: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -45,7 +45,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved board information');
|
logger.debug('Successfully retrieved board information');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -66,7 +66,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving layer list');
|
logger.debug('Retrieving layer list');
|
||||||
const result = await callKicadScript("get_layer_list", {});
|
const result = await callKicadScript("get_layer_list", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve layer list: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve layer list: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -80,7 +80,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully retrieved ${result.layers?.length || 0} layers`);
|
logger.debug(`Successfully retrieved ${result.layers?.length || 0} layers`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -107,10 +107,10 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}),
|
}),
|
||||||
async (uri, params) => {
|
async (uri, params) => {
|
||||||
const unit = params.unit || 'mm';
|
const unit = params.unit || 'mm';
|
||||||
|
|
||||||
logger.debug(`Retrieving board extents in ${unit}`);
|
logger.debug(`Retrieving board extents in ${unit}`);
|
||||||
const result = await callKicadScript("get_board_extents", { unit });
|
const result = await callKicadScript("get_board_extents", { unit });
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve board extents: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve board extents: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -124,7 +124,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved board extents');
|
logger.debug('Successfully retrieved board extents');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -156,7 +156,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
const height = params.height ? parseInt(params.height as string) : undefined;
|
const height = params.height ? parseInt(params.height as string) : undefined;
|
||||||
// Handle layers parameter - could be string or array
|
// Handle layers parameter - could be string or array
|
||||||
const layers = typeof params.layers === 'string' ? params.layers.split(',') : params.layers;
|
const layers = typeof params.layers === 'string' ? params.layers.split(',') : params.layers;
|
||||||
|
|
||||||
logger.debug('Retrieving 2D board view');
|
logger.debug('Retrieving 2D board view');
|
||||||
const result = await callKicadScript("get_board_2d_view", {
|
const result = await callKicadScript("get_board_2d_view", {
|
||||||
layers,
|
layers,
|
||||||
@@ -164,7 +164,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
height,
|
height,
|
||||||
format
|
format
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve 2D board view: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve 2D board view: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -178,9 +178,9 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved 2D board view');
|
logger.debug('Successfully retrieved 2D board view');
|
||||||
|
|
||||||
if (format === 'svg') {
|
if (format === 'svg') {
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -219,14 +219,14 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
const angle = params.angle || 'isometric';
|
const angle = params.angle || 'isometric';
|
||||||
const width = params.width ? parseInt(params.width as string) : undefined;
|
const width = params.width ? parseInt(params.width as string) : undefined;
|
||||||
const height = params.height ? parseInt(params.height as string) : undefined;
|
const height = params.height ? parseInt(params.height as string) : undefined;
|
||||||
|
|
||||||
logger.debug(`Retrieving 3D board view from ${angle} angle`);
|
logger.debug(`Retrieving 3D board view from ${angle} angle`);
|
||||||
const result = await callKicadScript("get_board_3d_view", {
|
const result = await callKicadScript("get_board_3d_view", {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
angle
|
angle
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve 3D board view: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve 3D board view: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -240,7 +240,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved 3D board view');
|
logger.debug('Successfully retrieved 3D board view');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -260,7 +260,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
"kicad://board/statistics",
|
"kicad://board/statistics",
|
||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Generating board statistics');
|
logger.debug('Generating board statistics');
|
||||||
|
|
||||||
// Get board info
|
// Get board info
|
||||||
const boardResult = await callKicadScript("get_board_info", {});
|
const boardResult = await callKicadScript("get_board_info", {});
|
||||||
if (!boardResult.success) {
|
if (!boardResult.success) {
|
||||||
@@ -276,7 +276,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get component list
|
// Get component list
|
||||||
const componentsResult = await callKicadScript("get_component_list", {});
|
const componentsResult = await callKicadScript("get_component_list", {});
|
||||||
if (!componentsResult.success) {
|
if (!componentsResult.success) {
|
||||||
@@ -292,7 +292,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get nets list
|
// Get nets list
|
||||||
const netsResult = await callKicadScript("get_nets_list", {});
|
const netsResult = await callKicadScript("get_nets_list", {});
|
||||||
if (!netsResult.success) {
|
if (!netsResult.success) {
|
||||||
@@ -308,7 +308,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all information into statistics
|
// Combine all information into statistics
|
||||||
const statistics = {
|
const statistics = {
|
||||||
board: {
|
board: {
|
||||||
@@ -324,7 +324,7 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
count: netsResult.nets?.length || 0
|
count: netsResult.nets?.length || 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.debug('Successfully generated board statistics');
|
logger.debug('Successfully generated board statistics');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -344,11 +344,11 @@ export function registerBoardResources(server: McpServer, callKicadScript: Comma
|
|||||||
*/
|
*/
|
||||||
function countComponentTypes(components: any[]): Record<string, number> {
|
function countComponentTypes(components: any[]): Record<string, number> {
|
||||||
const typeCounts: Record<string, number> = {};
|
const typeCounts: Record<string, number> = {};
|
||||||
|
|
||||||
for (const component of components) {
|
for (const component of components) {
|
||||||
const type = component.value?.split(' ')[0] || 'Unknown';
|
const type = component.value?.split(' ')[0] || 'Unknown';
|
||||||
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeCounts;
|
return typeCounts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Component resources for KiCAD MCP server
|
* Component resources for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These resources provide information about components on the PCB
|
* These resources provide information about components on the PCB
|
||||||
* to the LLM, enabling better context-aware assistance.
|
* to the LLM, enabling better context-aware assistance.
|
||||||
*/
|
*/
|
||||||
@@ -13,7 +13,7 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register component resources with the MCP server
|
* Register component resources with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
@@ -29,7 +29,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving component list');
|
logger.debug('Retrieving component list');
|
||||||
const result = await callKicadScript("get_component_list", {});
|
const result = await callKicadScript("get_component_list", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve component list: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve component list: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -43,7 +43,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully retrieved ${result.components?.length || 0} components`);
|
logger.debug(`Successfully retrieved ${result.components?.length || 0} components`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -69,7 +69,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
const result = await callKicadScript("get_component_properties", {
|
const result = await callKicadScript("get_component_properties", {
|
||||||
reference
|
reference
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve component details: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve component details: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -83,7 +83,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully retrieved details for component: ${reference}`);
|
logger.debug(`Successfully retrieved details for component: ${reference}`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -109,7 +109,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
const result = await callKicadScript("get_component_connections", {
|
const result = await callKicadScript("get_component_connections", {
|
||||||
reference
|
reference
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve component connections: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve component connections: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -123,7 +123,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully retrieved connections for component: ${reference}`);
|
logger.debug(`Successfully retrieved connections for component: ${reference}`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -144,7 +144,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving component placement information');
|
logger.debug('Retrieving component placement information');
|
||||||
const result = await callKicadScript("get_component_placement", {});
|
const result = await callKicadScript("get_component_placement", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve component placement: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve component placement: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -158,7 +158,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved component placement information');
|
logger.debug('Successfully retrieved component placement information');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -179,7 +179,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving component groups');
|
logger.debug('Retrieving component groups');
|
||||||
const result = await callKicadScript("get_component_groups", {});
|
const result = await callKicadScript("get_component_groups", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve component groups: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve component groups: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -193,7 +193,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully retrieved ${result.groups?.length || 0} component groups`);
|
logger.debug(`Successfully retrieved ${result.groups?.length || 0} component groups`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -219,7 +219,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
const result = await callKicadScript("get_component_visualization", {
|
const result = await callKicadScript("get_component_visualization", {
|
||||||
reference
|
reference
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to generate component visualization: ${result.errorDetails}`);
|
logger.error(`Failed to generate component visualization: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -233,7 +233,7 @@ export function registerComponentResources(server: McpServer, callKicadScript: C
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully generated visualization for component: ${reference}`);
|
logger.debug(`Successfully generated visualization for component: ${reference}`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Resources index for KiCAD MCP server
|
* Resources index for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* Exports all resource registration functions
|
* Exports all resource registration functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Library resources for KiCAD MCP server
|
* Library resources for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These resources provide information about KiCAD component libraries
|
* These resources provide information about KiCAD component libraries
|
||||||
* to the LLM, enabling better context-aware assistance.
|
* to the LLM, enabling better context-aware assistance.
|
||||||
*/
|
*/
|
||||||
@@ -14,7 +14,7 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register library resources with the MCP server
|
* Register library resources with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
@@ -39,7 +39,7 @@ export function registerLibraryResources(server: McpServer, callKicadScript: Com
|
|||||||
const limit = Number(params.limit) || undefined;
|
const limit = Number(params.limit) || undefined;
|
||||||
|
|
||||||
logger.debug(`Retrieving component library${filter ? ` with filter: ${filter}` : ''}${library ? ` from library: ${library}` : ''}`);
|
logger.debug(`Retrieving component library${filter ? ` with filter: ${filter}` : ''}${library ? ` from library: ${library}` : ''}`);
|
||||||
|
|
||||||
const result = await callKicadScript("get_component_library", {
|
const result = await callKicadScript("get_component_library", {
|
||||||
filter,
|
filter,
|
||||||
library,
|
library,
|
||||||
@@ -117,7 +117,7 @@ export function registerLibraryResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri, params) => {
|
async (uri, params) => {
|
||||||
const { componentId, library } = params;
|
const { componentId, library } = params;
|
||||||
logger.debug(`Retrieving details for component: ${componentId}${library ? ` from library: ${library}` : ''}`);
|
logger.debug(`Retrieving details for component: ${componentId}${library ? ` from library: ${library}` : ''}`);
|
||||||
|
|
||||||
const result = await callKicadScript("get_component_details", {
|
const result = await callKicadScript("get_component_details", {
|
||||||
componentId,
|
componentId,
|
||||||
library
|
library
|
||||||
@@ -159,7 +159,7 @@ export function registerLibraryResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri, params) => {
|
async (uri, params) => {
|
||||||
const { componentId, footprint } = params;
|
const { componentId, footprint } = params;
|
||||||
logger.debug(`Retrieving footprint for component: ${componentId}${footprint ? ` (${footprint})` : ''}`);
|
logger.debug(`Retrieving footprint for component: ${componentId}${footprint ? ` (${footprint})` : ''}`);
|
||||||
|
|
||||||
const result = await callKicadScript("get_component_footprint", {
|
const result = await callKicadScript("get_component_footprint", {
|
||||||
componentId,
|
componentId,
|
||||||
footprint
|
footprint
|
||||||
@@ -201,7 +201,7 @@ export function registerLibraryResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri, params) => {
|
async (uri, params) => {
|
||||||
const { componentId } = params;
|
const { componentId } = params;
|
||||||
logger.debug(`Retrieving symbol for component: ${componentId}`);
|
logger.debug(`Retrieving symbol for component: ${componentId}`);
|
||||||
|
|
||||||
const result = await callKicadScript("get_component_symbol", {
|
const result = await callKicadScript("get_component_symbol", {
|
||||||
componentId
|
componentId
|
||||||
});
|
});
|
||||||
@@ -255,7 +255,7 @@ export function registerLibraryResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri, params) => {
|
async (uri, params) => {
|
||||||
const { componentId, footprint } = params;
|
const { componentId, footprint } = params;
|
||||||
logger.debug(`Retrieving 3D model for component: ${componentId}${footprint ? ` (${footprint})` : ''}`);
|
logger.debug(`Retrieving 3D model for component: ${componentId}${footprint ? ` (${footprint})` : ''}`);
|
||||||
|
|
||||||
const result = await callKicadScript("get_component_3d_model", {
|
const result = await callKicadScript("get_component_3d_model", {
|
||||||
componentId,
|
componentId,
|
||||||
footprint
|
footprint
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Project resources for KiCAD MCP server
|
* Project resources for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These resources provide information about the KiCAD project
|
* These resources provide information about the KiCAD project
|
||||||
* to the LLM, enabling better context-aware assistance.
|
* to the LLM, enabling better context-aware assistance.
|
||||||
*/
|
*/
|
||||||
@@ -13,7 +13,7 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register project resources with the MCP server
|
* Register project resources with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
@@ -29,7 +29,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving project information');
|
logger.debug('Retrieving project information');
|
||||||
const result = await callKicadScript("get_project_info", {});
|
const result = await callKicadScript("get_project_info", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve project information: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve project information: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -43,7 +43,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved project information');
|
logger.debug('Successfully retrieved project information');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -64,7 +64,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving project properties');
|
logger.debug('Retrieving project properties');
|
||||||
const result = await callKicadScript("get_project_properties", {});
|
const result = await callKicadScript("get_project_properties", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve project properties: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve project properties: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -78,7 +78,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved project properties');
|
logger.debug('Successfully retrieved project properties');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -99,7 +99,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving project files');
|
logger.debug('Retrieving project files');
|
||||||
const result = await callKicadScript("get_project_files", {});
|
const result = await callKicadScript("get_project_files", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve project files: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve project files: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -113,7 +113,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Successfully retrieved ${result.files?.length || 0} project files`);
|
logger.debug(`Successfully retrieved ${result.files?.length || 0} project files`);
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -134,7 +134,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Retrieving project status');
|
logger.debug('Retrieving project status');
|
||||||
const result = await callKicadScript("get_project_status", {});
|
const result = await callKicadScript("get_project_status", {});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error(`Failed to retrieve project status: ${result.errorDetails}`);
|
logger.error(`Failed to retrieve project status: ${result.errorDetails}`);
|
||||||
return {
|
return {
|
||||||
@@ -148,7 +148,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Successfully retrieved project status');
|
logger.debug('Successfully retrieved project status');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -168,7 +168,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
"kicad://project/summary",
|
"kicad://project/summary",
|
||||||
async (uri) => {
|
async (uri) => {
|
||||||
logger.debug('Generating project summary');
|
logger.debug('Generating project summary');
|
||||||
|
|
||||||
// Get project info
|
// Get project info
|
||||||
const infoResult = await callKicadScript("get_project_info", {});
|
const infoResult = await callKicadScript("get_project_info", {});
|
||||||
if (!infoResult.success) {
|
if (!infoResult.success) {
|
||||||
@@ -184,7 +184,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get board info
|
// Get board info
|
||||||
const boardResult = await callKicadScript("get_board_info", {});
|
const boardResult = await callKicadScript("get_board_info", {});
|
||||||
if (!boardResult.success) {
|
if (!boardResult.success) {
|
||||||
@@ -200,7 +200,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get component list
|
// Get component list
|
||||||
const componentsResult = await callKicadScript("get_component_list", {});
|
const componentsResult = await callKicadScript("get_component_list", {});
|
||||||
if (!componentsResult.success) {
|
if (!componentsResult.success) {
|
||||||
@@ -216,7 +216,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all information into a summary
|
// Combine all information into a summary
|
||||||
const summary = {
|
const summary = {
|
||||||
project: infoResult.project,
|
project: infoResult.project,
|
||||||
@@ -230,7 +230,7 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
types: countComponentTypes(componentsResult.components || [])
|
types: countComponentTypes(componentsResult.components || [])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.debug('Successfully generated project summary');
|
logger.debug('Successfully generated project summary');
|
||||||
return {
|
return {
|
||||||
contents: [{
|
contents: [{
|
||||||
@@ -250,11 +250,11 @@ export function registerProjectResources(server: McpServer, callKicadScript: Com
|
|||||||
*/
|
*/
|
||||||
function countComponentTypes(components: any[]): Record<string, number> {
|
function countComponentTypes(components: any[]): Record<string, number> {
|
||||||
const typeCounts: Record<string, number> = {};
|
const typeCounts: Record<string, number> = {};
|
||||||
|
|
||||||
for (const component of components) {
|
for (const component of components) {
|
||||||
const type = component.value?.split(' ')[0] || 'Unknown';
|
const type = component.value?.split(' ')[0] || 'Unknown';
|
||||||
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeCounts;
|
return typeCounts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Board management tools for KiCAD MCP server
|
* Board management tools for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These tools handle board setup, layer management, and board properties
|
* These tools handle board setup, layer management, and board properties
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,13 +13,13 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register board management tools with the MCP server
|
* Register board management tools with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
export function registerBoardTools(server: McpServer, callKicadScript: CommandFunction): void {
|
export function registerBoardTools(server: McpServer, callKicadScript: CommandFunction): void {
|
||||||
logger.info('Registering board management tools');
|
logger.info('Registering board management tools');
|
||||||
|
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Set Board Size Tool
|
// Set Board Size Tool
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@@ -37,7 +37,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
height,
|
height,
|
||||||
unit
|
unit
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -70,7 +70,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
position,
|
position,
|
||||||
number
|
number
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -91,7 +91,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
async ({ layer }) => {
|
async ({ layer }) => {
|
||||||
logger.debug(`Setting active layer to: ${layer}`);
|
logger.debug(`Setting active layer to: ${layer}`);
|
||||||
const result = await callKicadScript("set_active_layer", { layer });
|
const result = await callKicadScript("set_active_layer", { layer });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -110,7 +110,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
async () => {
|
async () => {
|
||||||
logger.debug('Getting board information');
|
logger.debug('Getting board information');
|
||||||
const result = await callKicadScript("get_board_info", {});
|
const result = await callKicadScript("get_board_info", {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -129,7 +129,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
async () => {
|
async () => {
|
||||||
logger.debug('Getting layer list');
|
logger.debug('Getting layer list');
|
||||||
const result = await callKicadScript("get_layer_list", {});
|
const result = await callKicadScript("get_layer_list", {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -205,7 +205,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
diameter,
|
diameter,
|
||||||
padDiameter
|
padDiameter
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -244,7 +244,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
rotation,
|
rotation,
|
||||||
style
|
style
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -284,7 +284,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
minWidth,
|
minWidth,
|
||||||
padConnection
|
padConnection
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -305,7 +305,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
async ({ unit }) => {
|
async ({ unit }) => {
|
||||||
logger.debug('Getting board extents');
|
logger.debug('Getting board extents');
|
||||||
const result = await callKicadScript("get_board_extents", { unit });
|
const result = await callKicadScript("get_board_extents", { unit });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -334,7 +334,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
height,
|
height,
|
||||||
format
|
format
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -382,4 +382,3 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ type CommandFunction = (command: string, params: any) => Promise<any>;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register component management tools with the MCP server
|
* Register component management tools with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
export function registerComponentTools(server: McpServer, callKicadScript: CommandFunction): void {
|
export function registerComponentTools(server: McpServer, callKicadScript: CommandFunction): void {
|
||||||
logger.info('Registering component management tools');
|
logger.info('Registering component management tools');
|
||||||
|
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Place Component Tool
|
// Place Component Tool
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
server.registerTool({
|
server.registerTool({
|
||||||
name: "place_component",
|
name: "place_component",
|
||||||
description: "Places a component on the PCB at the specified location",
|
description: "Places a component on the PCB at the specified location",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Design rules tools for KiCAD MCP server
|
* Design rules tools for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These tools handle design rule checking and configuration
|
* These tools handle design rule checking and configuration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,13 +13,13 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register design rule tools with the MCP server
|
* Register design rule tools with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
export function registerDesignRuleTools(server: McpServer, callKicadScript: CommandFunction): void {
|
export function registerDesignRuleTools(server: McpServer, callKicadScript: CommandFunction): void {
|
||||||
logger.info('Registering design rule tools');
|
logger.info('Registering design rule tools');
|
||||||
|
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Set Design Rules Tool
|
// Set Design Rules Tool
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@@ -44,7 +44,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
async (params) => {
|
async (params) => {
|
||||||
logger.debug('Setting design rules');
|
logger.debug('Setting design rules');
|
||||||
const result = await callKicadScript("set_design_rules", params);
|
const result = await callKicadScript("set_design_rules", params);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -63,7 +63,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
async () => {
|
async () => {
|
||||||
logger.debug('Getting design rules');
|
logger.debug('Getting design rules');
|
||||||
const result = await callKicadScript("get_design_rules", {});
|
const result = await callKicadScript("get_design_rules", {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -84,7 +84,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
async ({ reportPath }) => {
|
async ({ reportPath }) => {
|
||||||
logger.debug('Running DRC check');
|
logger.debug('Running DRC check');
|
||||||
const result = await callKicadScript("run_drc", { reportPath });
|
const result = await callKicadScript("run_drc", { reportPath });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -127,7 +127,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
diff_pair_gap,
|
diff_pair_gap,
|
||||||
nets
|
nets
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -152,7 +152,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
net,
|
net,
|
||||||
netClass
|
netClass
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -183,7 +183,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
minViaDiameter,
|
minViaDiameter,
|
||||||
minViaDrill
|
minViaDrill
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -226,7 +226,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
item1,
|
item1,
|
||||||
item2
|
item2
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -247,7 +247,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
|||||||
async ({ severity }) => {
|
async ({ severity }) => {
|
||||||
logger.debug('Getting DRC violations');
|
logger.debug('Getting DRC violations');
|
||||||
const result = await callKicadScript("get_drc_violations", { severity });
|
const result = await callKicadScript("get_drc_violations", { severity });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Export tools for KiCAD MCP server
|
* Export tools for KiCAD MCP server
|
||||||
*
|
*
|
||||||
* These tools handle exporting PCB data to various formats
|
* These tools handle exporting PCB data to various formats
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,13 +13,13 @@ type CommandFunction = (command: string, params: Record<string, unknown>) => Pro
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register export tools with the MCP server
|
* Register export tools with the MCP server
|
||||||
*
|
*
|
||||||
* @param server MCP server instance
|
* @param server MCP server instance
|
||||||
* @param callKicadScript Function to call KiCAD script commands
|
* @param callKicadScript Function to call KiCAD script commands
|
||||||
*/
|
*/
|
||||||
export function registerExportTools(server: McpServer, callKicadScript: CommandFunction): void {
|
export function registerExportTools(server: McpServer, callKicadScript: CommandFunction): void {
|
||||||
logger.info('Registering export tools');
|
logger.info('Registering export tools');
|
||||||
|
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Export Gerber Tool
|
// Export Gerber Tool
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@@ -43,7 +43,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
generateMapFile,
|
generateMapFile,
|
||||||
useAuxOrigin
|
useAuxOrigin
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -74,7 +74,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
frameReference,
|
frameReference,
|
||||||
pageSize
|
pageSize
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -103,7 +103,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
blackAndWhite,
|
blackAndWhite,
|
||||||
includeComponents
|
includeComponents
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -136,7 +136,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
includeSolderMask,
|
includeSolderMask,
|
||||||
includeSilkscreen
|
includeSilkscreen
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -165,7 +165,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
groupByValue,
|
groupByValue,
|
||||||
includeAttributes
|
includeAttributes
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -190,7 +190,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
outputPath,
|
outputPath,
|
||||||
format
|
format
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -219,7 +219,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
units,
|
units,
|
||||||
side
|
side
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -246,7 +246,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
|||||||
includeComponents,
|
includeComponents,
|
||||||
useRelativePaths
|
useRelativePaths
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
|
|||||||
Reference in New Issue
Block a user