feat: Week 1 complete - Linux support + IPC API prep

🎉 Major v2.0 rebuild kickoff - Week 1 accomplished!

## Highlights

### Cross-Platform Support 🌍
-  Linux primary platform (Ubuntu/Debian tested)
-  Windows fully supported
-  macOS experimental support
-  Platform-agnostic path handling (XDG spec)
-  Auto-detection of KiCAD installation

### Infrastructure 🏗️
-  GitHub Actions CI/CD pipeline
-  Pytest framework with 20+ tests
-  Pre-commit hooks (Black, MyPy, ESLint)
-  Automated Linux installation script
-  Enhanced npm scripts

### IPC API Migration Prep 🚀
-  Comprehensive migration plan (30 pages)
-  Backend abstraction layer (800+ lines)
-  Factory pattern with auto-detection
-  SWIG backward compatibility wrapper
-  IPC backend skeleton ready

### Documentation 📚
-  Updated README (Linux installation)
-  CONTRIBUTING.md guide
-  Linux compatibility audit
-  IPC API migration plan
-  Session summaries
-  Platform-specific config templates

## Files Changed

- 27 files created
- ~3,000 lines of code/docs
- 8 comprehensive documentation pages
- 20+ unit tests
- 5 abstraction layer modules

## Next Steps

- Week 2: IPC API migration (project.py → component.py → routing.py)
- Migrate from deprecated SWIG to official IPC API
- JLCPCB/Digikey integration prep

🤖 Generated with Claude Code
https://claude.com/claude-code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KiCAD MCP Bot
2025-10-25 20:48:00 -04:00
commit e4c7119c51
81 changed files with 16003 additions and 0 deletions

345
src/tools/board.ts Normal file
View File

