* feat(units): add mil unit support across all position/coordinate commands KiCad natively supports mils, so the MCP server should too. Added "mil" as a valid unit option in tool schemas and updated all unit-to-nanometer scale conversions across component, routing, outline, view, and IPC handler code paths. 1 mil = 25400 nm (0.0254 mm). Also fixes a pre-existing mypy overload error in pin_locator.py (str cast on dict.get key) that was blocking pre-commit on any Python file change. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(units): add mil to TypeScript tool schemas The Python-side mil support was added but the actual input validation happens in the TypeScript/Zod schemas. Updated all z.enum(["mm", "inch"]) to include "mil" across board, component, routing, design-rules, and export tool definitions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tools): replace CP-1252 mojibake with correct Unicode in board.ts Replace U+00C3 U+00D7 (×) with U+00D7 (×) in add_logo size output string. Character was mangled when file was saved as CP-1252 instead of UTF-8. * fix: restore em-dash and fix pre-commit mypy in component/routing component.py: replace CP-1252 mojibake (â€") with correct Unicode em-dash (—) in the 'Add to board first' comment. Addresses maintainer review on PR #162. routing.py: annotate ex/ey as float at first assignment site in _point_to_segment_distance_nm so mypy pre-commit hook passes cleanly on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
328 lines
10 KiB
TypeScript
328 lines
10 KiB
TypeScript
/**
|
|
* 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",
|
|
"Export PCB Gerber manufacturing files to a directory. Optionally include drill files, map files and choose layer subset.",
|
|
{
|
|
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",
|
|
"Export the PCB layout as a PDF document, optionally selecting layers, page size and colour mode.",
|
|
{
|
|
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",
|
|
"Export the PCB layout as an SVG vector image, optionally selecting layers and colour mode.",
|
|
{
|
|
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",
|
|
"Export the PCB as a 3D model (STEP, STL, VRML or OBJ) including optional copper, solder mask, silkscreen and component 3D models.",
|
|
{
|
|
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",
|
|
"Export a Bill of Materials (BOM) from the PCB in CSV, XML, HTML or JSON format.",
|
|
{
|
|
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",
|
|
"Export the schematic netlist to a file using kicad-cli. Supports KiCad XML (default), Spice (for simulation), Cadstar, and OrcadPCB2 formats. Use this when you need to write a netlist file to disk — for example to produce a SPICE file for simulation or to diff against a reference. To get net/component data inline without writing a file, use generate_netlist instead.",
|
|
{
|
|
schematicPath: z.string().describe("Absolute path to the .kicad_sch schematic file"),
|
|
outputPath: z.string().describe("Absolute path for the output file (e.g. /tmp/design.spice)"),
|
|
format: z
|
|
.enum(["KiCad", "Spice", "Cadstar", "OrcadPCB2"])
|
|
.optional()
|
|
.describe("Netlist format (default: KiCad)"),
|
|
},
|
|
async ({ schematicPath, outputPath, format }) => {
|
|
logger.debug(`Exporting netlist to: ${outputPath}`);
|
|
const result = await callKicadScript("export_netlist", {
|
|
schematicPath,
|
|
outputPath,
|
|
format,
|
|
});
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: JSON.stringify(result),
|
|
},
|
|
],
|
|
};
|
|
},
|
|
);
|
|
|
|
// ------------------------------------------------------
|
|
// Export Position File Tool
|
|
// ------------------------------------------------------
|
|
server.tool(
|
|
"export_position_file",
|
|
"Export a component placement/position file (pick-and-place) for PCB assembly in CSV or ASCII format.",
|
|
{
|
|
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", "mil", "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",
|
|
"Export the PCB as a VRML 3D model for use in web viewers or simulation tools.",
|
|
{
|
|
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");
|
|
}
|