🎉 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>
262 lines
9.4 KiB
TypeScript
262 lines
9.4 KiB
TypeScript
/**
|
|
* 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');
|
|
}
|