feat: add footprint + symbol creator tools
Footprint tools: - create_footprint: generates .kicad_mod via f-string (SMD/THT, courtyard, silkscreen, fab) - edit_footprint_pad: updates size/position/drill/shape in-place - list_footprint_libraries: scans .pretty directories - register_footprint_library: adds entry to fp-lib-table Symbol tools: - create_symbol: generates .kicad_sym with pins, rectangles, polylines - delete_symbol: removes symbol from library - list_symbols_in_library: lists all symbols in a .kicad_sym file - register_symbol_library: adds entry to sym-lib-table Also: - Fix footprint format version: 20250114 (schematic) -> 20241229 (footprint) - Two MCP prompts: create_footprint_guide + footprint_ipc_checklist Live tested: TMC2209 footprint (19 pads), R_0603_Test, edit_footprint_pad, TMC2209_Custom symbol with pins all confirmed in KiCAD 9
This commit is contained in:
137
src/prompts/footprint.ts
Normal file
137
src/prompts/footprint.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Footprint prompts for KiCAD MCP server
|
||||
*
|
||||
* Guides Claude in creating and editing KiCAD footprints (.kicad_mod)
|
||||
* using the create_footprint, edit_footprint_pad, and list_footprint_libraries tools.
|
||||
*/
|
||||
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { z } from "zod";
|
||||
import { logger } from "../logger.js";
|
||||
|
||||
export function registerFootprintPrompts(server: McpServer): void {
|
||||
logger.info("Registering footprint prompts");
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Create Footprint Prompt
|
||||
// ------------------------------------------------------
|
||||
server.prompt(
|
||||
"create_footprint_guide",
|
||||
{
|
||||
component: z
|
||||
.string()
|
||||
.describe(
|
||||
"Component description, e.g. 'SOT-23 NPN transistor' or '2-pin JST XH 2.5mm connector'",
|
||||
),
|
||||
libraryPath: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Target .pretty library path (optional)"),
|
||||
},
|
||||
() => ({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `You are a KiCAD footprint expert. Create a correct KiCAD 9 footprint using the create_footprint tool.
|
||||
|
||||
## Component to footprint
|
||||
{{component}}
|
||||
|
||||
## Library path
|
||||
{{libraryPath}}
|
||||
|
||||
## Rules for correct footprints
|
||||
|
||||
### Coordinate system
|
||||
- Origin (0,0) is the footprint anchor, typically the centre of the pad pattern.
|
||||
- X increases to the right, Y increases downward (same as KiCAD screen).
|
||||
- All values in millimetres.
|
||||
|
||||
### SMD pads
|
||||
- type: "smd"
|
||||
- Default layers: ["F.Cu", "F.Paste", "F.Mask"]
|
||||
- No drill needed.
|
||||
- Common shapes: "rect" for square/rectangular, "roundrect" for ICs.
|
||||
|
||||
### THT pads
|
||||
- type: "thru_hole"
|
||||
- Default layers: ["*.Cu", "*.Mask"]
|
||||
- drill required (round = scalar, oval = {w, h}).
|
||||
- Pad 1 is typically square (rect), remaining pads are circle.
|
||||
|
||||
### Courtyard (F.CrtYd)
|
||||
- Add 0.25 mm clearance around the outermost extent of pads.
|
||||
- Line width: 0.05 mm.
|
||||
|
||||
### Silkscreen (F.SilkS)
|
||||
- Shows the component body outline, typically slightly inside the courtyard.
|
||||
- Line width: 0.12 mm.
|
||||
- Must not overlap pads.
|
||||
|
||||
### Fab layer (F.Fab)
|
||||
- Shows the realistic component outline with pin-1 marker.
|
||||
- Line width: 0.10 mm.
|
||||
|
||||
### Reference text
|
||||
- Place "REF**" above the courtyard (negative Y = above).
|
||||
- Value text below the courtyard (positive Y = below).
|
||||
|
||||
## Workflow
|
||||
1. Calculate pad positions from datasheet pitch and land pattern.
|
||||
2. Call create_footprint with pads[], courtyard, silkscreen, fabLayer.
|
||||
3. Verify with edit_footprint_pad if any correction is needed.
|
||||
|
||||
## Common packages quick reference
|
||||
| Package | Pitch | Pad size (SMD) | Notes |
|
||||
|-----------|--------|------------------|------------------------------|
|
||||
| 0402 | 1.0 mm | 0.6 × 0.7 mm | Very small, min 0.5 mm drill |
|
||||
| 0603 | 1.6 mm | 1.0 × 1.0 mm | Standard small passive |
|
||||
| 0805 | 2.0 mm | 1.4 × 1.2 mm | Easy to hand-solder |
|
||||
| SOT-23 | 0.95 mm| 1.0 × 1.3 mm | 3-pin, 2 on one side |
|
||||
| SOT-23-5 | 0.95 mm| 0.6 × 1.0 mm | 5-pin |
|
||||
| SOIC-8 | 1.27 mm| 1.6 × 0.6 mm | 4 pins each side |
|
||||
| DIP-8 | 2.54 mm| dia 1.6, drill 0.8| THT, 100 mil grid |
|
||||
|
||||
Now create the footprint for: {{component}}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Footprint IPC Checklist Prompt
|
||||
// ------------------------------------------------------
|
||||
server.prompt(
|
||||
"footprint_ipc_checklist",
|
||||
{
|
||||
footprintPath: z
|
||||
.string()
|
||||
.describe("Path to the .kicad_mod file to review"),
|
||||
},
|
||||
() => ({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `Review the footprint at {{footprintPath}} against IPC-7351 land pattern guidelines.
|
||||
|
||||
Check:
|
||||
1. **Pad size** – is the copper area sufficient for soldering (not undersized)?
|
||||
2. **Courtyard** – at least 0.25 mm clearance around all pads?
|
||||
3. **Silkscreen** – does it overlap pads? (it should NOT)
|
||||
4. **Pad 1 marker** – is pin 1 identifiable (square pad or triangle on silkscreen)?
|
||||
5. **Drill size** – for THT: drill ≥ lead diameter + 0.3 mm?
|
||||
6. **Layer assignments** – SMD pads: F.Cu/F.Paste/F.Mask; THT: *.Cu/*.Mask?
|
||||
7. **Anchor** – is the origin centred on the pad pattern?
|
||||
|
||||
Use edit_footprint_pad to fix any issues found.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Prompts index for KiCAD MCP server
|
||||
*
|
||||
* Exports all prompt registration functions
|
||||
*/
|
||||
|
||||
export { registerComponentPrompts } from './component.js';
|
||||
export { registerRoutingPrompts } from './routing.js';
|
||||
export { registerDesignPrompts } from './design.js';
|
||||
/**
|
||||
* Prompts index for KiCAD MCP server
|
||||
*
|
||||
* Exports all prompt registration functions
|
||||
*/
|
||||
|
||||
export { registerComponentPrompts } from "./component.js";
|
||||
export { registerRoutingPrompts } from "./routing.js";
|
||||
export { registerDesignPrompts } from "./design.js";
|
||||
export { registerFootprintPrompts } from "./footprint.js";
|
||||
|
||||
@@ -22,6 +22,8 @@ import { registerLibraryTools } from "./tools/library.js";
|
||||
import { registerSymbolLibraryTools } from "./tools/library-symbol.js";
|
||||
import { registerJLCPCBApiTools } from "./tools/jlcpcb-api.js";
|
||||
import { registerDatasheetTools } from "./tools/datasheet.js";
|
||||
import { registerFootprintTools } from "./tools/footprint.js";
|
||||
import { registerSymbolCreatorTools } from "./tools/symbol-creator.js";
|
||||
import { registerUITools } from "./tools/ui.js";
|
||||
import { registerRouterTools } from "./tools/router.js";
|
||||
|
||||
@@ -35,6 +37,7 @@ import { registerLibraryResources } from "./resources/library.js";
|
||||
import { registerComponentPrompts } from "./prompts/component.js";
|
||||
import { registerRoutingPrompts } from "./prompts/routing.js";
|
||||
import { registerDesignPrompts } from "./prompts/design.js";
|
||||
import { registerFootprintPrompts } from "./prompts/footprint.js";
|
||||
|
||||
/**
|
||||
* Find the Python executable to use
|
||||
@@ -243,6 +246,8 @@ export class KiCADMcpServer {
|
||||
registerSymbolLibraryTools(this.server, this.callKicadScript.bind(this));
|
||||
registerJLCPCBApiTools(this.server, this.callKicadScript.bind(this));
|
||||
registerDatasheetTools(this.server, this.callKicadScript.bind(this));
|
||||
registerFootprintTools(this.server, this.callKicadScript.bind(this));
|
||||
registerSymbolCreatorTools(this.server, this.callKicadScript.bind(this));
|
||||
registerUITools(this.server, this.callKicadScript.bind(this));
|
||||
|
||||
// Register all resources
|
||||
@@ -255,6 +260,7 @@ export class KiCADMcpServer {
|
||||
registerComponentPrompts(this.server);
|
||||
registerRoutingPrompts(this.server);
|
||||
registerDesignPrompts(this.server);
|
||||
registerFootprintPrompts(this.server);
|
||||
|
||||
logger.info("All KiCAD tools, resources, and prompts registered");
|
||||
logger.info(
|
||||
|
||||
237
src/tools/footprint.ts
Normal file
237
src/tools/footprint.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Footprint tools for KiCAD MCP server
|
||||
*
|
||||
* create_footprint – generate a complete .kicad_mod file in a .pretty library
|
||||
* edit_footprint_pad – update size / position / drill / shape of one pad
|
||||
* list_footprint_libraries – list available .pretty libraries
|
||||
*/
|
||||
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { z } from "zod";
|
||||
|
||||
// ---- shared sub-schemas ------------------------------------------------- //
|
||||
|
||||
const PadPosition = z.object({
|
||||
x: z.number().describe("X position in mm"),
|
||||
y: z.number().describe("Y position in mm"),
|
||||
angle: z.number().optional().describe("Rotation angle in degrees (default 0)"),
|
||||
});
|
||||
|
||||
const PadSize = z.object({
|
||||
w: z.number().describe("Width in mm"),
|
||||
h: z.number().describe("Height in mm"),
|
||||
});
|
||||
|
||||
const PadSchema = z.object({
|
||||
number: z.string().describe("Pad number / name, e.g. '1', '2', 'A1'"),
|
||||
type: z
|
||||
.enum(["smd", "thru_hole", "np_thru_hole"])
|
||||
.describe("Pad type: smd | thru_hole | np_thru_hole"),
|
||||
shape: z
|
||||
.enum(["rect", "circle", "oval", "roundrect"])
|
||||
.optional()
|
||||
.describe("Pad shape (default: rect for SMD, circle for THT)"),
|
||||
at: PadPosition.describe("Pad centre position"),
|
||||
size: PadSize.describe("Pad size in mm"),
|
||||
drill: z
|
||||
.union([
|
||||
z.number().describe("Round drill diameter in mm"),
|
||||
z.object({ w: z.number(), h: z.number() }).describe("Oval drill w×h in mm"),
|
||||
])
|
||||
.optional()
|
||||
.describe("Drill size (required for thru_hole pads)"),
|
||||
layers: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe("Override default layer list, e.g. ['F.Cu','F.Paste','F.Mask']"),
|
||||
roundrect_ratio: z
|
||||
.number()
|
||||
.min(0)
|
||||
.max(0.5)
|
||||
.optional()
|
||||
.describe("Corner radius ratio for roundrect shape (0.0–0.5, default 0.25)"),
|
||||
});
|
||||
|
||||
const RectSchema = z.object({
|
||||
x1: z.number().describe("Left X in mm"),
|
||||
y1: z.number().describe("Top Y in mm"),
|
||||
x2: z.number().describe("Right X in mm"),
|
||||
y2: z.number().describe("Bottom Y in mm"),
|
||||
width: z.number().optional().describe("Line width in mm"),
|
||||
});
|
||||
|
||||
// ---- tool registration --------------------------------------------------- //
|
||||
|
||||
export function registerFootprintTools(
|
||||
server: McpServer,
|
||||
callKicadScript: Function,
|
||||
) {
|
||||
// ── create_footprint ──────────────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"create_footprint",
|
||||
"Create a new KiCAD footprint (.kicad_mod) inside a .pretty library directory. " +
|
||||
"Supports SMD and THT pads, courtyard, silkscreen, and fab-layer rectangles.",
|
||||
{
|
||||
libraryPath: z
|
||||
.string()
|
||||
.describe(
|
||||
"Path to the .pretty library directory (created if missing). " +
|
||||
"E.g. C:/MyProject/MyLib.pretty",
|
||||
),
|
||||
name: z.string().describe("Footprint name, e.g. 'R_0603_Custom'"),
|
||||
description: z.string().optional().describe("Human-readable description"),
|
||||
tags: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Space-separated tag string, e.g. 'resistor SMD 0603'"),
|
||||
pads: z
|
||||
.array(PadSchema)
|
||||
.optional()
|
||||
.describe("List of pads to add (can be empty for outlines-only footprints)"),
|
||||
courtyard: RectSchema.optional().describe(
|
||||
"Courtyard rectangle on F.CrtYd (recommended: 0.25 mm clearance around pads)",
|
||||
),
|
||||
silkscreen: RectSchema.optional().describe(
|
||||
"Silkscreen rectangle on F.SilkS",
|
||||
),
|
||||
fabLayer: RectSchema.optional().describe(
|
||||
"Fab-layer rectangle on F.Fab (shows component body)",
|
||||
),
|
||||
refPosition: z
|
||||
.object({ x: z.number(), y: z.number() })
|
||||
.optional()
|
||||
.describe("Position of the REF** text (default: 0, -1.27)"),
|
||||
valuePosition: z
|
||||
.object({ x: z.number(), y: z.number() })
|
||||
.optional()
|
||||
.describe("Position of the Value text (default: 0, 1.27)"),
|
||||
overwrite: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe("Replace existing footprint file (default: false)"),
|
||||
},
|
||||
async (args: {
|
||||
libraryPath: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
tags?: string;
|
||||
pads?: z.infer<typeof PadSchema>[];
|
||||
courtyard?: z.infer<typeof RectSchema>;
|
||||
silkscreen?: z.infer<typeof RectSchema>;
|
||||
fabLayer?: z.infer<typeof RectSchema>;
|
||||
refPosition?: { x: number; y: number };
|
||||
valuePosition?: { x: number; y: number };
|
||||
overwrite?: boolean;
|
||||
}) => {
|
||||
const result = await callKicadScript("create_footprint", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── edit_footprint_pad ────────────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"edit_footprint_pad",
|
||||
"Edit an existing pad inside a .kicad_mod footprint file. " +
|
||||
"Updates size, position, drill, or shape without recreating the whole footprint.",
|
||||
{
|
||||
footprintPath: z
|
||||
.string()
|
||||
.describe("Full path to the .kicad_mod file, e.g. C:/MyLib.pretty/R_Custom.kicad_mod"),
|
||||
padNumber: z
|
||||
.union([z.string(), z.number()])
|
||||
.describe("Pad number to edit, e.g. '1' or 2"),
|
||||
size: PadSize.optional().describe("New pad size in mm"),
|
||||
at: PadPosition.optional().describe("New pad position in mm"),
|
||||
drill: z
|
||||
.union([
|
||||
z.number().describe("Round drill diameter in mm"),
|
||||
z.object({ w: z.number(), h: z.number() }).describe("Oval drill"),
|
||||
])
|
||||
.optional()
|
||||
.describe("New drill size (for THT pads)"),
|
||||
shape: z
|
||||
.enum(["rect", "circle", "oval", "roundrect"])
|
||||
.optional()
|
||||
.describe("New pad shape"),
|
||||
},
|
||||
async (args: {
|
||||
footprintPath: string;
|
||||
padNumber: string | number;
|
||||
size?: { w: number; h: number };
|
||||
at?: { x: number; y: number; angle?: number };
|
||||
drill?: number | { w: number; h: number };
|
||||
shape?: string;
|
||||
}) => {
|
||||
const result = await callKicadScript("edit_footprint_pad", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── register_footprint_library ───────────────────────────────────────── //
|
||||
server.tool(
|
||||
"register_footprint_library",
|
||||
"Register a .pretty footprint library in KiCAD's fp-lib-table so KiCAD can find the footprints. " +
|
||||
"Run this after create_footprint when KiCAD shows 'library not found in footprint library table'.",
|
||||
{
|
||||
libraryPath: z
|
||||
.string()
|
||||
.describe("Full path to the .pretty directory to register"),
|
||||
libraryName: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Nickname for the library in KiCAD (default: directory name without .pretty)"),
|
||||
description: z.string().optional().describe("Optional description"),
|
||||
scope: z
|
||||
.enum(["project", "global"])
|
||||
.optional()
|
||||
.describe(
|
||||
"project = writes fp-lib-table next to the .kicad_pro file (default); " +
|
||||
"global = writes to the user's global KiCAD config",
|
||||
),
|
||||
projectPath: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"Path to the .kicad_pro file or its directory (required for scope=project " +
|
||||
"when the library is not in the project folder)",
|
||||
),
|
||||
},
|
||||
async (args: {
|
||||
libraryPath: string;
|
||||
libraryName?: string;
|
||||
description?: string;
|
||||
scope?: "project" | "global";
|
||||
projectPath?: string;
|
||||
}) => {
|
||||
const result = await callKicadScript("register_footprint_library", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── list_footprint_libraries ─────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"list_footprint_libraries",
|
||||
"List available .pretty footprint libraries and their contents (first 20 footprints per library). " +
|
||||
"Searches KiCAD standard install paths by default.",
|
||||
{
|
||||
searchPaths: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe(
|
||||
"Override default search paths. Each entry should be a directory that contains .pretty subdirs.",
|
||||
),
|
||||
},
|
||||
async (args: { searchPaths?: string[] }) => {
|
||||
const result = await callKicadScript("list_footprint_libraries", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -14,3 +14,5 @@ export { registerSchematicTools } from "./schematic.js";
|
||||
export { registerLibraryTools } from "./library.js";
|
||||
export { registerUITools } from "./ui.js";
|
||||
export { registerDatasheetTools } from "./datasheet.js";
|
||||
export { registerFootprintTools } from "./footprint.js";
|
||||
export { registerSymbolCreatorTools } from "./symbol-creator.js";
|
||||
|
||||
194
src/tools/symbol-creator.ts
Normal file
194
src/tools/symbol-creator.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Symbol creator tools for KiCAD MCP server
|
||||
*
|
||||
* create_symbol – add a new symbol to a .kicad_sym library
|
||||
* delete_symbol – remove a symbol from a library
|
||||
* list_symbols_in_library – list all symbols in a .kicad_sym file
|
||||
* register_symbol_library – add library to sym-lib-table
|
||||
*/
|
||||
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { z } from "zod";
|
||||
|
||||
const PinSchema = z.object({
|
||||
name: z.string().describe("Pin name, e.g. 'VCC', 'GND', 'IN+', '~' for unnamed"),
|
||||
number: z.union([z.string(), z.number()]).describe("Pin number, e.g. '1', '2', 'A1'"),
|
||||
type: z
|
||||
.enum([
|
||||
"input", "output", "bidirectional", "tri_state", "passive",
|
||||
"free", "unspecified", "power_in", "power_out",
|
||||
"open_collector", "open_emitter", "no_connect",
|
||||
])
|
||||
.describe("Electrical pin type"),
|
||||
at: z.object({
|
||||
x: z.number().describe("X position in mm"),
|
||||
y: z.number().describe("Y position in mm"),
|
||||
angle: z.number().describe(
|
||||
"Direction the pin wire extends FROM the symbol body: 0=right, 90=up, 180=left, 270=down"
|
||||
),
|
||||
}).describe("Pin endpoint position (where the wire connects)"),
|
||||
length: z.number().optional().describe("Pin length in mm (default 2.54)"),
|
||||
shape: z
|
||||
.enum(["line", "inverted", "clock", "inverted_clock", "input_low",
|
||||
"clock_low", "output_low", "falling_edge_clock", "non_logic"])
|
||||
.optional()
|
||||
.describe("Pin graphic shape (default: line)"),
|
||||
});
|
||||
|
||||
const RectSchema = z.object({
|
||||
x1: z.number(), y1: z.number(),
|
||||
x2: z.number(), y2: z.number(),
|
||||
width: z.number().optional().describe("Stroke width in mm (default 0.254)"),
|
||||
fill: z.enum(["none", "outline", "background"]).optional()
|
||||
.describe("Fill type (default: background)"),
|
||||
});
|
||||
|
||||
const PolylineSchema = z.object({
|
||||
points: z.array(z.object({ x: z.number(), y: z.number() }))
|
||||
.describe("List of XY points in mm"),
|
||||
width: z.number().optional().describe("Stroke width in mm (default 0.254)"),
|
||||
fill: z.enum(["none", "outline", "background"]).optional(),
|
||||
});
|
||||
|
||||
export function registerSymbolCreatorTools(
|
||||
server: McpServer,
|
||||
callKicadScript: Function,
|
||||
) {
|
||||
// ── create_symbol ────────────────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"create_symbol",
|
||||
"Create a new schematic symbol in a .kicad_sym library file (created if missing). " +
|
||||
"After creation, use register_symbol_library so KiCAD finds it. " +
|
||||
"Pin positions are where the wire connects; the symbol body is drawn between them.\n\n" +
|
||||
"Coordinate tips:\n" +
|
||||
"- Body rectangle typically spans ±2.54 to ±5.08 mm\n" +
|
||||
"- Pins on left side: at.x = body_left - length, angle=0 (wire goes right)\n" +
|
||||
"- Pins on right side: at.x = body_right + length, angle=180 (wire goes left)\n" +
|
||||
"- Pins on top: at.y = body_top + length, angle=270 (wire goes down)\n" +
|
||||
"- Pins on bottom: at.y = body_bottom - length, angle=90 (wire goes up)\n" +
|
||||
"- Standard pin length: 2.54 mm, standard grid: 2.54 mm",
|
||||
{
|
||||
libraryPath: z
|
||||
.string()
|
||||
.describe("Path to the .kicad_sym file (created if missing)"),
|
||||
name: z.string().describe("Symbol name, e.g. 'TMC2209', 'MyOpAmp'"),
|
||||
referencePrefix: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Schematic reference prefix: 'U' (IC), 'R' (resistor), 'J' (connector), etc. Default: 'U'"),
|
||||
description: z.string().optional().describe("Human-readable description"),
|
||||
keywords: z.string().optional().describe("Space-separated search keywords"),
|
||||
datasheet: z.string().optional().describe("Datasheet URL or '~'"),
|
||||
footprint: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Default footprint, e.g. 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm'"),
|
||||
inBom: z.boolean().optional().describe("Include in BOM (default true)"),
|
||||
onBoard: z.boolean().optional().describe("Include in netlist for PCB (default true)"),
|
||||
pins: z
|
||||
.array(PinSchema)
|
||||
.optional()
|
||||
.describe("List of pins (can be empty for graphical-only symbols)"),
|
||||
rectangles: z
|
||||
.array(RectSchema)
|
||||
.optional()
|
||||
.describe("Body rectangle(s). Typically one rectangle defining the IC body."),
|
||||
polylines: z
|
||||
.array(PolylineSchema)
|
||||
.optional()
|
||||
.describe("Polyline graphics for custom body shapes (op-amp triangles, etc.)"),
|
||||
overwrite: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe("Replace existing symbol with same name (default false)"),
|
||||
},
|
||||
async (args: {
|
||||
libraryPath: string;
|
||||
name: string;
|
||||
referencePrefix?: string;
|
||||
description?: string;
|
||||
keywords?: string;
|
||||
datasheet?: string;
|
||||
footprint?: string;
|
||||
inBom?: boolean;
|
||||
onBoard?: boolean;
|
||||
pins?: z.infer<typeof PinSchema>[];
|
||||
rectangles?: z.infer<typeof RectSchema>[];
|
||||
polylines?: z.infer<typeof PolylineSchema>[];
|
||||
overwrite?: boolean;
|
||||
}) => {
|
||||
const result = await callKicadScript("create_symbol", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── delete_symbol ────────────────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"delete_symbol",
|
||||
"Remove a symbol from a .kicad_sym library file.",
|
||||
{
|
||||
libraryPath: z.string().describe("Path to the .kicad_sym file"),
|
||||
name: z.string().describe("Symbol name to delete"),
|
||||
},
|
||||
async (args: { libraryPath: string; name: string }) => {
|
||||
const result = await callKicadScript("delete_symbol", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── list_symbols_in_library ──────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"list_symbols_in_library",
|
||||
"List all symbol names in a .kicad_sym library file.",
|
||||
{
|
||||
libraryPath: z.string().describe("Path to the .kicad_sym file"),
|
||||
},
|
||||
async (args: { libraryPath: string }) => {
|
||||
const result = await callKicadScript("list_symbols_in_library", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── register_symbol_library ──────────────────────────────────────────── //
|
||||
server.tool(
|
||||
"register_symbol_library",
|
||||
"Register a .kicad_sym library in KiCAD's sym-lib-table so symbols can be used in schematics. " +
|
||||
"Run this after create_symbol when KiCAD shows 'library not found'.",
|
||||
{
|
||||
libraryPath: z
|
||||
.string()
|
||||
.describe("Full path to the .kicad_sym file"),
|
||||
libraryName: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Nickname (default: file name without extension)"),
|
||||
description: z.string().optional(),
|
||||
scope: z
|
||||
.enum(["project", "global"])
|
||||
.optional()
|
||||
.describe("project = writes sym-lib-table next to .kicad_pro; global = user config"),
|
||||
projectPath: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Path to .kicad_pro or its directory (for scope=project)"),
|
||||
},
|
||||
async (args: {
|
||||
libraryPath: string;
|
||||
libraryName?: string;
|
||||
description?: string;
|
||||
scope?: "project" | "global";
|
||||
projectPath?: string;
|
||||
}) => {
|
||||
const result = await callKicadScript("register_symbol_library", args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user