@@ -0,0 +1,345 @@
/**
* Board management tools for KiCAD MCP server
*
* These tools handle board setup, layer management, and board properties
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from '../logger.js';
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
/**
* Register board management tools with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerBoardTools(server: McpServer, callKicadScript: CommandFunction): void {
logger.info('Registering board management tools');
// ------------------------------------------------------
// Set Board Size Tool
// ------------------------------------------------------
server.tool(
"set_board_size",
{
width: z.number().describe("Board width"),
height: z.number().describe("Board height"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement")
},
async ({ width, height, unit }) => {
logger.debug(`Setting board size to ${width}x${height} ${unit}`);
const result = await callKicadScript("set_board_size", {
width,
height,
unit
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Layer Tool
// ------------------------------------------------------
server.tool(
"add_layer",
{
name: z.string().describe("Layer name"),
type: z.enum([
"copper", "technical", "user", "signal"
]).describe("Layer type"),
position: z.enum([
"top", "bottom", "inner"
]).describe("Layer position"),
number: z.number().optional().describe("Layer number (for inner layers)")
},
async ({ name, type, position, number }) => {
logger.debug(`Adding ${type} layer: ${name}`);
const result = await callKicadScript("add_layer", {
name,
type,
position,
number
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Set Active Layer Tool
// ------------------------------------------------------
server.tool(
"set_active_layer",
{
layer: z.string().describe("Layer name to set as active")
},
async ({ layer }) => {
logger.debug(`Setting active layer to: ${layer}`);
const result = await callKicadScript("set_active_layer", { layer });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get Board Info Tool
// ------------------------------------------------------
server.tool(
"get_board_info",
{},
async () => {
logger.debug('Getting board information');
const result = await callKicadScript("get_board_info", {});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get Layer List Tool
// ------------------------------------------------------
server.tool(
"get_layer_list",
{},
async () => {
logger.debug('Getting layer list');
const result = await callKicadScript("get_layer_list", {});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Board Outline Tool
// ------------------------------------------------------
server.tool(
"add_board_outline",
{
shape: z.enum(["rectangle", "circle", "polygon"]).describe("Shape of the outline"),
params: z.object({
// For rectangle
width: z.number().optional().describe("Width of rectangle"),
height: z.number().optional().describe("Height of rectangle"),
// For circle
radius: z.number().optional().describe("Radius of circle"),
// For polygon
points: z.array(
z.object({
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate")
})
).optional().describe("Points of polygon"),
// Common parameters
x: z.number().describe("X coordinate of center/origin"),
y: z.number().describe("Y coordinate of center/origin"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement")
}).describe("Parameters for the outline shape")
},
async ({ shape, params }) => {
logger.debug(`Adding ${shape} board outline`);
const result = await callKicadScript("add_board_outline", {
shape,
params
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Mounting Hole Tool
// ------------------------------------------------------
server.tool(
"add_mounting_hole",
{
position: z.object({
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement")
}).describe("Position of the mounting hole"),
diameter: z.number().describe("Diameter of the hole"),
padDiameter: z.number().optional().describe("Optional diameter of the pad around the hole")
},
async ({ position, diameter, padDiameter }) => {
logger.debug(`Adding mounting hole at (${position.x},${position.y}) ${position.unit}`);
const result = await callKicadScript("add_mounting_hole", {
position,
diameter,
padDiameter
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Text Tool
// ------------------------------------------------------
server.tool(
"add_board_text",
{
text: z.string().describe("Text content"),
position: z.object({
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement")
}).describe("Position of the text"),
layer: z.string().describe("Layer to place the text on"),
size: z.number().describe("Text size"),
thickness: z.number().optional().describe("Line thickness"),
rotation: z.number().optional().describe("Rotation angle in degrees"),
style: z.enum(["normal", "italic", "bold"]).optional().describe("Text style")
},
async ({ text, position, layer, size, thickness, rotation, style }) => {
logger.debug(`Adding text "${text}" at (${position.x},${position.y}) ${position.unit}`);
const result = await callKicadScript("add_board_text", {
text,
position,
layer,
size,
thickness,
rotation,
style
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Zone Tool
// ------------------------------------------------------
server.tool(
"add_zone",
{
layer: z.string().describe("Layer for the zone"),
net: z.string().describe("Net name for the zone"),
points: z.array(
z.object({
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate")
})
).describe("Points defining the zone outline"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement"),
clearance: z.number().optional().describe("Clearance value"),
minWidth: z.number().optional().describe("Minimum width"),
padConnection: z.enum(["thermal", "solid", "none"]).optional().describe("Pad connection type")
},
async ({ layer, net, points, unit, clearance, minWidth, padConnection }) => {
logger.debug(`Adding zone on layer ${layer} for net ${net}`);
const result = await callKicadScript("add_zone", {
layer,
net,
points,
unit,
clearance,
minWidth,
padConnection
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get Board Extents Tool
// ------------------------------------------------------
server.tool(
"get_board_extents",
{
unit: z.enum(["mm", "inch"]).optional().describe("Unit of measurement for the result")
},
async ({ unit }) => {
logger.debug('Getting board extents');
const result = await callKicadScript("get_board_extents", { unit });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get Board 2D View Tool
// ------------------------------------------------------
server.tool(
"get_board_2d_view",
{
layers: z.array(z.string()).optional().describe("Optional array of layer names to include"),
width: z.number().optional().describe("Optional width of the image in pixels"),
height: z.number().optional().describe("Optional height of the image in pixels"),
format: z.enum(["png", "jpg", "svg"]).optional().describe("Image format")
},
async ({ layers, width, height, format }) => {
logger.debug('Getting 2D board view');
const result = await callKicadScript("get_board_2d_view", {
layers,
width,
height,
format
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
logger.info('Board management tools registered');
}

291
src/tools/component.ts Normal file
View File

@@ -0,0 +1,291 @@
/**
* Component management tools for KiCAD MCP server
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from '../logger.js';
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
/**
* Register component management tools with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerComponentTools(server: McpServer, callKicadScript: CommandFunction): void {
logger.info('Registering component management tools');
// ------------------------------------------------------
// Place Component Tool
// ------------------------------------------------------
server.tool(
"place_component",
{
componentId: z.string().describe("Identifier for the component to place (e.g., 'R_0603_10k')"),
position: z.object({
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement")
}).describe("Position coordinates and unit"),
reference: z.string().optional().describe("Optional desired reference (e.g., 'R5')"),
value: z.string().optional().describe("Optional component value (e.g., '10k')"),
footprint: z.string().optional().describe("Optional specific footprint name"),
rotation: z.number().optional().describe("Optional rotation in degrees"),
layer: z.string().optional().describe("Optional layer (e.g., 'F.Cu', 'B.SilkS')")
},
async ({ componentId, position, reference, value, footprint, rotation, layer }) => {
logger.debug(`Placing component: ${componentId} at ${position.x},${position.y} ${position.unit}`);
const result = await callKicadScript("place_component", {
componentId,
position,
reference,
value,
footprint,
rotation,
layer
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Move Component Tool
// ------------------------------------------------------
server.tool(
"move_component",
{
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
position: z.object({
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
unit: z.enum(["mm", "inch"]).describe("Unit of measurement")
}).describe("New position coordinates and unit"),
rotation: z.number().optional().describe("Optional new rotation in degrees")
},
async ({ reference, position, rotation }) => {
logger.debug(`Moving component: ${reference} to ${position.x},${position.y} ${position.unit}`);
const result = await callKicadScript("move_component", {
reference,
position,
rotation
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Rotate Component Tool
// ------------------------------------------------------
server.tool(
"rotate_component",
{
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
angle: z.number().describe("Rotation angle in degrees (absolute, not relative)")
},
async ({ reference, angle }) => {
logger.debug(`Rotating component: ${reference} to ${angle} degrees`);
const result = await callKicadScript("rotate_component", {
reference,
angle
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Delete Component Tool
// ------------------------------------------------------
server.tool(
"delete_component",
{
reference: z.string().describe("Reference designator of the component to delete (e.g., 'R5')")
},
async ({ reference }) => {
logger.debug(`Deleting component: ${reference}`);
const result = await callKicadScript("delete_component", { reference });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Edit Component Properties Tool
// ------------------------------------------------------
server.tool(
"edit_component",
{
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
newReference: z.string().optional().describe("Optional new reference designator"),
value: z.string().optional().describe("Optional new component value"),
footprint: z.string().optional().describe("Optional new footprint")
},
async ({ reference, newReference, value, footprint }) => {
logger.debug(`Editing component: ${reference}`);
const result = await callKicadScript("edit_component", {
reference,
newReference,
value,
footprint
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Find Component Tool
// ------------------------------------------------------
server.tool(
"find_component",
{
reference: z.string().optional().describe("Reference designator to search for"),
value: z.string().optional().describe("Component value to search for")
},
async ({ reference, value }) => {
logger.debug(`Finding component with ${reference ? `reference: ${reference}` : `value: ${value}`}`);
const result = await callKicadScript("find_component", { reference, value });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get Component Properties Tool
// ------------------------------------------------------
server.tool(
"get_component_properties",
{
reference: z.string().describe("Reference designator of the component (e.g., 'R5')")
},
async ({ reference }) => {
logger.debug(`Getting properties for component: ${reference}`);
const result = await callKicadScript("get_component_properties", { reference });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Component Annotation Tool
// ------------------------------------------------------
server.tool(
"add_component_annotation",
{
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
annotation: z.string().describe("Annotation or comment text to add"),
visible: z.boolean().optional().describe("Whether the annotation should be visible on the PCB")
},
async ({ reference, annotation, visible }) => {
logger.debug(`Adding annotation to component: ${reference}`);
const result = await callKicadScript("add_component_annotation", {
reference,
annotation,
visible
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Group Components Tool
// ------------------------------------------------------
server.tool(
"group_components",
{
references: z.array(z.string()).describe("Reference designators of components to group"),
groupName: z.string().describe("Name for the component group")
},
async ({ references, groupName }) => {
logger.debug(`Grouping components: ${references.join(', ')} as ${groupName}`);
const result = await callKicadScript("group_components", {
references,
groupName
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Replace Component Tool
// ------------------------------------------------------
server.tool(
"replace_component",
{
reference: z.string().describe("Reference designator of the component to replace"),
newComponentId: z.string().describe("ID of the new component to use"),
newFootprint: z.string().optional().describe("Optional new footprint"),
newValue: z.string().optional().describe("Optional new component value")
},
async ({ reference, newComponentId, newFootprint, newValue }) => {
logger.debug(`Replacing component: ${reference} with ${newComponentId}`);
const result = await callKicadScript("replace_component", {
reference,
newComponentId,
newFootprint,
newValue
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
logger.info('Component management tools registered');
}

26
src/tools/component.txt Normal file
View File

@@ -0,0 +1,26 @@
/**
* Component management tools for KiCAD MCP server
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from '../logger.js';
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: any) => Promise<any>;
/**
* Register component management tools with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerComponentTools(server: McpServer, callKicadScript: CommandFunction): void {
logger.info('Registering component management tools');
// ------------------------------------------------------
// Place Component Tool
// ------------------------------------------------------
server.registerTool({
name: "place_component",
description: "Places a component on the PCB at the specified location",

261
src/tools/design-rules.ts Normal file
View File

@@ -0,0 +1,261 @@
/**
* Design rules tools for KiCAD MCP server
*
* These tools handle design rule checking and configuration
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from '../logger.js';
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
/**
* Register design rule tools with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerDesignRuleTools(server: McpServer, callKicadScript: CommandFunction): void {
logger.info('Registering design rule tools');
// ------------------------------------------------------
// Set Design Rules Tool
// ------------------------------------------------------
server.tool(
"set_design_rules",
{
clearance: z.number().optional().describe("Minimum clearance between copper items (mm)"),
trackWidth: z.number().optional().describe("Default track width (mm)"),
viaDiameter: z.number().optional().describe("Default via diameter (mm)"),
viaDrill: z.number().optional().describe("Default via drill size (mm)"),
microViaDiameter: z.number().optional().describe("Default micro via diameter (mm)"),
microViaDrill: z.number().optional().describe("Default micro via drill size (mm)"),
minTrackWidth: z.number().optional().describe("Minimum track width (mm)"),
minViaDiameter: z.number().optional().describe("Minimum via diameter (mm)"),
minViaDrill: z.number().optional().describe("Minimum via drill size (mm)"),
minMicroViaDiameter: z.number().optional().describe("Minimum micro via diameter (mm)"),
minMicroViaDrill: z.number().optional().describe("Minimum micro via drill size (mm)"),
minHoleDiameter: z.number().optional().describe("Minimum hole diameter (mm)"),
requireCourtyard: z.boolean().optional().describe("Whether to require courtyards for all footprints"),
courtyardClearance: z.number().optional().describe("Minimum clearance between courtyards (mm)")
},
async (params) => {
logger.debug('Setting design rules');
const result = await callKicadScript("set_design_rules", params);
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get Design Rules Tool
// ------------------------------------------------------
server.tool(
"get_design_rules",
{},
async () => {
logger.debug('Getting design rules');
const result = await callKicadScript("get_design_rules", {});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Run DRC Tool
// ------------------------------------------------------
server.tool(
"run_drc",
{
reportPath: z.string().optional().describe("Optional path to save the DRC report")
},
async ({ reportPath }) => {
logger.debug('Running DRC check');
const result = await callKicadScript("run_drc", { reportPath });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Add Net Class Tool
// ------------------------------------------------------
server.tool(
"add_net_class",
{
name: z.string().describe("Name of the net class"),
description: z.string().optional().describe("Optional description of the net class"),
clearance: z.number().describe("Clearance for this net class (mm)"),
trackWidth: z.number().describe("Track width for this net class (mm)"),
viaDiameter: z.number().describe("Via diameter for this net class (mm)"),
viaDrill: z.number().describe("Via drill size for this net class (mm)"),
uvia_diameter: z.number().optional().describe("Micro via diameter for this net class (mm)"),
uvia_drill: z.number().optional().describe("Micro via drill size for this net class (mm)"),
diff_pair_width: z.number().optional().describe("Differential pair width for this net class (mm)"),
diff_pair_gap: z.number().optional().describe("Differential pair gap for this net class (mm)"),
nets: z.array(z.string()).optional().describe("Array of net names to assign to this class")
},
async ({ name, description, clearance, trackWidth, viaDiameter, viaDrill, uvia_diameter, uvia_drill, diff_pair_width, diff_pair_gap, nets }) => {
logger.debug(`Adding net class: ${name}`);
const result = await callKicadScript("add_net_class", {
name,
description,
clearance,
trackWidth,
viaDiameter,
viaDrill,
uvia_diameter,
uvia_drill,
diff_pair_width,
diff_pair_gap,
nets
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Assign Net to Class Tool
// ------------------------------------------------------
server.tool(
"assign_net_to_class",
{
net: z.string().describe("Name of the net"),
netClass: z.string().describe("Name of the net class")
},
async ({ net, netClass }) => {
logger.debug(`Assigning net ${net} to class ${netClass}`);
const result = await callKicadScript("assign_net_to_class", {
net,
netClass
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Set Layer Constraints Tool
// ------------------------------------------------------
server.tool(
"set_layer_constraints",
{
layer: z.string().describe("Layer name (e.g., 'F.Cu')"),
minTrackWidth: z.number().optional().describe("Minimum track width for this layer (mm)"),
minClearance: z.number().optional().describe("Minimum clearance for this layer (mm)"),
minViaDiameter: z.number().optional().describe("Minimum via diameter for this layer (mm)"),
minViaDrill: z.number().optional().describe("Minimum via drill size for this layer (mm)")
},
async ({ layer, minTrackWidth, minClearance, minViaDiameter, minViaDrill }) => {
logger.debug(`Setting constraints for layer: ${layer}`);
const result = await callKicadScript("set_layer_constraints", {
layer,
minTrackWidth,
minClearance,
minViaDiameter,
minViaDrill
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Check Clearance Tool
// ------------------------------------------------------
server.tool(
"check_clearance",
{
item1: z.object({
type: z.enum(["track", "via", "pad", "zone", "component"]).describe("Type of the first item"),
id: z.string().optional().describe("ID of the first item (if applicable)"),
reference: z.string().optional().describe("Reference designator (for component)"),
position: z.object({
x: z.number().optional(),
y: z.number().optional(),
unit: z.enum(["mm", "inch"]).optional()
}).optional().describe("Position to check (if ID not provided)")
}).describe("First item to check"),
item2: z.object({
type: z.enum(["track", "via", "pad", "zone", "component"]).describe("Type of the second item"),
id: z.string().optional().describe("ID of the second item (if applicable)"),
reference: z.string().optional().describe("Reference designator (for component)"),
position: z.object({
x: z.number().optional(),
y: z.number().optional(),
unit: z.enum(["mm", "inch"]).optional()
}).optional().describe("Position to check (if ID not provided)")
}).describe("Second item to check")
},
async ({ item1, item2 }) => {
logger.debug(`Checking clearance between ${item1.type} and ${item2.type}`);
const result = await callKicadScript("check_clearance", {
item1,
item2
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Get DRC Violations Tool
// ------------------------------------------------------
server.tool(
"get_drc_violations",
{
severity: z.enum(["error", "warning", "all"]).optional().describe("Filter violations by severity")
},
async ({ severity }) => {
logger.debug('Getting DRC violations');
const result = await callKicadScript("get_drc_violations", { severity });
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
logger.info('Design rule tools registered');
}

260
src/tools/export.ts Normal file
View File

@@ -0,0 +1,260 @@
/**
* Export tools for KiCAD MCP server
*
* These tools handle exporting PCB data to various formats
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from '../logger.js';
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
/**
* Register export tools with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerExportTools(server: McpServer, callKicadScript: CommandFunction): void {
logger.info('Registering export tools');
// ------------------------------------------------------
// Export Gerber Tool
// ------------------------------------------------------
server.tool(
"export_gerber",
{
outputDir: z.string().describe("Directory to save Gerber files"),
layers: z.array(z.string()).optional().describe("Optional array of layer names to export (default: all)"),
useProtelExtensions: z.boolean().optional().describe("Whether to use Protel filename extensions"),
generateDrillFiles: z.boolean().optional().describe("Whether to generate drill files"),
generateMapFile: z.boolean().optional().describe("Whether to generate a map file"),
useAuxOrigin: z.boolean().optional().describe("Whether to use auxiliary axis as origin")
},
async ({ outputDir, layers, useProtelExtensions, generateDrillFiles, generateMapFile, useAuxOrigin }) => {
logger.debug(`Exporting Gerber files to: ${outputDir}`);
const result = await callKicadScript("export_gerber", {
outputDir,
layers,
useProtelExtensions,
generateDrillFiles,
generateMapFile,
useAuxOrigin
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export PDF Tool
// ------------------------------------------------------
server.tool(
"export_pdf",
{
outputPath: z.string().describe("Path to save the PDF file"),
layers: z.array(z.string()).optional().describe("Optional array of layer names to include (default: all)"),
blackAndWhite: z.boolean().optional().describe("Whether to export in black and white"),
frameReference: z.boolean().optional().describe("Whether to include frame reference"),
pageSize: z.enum(["A4", "A3", "A2", "A1", "A0", "Letter", "Legal", "Tabloid"]).optional().describe("Page size")
},
async ({ outputPath, layers, blackAndWhite, frameReference, pageSize }) => {
logger.debug(`Exporting PDF to: ${outputPath}`);
const result = await callKicadScript("export_pdf", {
outputPath,
layers,
blackAndWhite,
frameReference,
pageSize
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export SVG Tool
// ------------------------------------------------------
server.tool(
"export_svg",
{
outputPath: z.string().describe("Path to save the SVG file"),
layers: z.array(z.string()).optional().describe("Optional array of layer names to include (default: all)"),
blackAndWhite: z.boolean().optional().describe("Whether to export in black and white"),
includeComponents: z.boolean().optional().describe("Whether to include component outlines")
},
async ({ outputPath, layers, blackAndWhite, includeComponents }) => {
logger.debug(`Exporting SVG to: ${outputPath}`);
const result = await callKicadScript("export_svg", {
outputPath,
layers,
blackAndWhite,
includeComponents
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export 3D Model Tool
// ------------------------------------------------------
server.tool(
"export_3d",
{
outputPath: z.string().describe("Path to save the 3D model file"),
format: z.enum(["STEP", "STL", "VRML", "OBJ"]).describe("3D model format"),
includeComponents: z.boolean().optional().describe("Whether to include 3D component models"),
includeCopper: z.boolean().optional().describe("Whether to include copper layers"),
includeSolderMask: z.boolean().optional().describe("Whether to include solder mask"),
includeSilkscreen: z.boolean().optional().describe("Whether to include silkscreen")
},
async ({ outputPath, format, includeComponents, includeCopper, includeSolderMask, includeSilkscreen }) => {
logger.debug(`Exporting 3D model to: ${outputPath}`);
const result = await callKicadScript("export_3d", {
outputPath,
format,
includeComponents,
includeCopper,
includeSolderMask,
includeSilkscreen
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export BOM Tool
// ------------------------------------------------------
server.tool(
"export_bom",
{
outputPath: z.string().describe("Path to save the BOM file"),
format: z.enum(["CSV", "XML", "HTML", "JSON"]).describe("BOM file format"),
groupByValue: z.boolean().optional().describe("Whether to group components by value"),
includeAttributes: z.array(z.string()).optional().describe("Optional array of additional attributes to include")
},
async ({ outputPath, format, groupByValue, includeAttributes }) => {
logger.debug(`Exporting BOM to: ${outputPath}`);
const result = await callKicadScript("export_bom", {
outputPath,
format,
groupByValue,
includeAttributes
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export Netlist Tool
// ------------------------------------------------------
server.tool(
"export_netlist",
{
outputPath: z.string().describe("Path to save the netlist file"),
format: z.enum(["KiCad", "Spice", "Cadstar", "OrcadPCB2"]).optional().describe("Netlist format (default: KiCad)")
},
async ({ outputPath, format }) => {
logger.debug(`Exporting netlist to: ${outputPath}`);
const result = await callKicadScript("export_netlist", {
outputPath,
format
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export Position File Tool
// ------------------------------------------------------
server.tool(
"export_position_file",
{
outputPath: z.string().describe("Path to save the position file"),
format: z.enum(["CSV", "ASCII"]).optional().describe("File format (default: CSV)"),
units: z.enum(["mm", "inch"]).optional().describe("Units to use (default: mm)"),
side: z.enum(["top", "bottom", "both"]).optional().describe("Which board side to include (default: both)")
},
async ({ outputPath, format, units, side }) => {
logger.debug(`Exporting position file to: ${outputPath}`);
const result = await callKicadScript("export_position_file", {
outputPath,
format,
units,
side
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
// ------------------------------------------------------
// Export VRML Tool
// ------------------------------------------------------
server.tool(
"export_vrml",
{
outputPath: z.string().describe("Path to save the VRML file"),
includeComponents: z.boolean().optional().describe("Whether to include 3D component models"),
useRelativePaths: z.boolean().optional().describe("Whether to use relative paths for 3D models")
},
async ({ outputPath, includeComponents, useRelativePaths }) => {
logger.debug(`Exporting VRML to: ${outputPath}`);
const result = await callKicadScript("export_vrml", {
outputPath,
includeComponents,
useRelativePaths
});
return {
content: [{
type: "text",
text: JSON.stringify(result)
}]
};
}
);
logger.info('Export tools registered');
}

13
src/tools/index.ts Normal file
View File

@@ -0,0 +1,13 @@
/**
* Tools index for KiCAD MCP server
*
* Exports all tool registration functions
*/
export { registerProjectTools } from './project.js';
export { registerBoardTools } from './board.js';
export { registerComponentTools } from './component.js';
export { registerRoutingTools } from './routing.js';
export { registerDesignRuleTools } from './design-rules.js';
export { registerExportTools } from './export.js';
export { registerSchematicTools } from './schematic.js';