diff --git a/src/tools/registry.ts b/src/tools/registry.ts index 41e7ad4..4799052 100644 --- a/src/tools/registry.ts +++ b/src/tools/registry.ts @@ -1,307 +1,308 @@ -/** - * Tool Registry for KiCAD MCP Server - * - * Centralizes all tool definitions and provides lookup/search functionality - */ - -import { z } from "zod"; - -export interface ToolDefinition { - name: string; - description: string; - inputSchema: z.ZodObject | z.ZodType; - // Handler will be registered separately in the existing tool files -} - -export interface ToolCategory { - name: string; - description: string; - tools: string[]; // Tool names in this category -} - -/** - * Tool category definitions - * Each category groups related tools for better organization - */ -export const toolCategories: ToolCategory[] = [ - { - name: "board", - description: - "Board configuration: layers, mounting holes, zones, visualization", - tools: [ - "add_layer", - "set_active_layer", - "get_layer_list", - "add_mounting_hole", - "add_board_text", - "add_zone", - "get_board_extents", - "get_board_2d_view", - "launch_kicad_ui", - ], - }, - { - name: "component", - description: - "Advanced component operations: edit, delete, search, group, annotate", - tools: [ - "rotate_component", - "delete_component", - "edit_component", - "find_component", - "get_component_properties", - "add_component_annotation", - "group_components", - "replace_component", - ], - }, - { - name: "export", - description: - "File export for fabrication and documentation: Gerber, PDF, BOM, 3D models", - tools: [ - "export_gerber", - "export_pdf", - "export_svg", - "export_3d", - "export_bom", - "export_netlist", - "export_position_file", - "export_vrml", - ], - }, - { - name: "drc", - description: - "Design rule checking and electrical validation: DRC, net classes, clearances", - tools: [ - "set_design_rules", - "get_design_rules", - "run_drc", - "add_net_class", - "assign_net_to_class", - "set_layer_constraints", - "check_clearance", - "get_drc_violations", - ], - }, - { - name: "schematic", - description: - "Schematic operations: create, inspect, add/edit/delete components, wire connections, netlists, annotation", - tools: [ - "create_schematic", - "add_schematic_component", - "list_schematic_components", - "move_schematic_component", - "rotate_schematic_component", - "annotate_schematic", - "add_schematic_wire", - "delete_schematic_wire", - "add_schematic_junction", - "add_schematic_net_label", - "delete_schematic_net_label", - "connect_to_net", - "connect_passthrough", - "get_net_connections", - "list_schematic_nets", - "list_schematic_wires", - "list_schematic_labels", - "get_wire_connections", - "generate_netlist", - "sync_schematic_to_board", - "get_schematic_view", - "export_schematic_svg", - "export_schematic_pdf", - ], - }, - { - name: "library", - description: - "Footprint library access: search, browse, get footprint information", - tools: [ - "list_libraries", - "search_footprints", - "list_library_footprints", - "get_footprint_info", - ], - }, - { - name: "routing", - description: "Advanced routing operations: vias, copper pours", - tools: ["add_via", "add_copper_pour"], - }, - { - name: "autoroute", - description: - "Freerouting autorouter: automatic PCB routing via Specctra DSN/SES", - tools: ["autoroute", "export_dsn", "import_ses", "check_freerouting"], - }, -]; - -/** - * Direct tools that are always visible (not routed) - * These are the most frequently used tools - */ -export const directToolNames = [ - // Project lifecycle - "create_project", - "open_project", - "save_project", - "snapshot_project", - "get_project_info", - - // Core PCB operations - "place_component", - "move_component", - "add_net", - "route_trace", - "get_board_info", - "set_board_size", - - // Board setup - "add_board_outline", - - // Schematic essentials (always visible so AI uses them correctly) - "add_schematic_component", - "list_schematic_components", - "annotate_schematic", - "connect_passthrough", - "connect_to_net", - "add_schematic_net_label", - - // Schematic <-> PCB sync (F8 equivalent) - "sync_schematic_to_board", - - // UI management - "check_kicad_ui", -]; - -// Build lookup maps at module load time -const categoryMap = new Map(); -const toolCategoryMap = new Map(); - -export function initializeRegistry() { - // Build category map - for (const category of toolCategories) { - categoryMap.set(category.name, category); - - // Build tool -> category map - for (const toolName of category.tools) { - toolCategoryMap.set(toolName, category.name); - } - } -} - -/** - * Get a category by name - */ -export function getCategory(name: string): ToolCategory | undefined { - return categoryMap.get(name); -} - -/** - * Get the category name for a tool - */ -export function getToolCategory(toolName: string): string | undefined { - return toolCategoryMap.get(toolName); -} - -/** - * Get all categories - */ -export function getAllCategories(): ToolCategory[] { - return toolCategories; -} - -/** - * Get all routed tool names (excludes direct tools) - */ -export function getRoutedToolNames(): string[] { - const allRoutedTools: string[] = []; - for (const category of toolCategories) { - allRoutedTools.push(...category.tools); - } - return allRoutedTools; -} - -/** - * Check if a tool is a direct tool - */ -export function isDirectTool(toolName: string): boolean { - return directToolNames.includes(toolName); -} - -/** - * Check if a tool is a routed tool - */ -export function isRoutedTool(toolName: string): boolean { - return toolCategoryMap.has(toolName); -} - -/** - * Search for tools by keyword - * Searches tool names, descriptions, and category names - */ -export interface SearchResult { - category: string; - tool: string; - description: string; -} - -export function searchTools(query: string): SearchResult[] { - const q = query.toLowerCase(); - const matches: SearchResult[] = []; - - // Search direct tools first - for (const toolName of directToolNames) { - if (toolName.toLowerCase().includes(q)) { - matches.push({ - category: "direct", - tool: toolName, - description: `${toolName} (direct tool — call directly, no execute_tool needed)`, - }); - } - } - - // Search routed tools by name and category - for (const category of toolCategories) { - const categoryMatch = - category.name.toLowerCase().includes(q) || - category.description.toLowerCase().includes(q); - - for (const toolName of category.tools) { - if (toolName.toLowerCase().includes(q) || categoryMatch) { - matches.push({ - category: category.name, - tool: toolName, - description: `${toolName} (${category.name})`, - }); - } - } - } - - return matches.slice(0, 20); // Limit results -} - -/** - * Get statistics about the tool registry - */ -export function getRegistryStats() { - const routedToolCount = getRoutedToolNames().length; - const directToolCount = directToolNames.length; - - return { - total_categories: toolCategories.length, - total_routed_tools: routedToolCount, - total_direct_tools: directToolCount, - total_tools: routedToolCount + directToolCount, - categories: toolCategories.map((c) => ({ - name: c.name, - tool_count: c.tools.length, - })), - }; -} - -// Initialize on module load -initializeRegistry(); +/** + * Tool Registry for KiCAD MCP Server + * + * Centralizes all tool definitions and provides lookup/search functionality + */ + +import { z } from 'zod'; + +export interface ToolDefinition { + name: string; + description: string; + inputSchema: z.ZodObject | z.ZodType; + // Handler will be registered separately in the existing tool files +} + +export interface ToolCategory { + name: string; + description: string; + tools: string[]; // Tool names in this category +} + +/** + * Tool category definitions + * Each category groups related tools for better organization + */ +export const toolCategories: ToolCategory[] = [ + { + name: "board", + description: "Board configuration: layers, mounting holes, zones, visualization", + tools: [ + "add_layer", + "set_active_layer", + "get_layer_list", + "add_mounting_hole", + "add_board_text", + "add_zone", + "get_board_extents", + "get_board_2d_view", + "launch_kicad_ui" + ] + }, + { + name: "component", + description: "Advanced component operations: edit, delete, search, group, annotate", + tools: [ + "rotate_component", + "delete_component", + "edit_component", + "find_component", + "get_component_properties", + "add_component_annotation", + "group_components", + "replace_component" + ] + }, + { + name: "export", + description: "File export for fabrication and documentation: Gerber, PDF, BOM, 3D models", + tools: [ + "export_gerber", + "export_pdf", + "export_svg", + "export_3d", + "export_bom", + "export_netlist", + "export_position_file", + "export_vrml" + ] + }, + { + name: "drc", + description: "Design rule checking and electrical validation: DRC, net classes, clearances", + tools: [ + "set_design_rules", + "get_design_rules", + "run_drc", + "add_net_class", + "assign_net_to_class", + "set_layer_constraints", + "check_clearance", + "get_drc_violations" + ] + }, + { + name: "schematic", + description: "Schematic operations: create, inspect, add/edit/delete components, wire connections, netlists, annotation", + tools: [ + "create_schematic", + "add_schematic_component", + "list_schematic_components", + "move_schematic_component", + "rotate_schematic_component", + "annotate_schematic", + "add_schematic_wire", + "delete_schematic_wire", + "add_schematic_junction", + "add_schematic_net_label", + "delete_schematic_net_label", + "connect_to_net", + "connect_passthrough", + "get_net_connections", + "list_schematic_nets", + "list_schematic_wires", + "list_schematic_labels", + "get_wire_connections", + "generate_netlist", + "sync_schematic_to_board", + "get_schematic_view", + "export_schematic_svg", + "export_schematic_pdf" + ] + }, + { + name: "library", + description: "Footprint library access: search, browse, get footprint information", + tools: [ + "list_libraries", + "search_footprints", + "list_library_footprints", + "get_footprint_info" + ] + }, + { + name: "routing", + description: "Advanced routing operations: vias, copper pours", + tools: [ + "add_via", + "add_copper_pour" + ] + }, + { + name: "autoroute", + description: "Freerouting autorouter: automatic PCB routing via Specctra DSN/SES", + tools: [ + "autoroute", + "export_dsn", + "import_ses", + "check_freerouting" + ] + } +]; + +/** + * Direct tools that are always visible (not routed) + * These are the most frequently used tools + */ +export const directToolNames = [ + // Project lifecycle + "create_project", + "open_project", + "save_project", + "snapshot_project", + "get_project_info", + + // Core PCB operations + "place_component", + "move_component", + "add_net", + "route_trace", + "get_board_info", + "set_board_size", + + // Board setup + "add_board_outline", + + // Schematic essentials (always visible so AI uses them correctly) + "add_schematic_component", + "list_schematic_components", + "annotate_schematic", + "connect_passthrough", + "connect_to_net", + "add_schematic_net_label", + + // Schematic <-> PCB sync (F8 equivalent) + "sync_schematic_to_board", + + // UI management + "check_kicad_ui" +]; + +// Build lookup maps at module load time +const categoryMap = new Map(); +const toolCategoryMap = new Map(); + +export function initializeRegistry() { + // Build category map + for (const category of toolCategories) { + categoryMap.set(category.name, category); + + // Build tool -> category map + for (const toolName of category.tools) { + toolCategoryMap.set(toolName, category.name); + } + } +} + +/** + * Get a category by name + */ +export function getCategory(name: string): ToolCategory | undefined { + return categoryMap.get(name); +} + +/** + * Get the category name for a tool + */ +export function getToolCategory(toolName: string): string | undefined { + return toolCategoryMap.get(toolName); +} + +/** + * Get all categories + */ +export function getAllCategories(): ToolCategory[] { + return toolCategories; +} + +/** + * Get all routed tool names (excludes direct tools) + */ +export function getRoutedToolNames(): string[] { + const allRoutedTools: string[] = []; + for (const category of toolCategories) { + allRoutedTools.push(...category.tools); + } + return allRoutedTools; +} + +/** + * Check if a tool is a direct tool + */ +export function isDirectTool(toolName: string): boolean { + return directToolNames.includes(toolName); +} + +/** + * Check if a tool is a routed tool + */ +export function isRoutedTool(toolName: string): boolean { + return toolCategoryMap.has(toolName); +} + +/** + * Search for tools by keyword + * Searches tool names, descriptions, and category names + */ +export interface SearchResult { + category: string; + tool: string; + description: string; +} + +export function searchTools(query: string): SearchResult[] { + const q = query.toLowerCase(); + const matches: SearchResult[] = []; + + // Search direct tools first + for (const toolName of directToolNames) { + if (toolName.toLowerCase().includes(q)) { + matches.push({ + category: "direct", + tool: toolName, + description: `${toolName} (direct tool — call directly, no execute_tool needed)` + }); + } + } + + // Search routed tools by name and category + for (const category of toolCategories) { + const categoryMatch = + category.name.toLowerCase().includes(q) || + category.description.toLowerCase().includes(q); + + for (const toolName of category.tools) { + if (toolName.toLowerCase().includes(q) || categoryMatch) { + matches.push({ + category: category.name, + tool: toolName, + description: `${toolName} (${category.name})` + }); + } + } + } + + return matches.slice(0, 20); // Limit results +} + +/** + * Get statistics about the tool registry + */ +export function getRegistryStats() { + const routedToolCount = getRoutedToolNames().length; + const directToolCount = directToolNames.length; + + return { + total_categories: toolCategories.length, + total_routed_tools: routedToolCount, + total_direct_tools: directToolCount, + total_tools: routedToolCount + directToolCount, + categories: toolCategories.map(c => ({ + name: c.name, + tool_count: c.tools.length + })) + }; +} + +// Initialize on module load +initializeRegistry(); diff --git a/src/tools/schematic.ts b/src/tools/schematic.ts index a021f3f..1fcf905 100644 --- a/src/tools/schematic.ts +++ b/src/tools/schematic.ts @@ -161,37 +161,15 @@ preserves the component's position and UUID. Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_component.`, { schematicPath: z.string().describe("Path to the .kicad_sch file"), - reference: z - .string() - .describe( - "Current reference designator of the component (e.g. R1, U3)", - ), - footprint: z - .string() - .optional() - .describe( - "New KiCAD footprint string (e.g. Resistor_SMD:R_0603_1608Metric)", - ), - value: z - .string() - .optional() - .describe("New value string (e.g. 10k, 100nF)"), - newReference: z - .string() - .optional() - .describe("Rename the reference designator (e.g. R1 → R10)"), - fieldPositions: z - .record( - z.object({ - x: z.number(), - y: z.number(), - angle: z.number().optional().default(0), - }), - ) - .optional() - .describe( - 'Reposition field labels: map of field name to {x, y, angle} (e.g. {"Reference": {"x": 12.5, "y": 17.0}})', - ), + reference: z.string().describe("Current reference designator of the component (e.g. R1, U3)"), + footprint: z.string().optional().describe("New KiCAD footprint string (e.g. Resistor_SMD:R_0603_1608Metric)"), + value: z.string().optional().describe("New value string (e.g. 10k, 100nF)"), + newReference: z.string().optional().describe("Rename the reference designator (e.g. R1 → R10)"), + fieldPositions: z.record(z.object({ + x: z.number(), + y: z.number(), + angle: z.number().optional().default(0), + })).optional().describe("Reposition field labels: map of field name to {x, y, angle} (e.g. {\"Reference\": {\"x\": 12.5, \"y\": 17.0}})"), }, async (args: { schematicPath: string; @@ -232,9 +210,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp "Get full component info from a schematic: position, field values, and each field's label position (at x/y/angle). Use this to inspect or prepare repositioning of Reference/Value labels.", { schematicPath: z.string().describe("Path to the .kicad_sch file"), - reference: z - .string() - .describe("Component reference designator (e.g. R1, U1)"), + reference: z.string().describe("Component reference designator (e.g. R1, U1)"), }, async (args: { schematicPath: string; reference: string }) => { const result = await callKicadScript("get_schematic_component", args); @@ -244,24 +220,20 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp : "unknown"; const fieldLines = Object.entries(result.fields ?? {}).map( ([name, f]: [string, any]) => - ` ${name}: "${f.value}" @ (${f.x}, ${f.y}, angle=${f.angle}°)`, + ` ${name}: "${f.value}" @ (${f.x}, ${f.y}, angle=${f.angle}°)` ); return { - content: [ - { - type: "text", - text: `Component ${result.reference} at ${pos}\nFields:\n${fieldLines.join("\n")}`, - }, - ], + content: [{ + type: "text", + text: `Component ${result.reference} at ${pos}\nFields:\n${fieldLines.join("\n")}`, + }], }; } return { - content: [ - { - type: "text", - text: `Failed to get component: ${result.message || "Unknown error"}`, - }, - ], + content: [{ + type: "text", + text: `Failed to get component: ${result.message || "Unknown error"}`, + }], }; }, ); @@ -608,14 +580,8 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp schematicPath: z.string().describe("Path to the .kicad_sch file"), filter: z .object({ - libId: z - .string() - .optional() - .describe("Filter by library ID (e.g., 'Device:R')"), - referencePrefix: z - .string() - .optional() - .describe("Filter by reference prefix (e.g., 'R', 'C', 'U')"), + libId: z.string().optional().describe("Filter by library ID (e.g., 'Device:R')"), + referencePrefix: z.string().optional().describe("Filter by reference prefix (e.g., 'R', 'C', 'U')"), }) .optional() .describe("Optional filters"), @@ -629,9 +595,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp const comps = result.components || []; if (comps.length === 0) { return { - content: [ - { type: "text", text: "No components found in schematic." }, - ], + content: [{ type: "text", text: "No components found in schematic." }], }; } const lines = comps.map( @@ -692,10 +656,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp } return { content: [ - { - type: "text", - text: `Failed to list nets: ${result.message || "Unknown error"}`, - }, + { type: "text", text: `Failed to list nets: ${result.message || "Unknown error"}` }, ], isError: true, }; @@ -733,10 +694,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp } return { content: [ - { - type: "text", - text: `Failed to list wires: ${result.message || "Unknown error"}`, - }, + { type: "text", text: `Failed to list wires: ${result.message || "Unknown error"}` }, ], isError: true, }; @@ -774,10 +732,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp } return { content: [ - { - type: "text", - text: `Failed to list labels: ${result.message || "Unknown error"}`, - }, + { type: "text", text: `Failed to list labels: ${result.message || "Unknown error"}` }, ], isError: true, }; @@ -834,7 +789,10 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp schematicPath: z.string().describe("Path to the .kicad_sch file"), reference: z.string().describe("Reference designator (e.g., R1, U1)"), angle: z.number().describe("Rotation angle in degrees (0, 90, 180, 270)"), - mirror: z.enum(["x", "y"]).optional().describe("Optional mirror axis"), + mirror: z + .enum(["x", "y"]) + .optional() + .describe("Optional mirror axis"), }, async (args: { schematicPath: string; @@ -1078,14 +1036,8 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp .enum(["png", "svg"]) .optional() .describe("Output format (default: png)"), - width: z - .number() - .optional() - .describe("Image width in pixels (default: 1200)"), - height: z - .number() - .optional() - .describe("Image height in pixels (default: 900)"), + width: z.number().optional().describe("Image width in pixels (default: 1200)"), + height: z.number().optional().describe("Image height in pixels (default: 900)"), }, async (args: { schematicPath: string; @@ -1255,35 +1207,19 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp "get_schematic_view_region", "Export a cropped region of the schematic as an image (PNG or SVG). Specify bounding box coordinates in schematic mm. Useful for zooming into a specific area to inspect wiring or layout.", { - schematicPath: z - .string() - .describe("Path to the .kicad_sch schematic file"), + schematicPath: z.string().describe("Path to the .kicad_sch schematic file"), x1: z.number().describe("Left X coordinate of the region in mm"), y1: z.number().describe("Top Y coordinate of the region in mm"), x2: z.number().describe("Right X coordinate of the region in mm"), y2: z.number().describe("Bottom Y coordinate of the region in mm"), - format: z - .enum(["png", "svg"]) - .optional() - .describe("Output image format (default: png)"), - width: z - .number() - .optional() - .describe("Output image width in pixels (default: 800)"), - height: z - .number() - .optional() - .describe("Output image height in pixels (default: 600)"), + format: z.enum(["png", "svg"]).optional().describe("Output image format (default: png)"), + width: z.number().optional().describe("Output image width in pixels (default: 800)"), + height: z.number().optional().describe("Output image height in pixels (default: 600)"), }, async (args: { schematicPath: string; - x1: number; - y1: number; - x2: number; - y2: number; - format?: string; - width?: number; - height?: number; + x1: number; y1: number; x2: number; y2: number; + format?: string; width?: number; height?: number; }) => { const result = await callKicadScript("get_schematic_view_region", args); if (result.success && result.imageData) { @@ -1291,40 +1227,27 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp return { content: [{ type: "text", text: result.imageData }] }; } return { - content: [ - { - type: "image", - data: result.imageData, - mimeType: "image/png", - }, - ], + content: [{ + type: "image", + data: result.imageData, + mimeType: "image/png", + }], }; } return { - content: [ - { - type: "text", - text: `Failed: ${result.message || "Unknown error"}`, - }, - ], + content: [{ type: "text", text: `Failed: ${result.message || "Unknown error"}` }], }; }, ); + // Find overlapping elements server.tool( "find_overlapping_elements", "Detect spatially overlapping symbols, wires, and labels in the schematic. Finds duplicate power symbols at the same position, collinear overlapping wires, and labels stacked on top of each other.", { - schematicPath: z - .string() - .describe("Path to the .kicad_sch schematic file"), - tolerance: z - .number() - .optional() - .describe( - "Distance threshold in mm for label proximity and wire collinearity checks. Symbol overlap uses bounding-box intersection. (default: 0.5)", - ), + schematicPath: z.string().describe("Path to the .kicad_sch schematic file"), + tolerance: z.number().optional().describe("Distance threshold in mm for label proximity and wire collinearity checks. Symbol overlap uses bounding-box intersection. (default: 0.5)"), }, async (args: { schematicPath: string; tolerance?: number }) => { const result = await callKicadScript("find_overlapping_elements", args); @@ -1336,36 +1259,25 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp if (syms.length) { lines.push(`\nOverlapping symbols (${syms.length}):`); syms.slice(0, 20).forEach((o: any) => { - lines.push( - ` ${o.element1.reference} ↔ ${o.element2.reference} (${o.distance}mm) [${o.type}]`, - ); + lines.push(` ${o.element1.reference} ↔ ${o.element2.reference} (${o.distance}mm) [${o.type}]`); }); } if (lbls.length) { lines.push(`\nOverlapping labels (${lbls.length}):`); lbls.slice(0, 20).forEach((o: any) => { - lines.push( - ` "${o.element1.name}" ↔ "${o.element2.name}" (${o.distance}mm)`, - ); + lines.push(` "${o.element1.name}" ↔ "${o.element2.name}" (${o.distance}mm)`); }); } if (wires.length) { lines.push(`\nOverlapping wires (${wires.length}):`); wires.slice(0, 20).forEach((o: any) => { - lines.push( - ` wire @ (${o.wire1.start.x},${o.wire1.start.y})→(${o.wire1.end.x},${o.wire1.end.y}) overlaps with another`, - ); + lines.push(` wire @ (${o.wire1.start.x},${o.wire1.start.y})→(${o.wire1.end.x},${o.wire1.end.y}) overlaps with another`); }); } return { content: [{ type: "text", text: lines.join("\n") }] }; } return { - content: [ - { - type: "text", - text: `Failed: ${result.message || "Unknown error"}`, - }, - ], + content: [{ type: "text", text: `Failed: ${result.message || "Unknown error"}` }], }; }, ); @@ -1375,9 +1287,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp "get_elements_in_region", "List all symbols, wires, and labels within a rectangular region of the schematic. Useful for understanding what is in a specific area before modifying it.", { - schematicPath: z - .string() - .describe("Path to the .kicad_sch schematic file"), + schematicPath: z.string().describe("Path to the .kicad_sch schematic file"), x1: z.number().describe("Left X coordinate of the region in mm"), y1: z.number().describe("Top Y coordinate of the region in mm"), x2: z.number().describe("Right X coordinate of the region in mm"), @@ -1385,56 +1295,39 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp }, async (args: { schematicPath: string; - x1: number; - y1: number; - x2: number; - y2: number; + x1: number; y1: number; x2: number; y2: number; }) => { const result = await callKicadScript("get_elements_in_region", args); if (result.success) { const c = result.counts; - const lines = [ - `Region (${args.x1},${args.y1})→(${args.x2},${args.y2}): ${c.symbols} symbols, ${c.wires} wires, ${c.labels} labels`, - ]; + const lines = [`Region (${args.x1},${args.y1})→(${args.x2},${args.y2}): ${c.symbols} symbols, ${c.wires} wires, ${c.labels} labels`]; const syms: any[] = result.symbols || []; if (syms.length) { lines.push("\nSymbols:"); syms.forEach((s: any) => { const pinCount = s.pins ? Object.keys(s.pins).length : 0; - lines.push( - ` ${s.reference} (${s.libId}) @ (${s.position.x}, ${s.position.y}) [${pinCount} pins]`, - ); + lines.push(` ${s.reference} (${s.libId}) @ (${s.position.x}, ${s.position.y}) [${pinCount} pins]`); }); } const wires: any[] = result.wires || []; if (wires.length) { lines.push(`\nWires (${wires.length}):`); wires.slice(0, 30).forEach((w: any) => { - lines.push( - ` (${w.start.x},${w.start.y}) → (${w.end.x},${w.end.y})`, - ); + lines.push(` (${w.start.x},${w.start.y}) → (${w.end.x},${w.end.y})`); }); - if (wires.length > 30) - lines.push(` ... and ${wires.length - 30} more`); + if (wires.length > 30) lines.push(` ... and ${wires.length - 30} more`); } const labels: any[] = result.labels || []; if (labels.length) { lines.push(`\nLabels (${labels.length}):`); labels.forEach((l: any) => { - lines.push( - ` "${l.name}" [${l.type}] @ (${l.position.x}, ${l.position.y})`, - ); + lines.push(` "${l.name}" [${l.type}] @ (${l.position.x}, ${l.position.y})`); }); } return { content: [{ type: "text", text: lines.join("\n") }] }; } return { - content: [ - { - type: "text", - text: `Failed: ${result.message || "Unknown error"}`, - }, - ], + content: [{ type: "text", text: `Failed: ${result.message || "Unknown error"}` }], }; }, ); @@ -1444,9 +1337,7 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp "find_wires_crossing_symbols", "Find all wires that cross over component symbol bodies. Wires passing over symbols are unacceptable in schematics — they indicate routing mistakes where a wire was drawn across a component instead of around it.", { - schematicPath: z - .string() - .describe("Path to the .kicad_sch schematic file"), + schematicPath: z.string().describe("Path to the .kicad_sch schematic file"), }, async (args: { schematicPath: string }) => { const result = await callKicadScript("find_wires_crossing_symbols", args); @@ -1455,20 +1346,14 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp const lines = [`Found ${collisions.length} wire(s) crossing symbols:`]; collisions.slice(0, 30).forEach((c: any, i: number) => { lines.push( - ` ${i + 1}. Wire (${c.wire.start.x},${c.wire.start.y})→(${c.wire.end.x},${c.wire.end.y}) crosses ${c.component.reference} (${c.component.libId})`, + ` ${i + 1}. Wire (${c.wire.start.x},${c.wire.start.y})→(${c.wire.end.x},${c.wire.end.y}) crosses ${c.component.reference} (${c.component.libId})` ); }); - if (collisions.length > 30) - lines.push(` ... and ${collisions.length - 30} more`); + if (collisions.length > 30) lines.push(` ... and ${collisions.length - 30} more`); return { content: [{ type: "text", text: lines.join("\n") }] }; } return { - content: [ - { - type: "text", - text: `Failed: ${result.message || "Unknown error"}`, - }, - ], + content: [{ type: "text", text: `Failed: ${result.message || "Unknown error"}` }], }; }, );