feat: add no_connect handler, server icon, split READMEs by language, VS Code Copilot guide, tool inventory update
This commit is contained in:
@@ -214,9 +214,7 @@ export class KiCADMcpServer {
|
||||
logger.info("Registering KiCAD tools, resources, and prompts...");
|
||||
|
||||
// Register router tools FIRST (for tool discovery and execution)
|
||||
// NOTE: Router disabled — causes Claude to hallucinate tool schemas via search_tools/execute_tool.
|
||||
// All tools are registered directly below and are immediately visible to Claude.
|
||||
// registerRouterTools(this.server, this.callKicadScript.bind(this));
|
||||
registerRouterTools(this.server, this.callKicadScript.bind(this));
|
||||
|
||||
// Register all tools
|
||||
registerProjectTools(this.server, this.callKicadScript.bind(this));
|
||||
@@ -248,7 +246,6 @@ export class KiCADMcpServer {
|
||||
registerFootprintPrompts(this.server);
|
||||
|
||||
logger.info("All KiCAD tools, resources, and prompts registered");
|
||||
logger.info("Router pattern enabled: 4 router tools + direct tools for discovery");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"set_board_size",
|
||||
"Set the PCB board dimensions (width and height) in the specified unit.",
|
||||
{
|
||||
width: z.number().describe("Board width"),
|
||||
height: z.number().describe("Board height"),
|
||||
@@ -54,6 +55,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_layer",
|
||||
"Add a new copper or technical layer to the PCB stackup.",
|
||||
{
|
||||
name: z.string().describe("Layer name"),
|
||||
type: z.enum(["copper", "technical", "user", "signal"]).describe("Layer type"),
|
||||
@@ -85,6 +87,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"set_active_layer",
|
||||
"Set the currently active PCB layer by name (e.g. F.Cu, B.Cu).",
|
||||
{
|
||||
layer: z.string().describe("Layer name to set as active"),
|
||||
},
|
||||
@@ -106,42 +109,53 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
// Get Board Info Tool
|
||||
// ------------------------------------------------------
|
||||
server.tool("get_board_info", {}, async () => {
|
||||
logger.debug("Getting board information");
|
||||
const result = await callKicadScript("get_board_info", {});
|
||||
server.tool(
|
||||
"get_board_info",
|
||||
"Retrieve general information about the current PCB board (dimensions, layer count, DRC status).",
|
||||
{},
|
||||
async () => {
|
||||
logger.debug("Getting board information");
|
||||
const result = await callKicadScript("get_board_info", {});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(result),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
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", {});
|
||||
server.tool(
|
||||
"get_layer_list",
|
||||
"Return the list of all layers defined in the current PCB board.",
|
||||
{},
|
||||
async () => {
|
||||
logger.debug("Getting layer list");
|
||||
const result = await callKicadScript("get_layer_list", {});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(result),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(result),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Add Board Outline Tool
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_board_outline",
|
||||
"Draw the PCB board outline (Edge.Cuts layer) as a rectangle, rounded rectangle, circle or polygon.",
|
||||
{
|
||||
shape: z
|
||||
.enum(["rectangle", "circle", "polygon", "rounded_rectangle"])
|
||||
@@ -196,6 +210,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_mounting_hole",
|
||||
"Place a mounting hole (NPTH or PTH) at the specified position on the PCB.",
|
||||
{
|
||||
position: z
|
||||
.object({
|
||||
@@ -231,6 +246,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_board_text",
|
||||
"Add a text label to a PCB layer (e.g. silkscreen, fab, courtyard).",
|
||||
{
|
||||
text: z.string().describe("Text content"),
|
||||
position: z
|
||||
@@ -274,6 +290,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_zone",
|
||||
"Create a copper fill zone (pour) on a PCB layer for a specified net.",
|
||||
{
|
||||
layer: z.string().describe("Layer for the zone"),
|
||||
net: z.string().describe("Net name for the zone"),
|
||||
@@ -321,6 +338,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_board_extents",
|
||||
"Return the bounding box (min/max X and Y) of all objects on the current PCB board.",
|
||||
{
|
||||
unit: z.enum(["mm", "inch"]).optional().describe("Unit of measurement for the result"),
|
||||
},
|
||||
@@ -344,6 +362,7 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_board_2d_view",
|
||||
"Render a 2D image of the current PCB board and return it as PNG, JPG or SVG.",
|
||||
{
|
||||
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"),
|
||||
|
||||
@@ -23,6 +23,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"place_component",
|
||||
"Place a footprint component onto the PCB at the specified position. Optionally set reference, value, footprint, rotation and layer.",
|
||||
{
|
||||
componentId: z
|
||||
.string()
|
||||
@@ -77,6 +78,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"move_component",
|
||||
"Move a PCB component to a new position. Optionally update rotation or flip to a different copper layer.",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
|
||||
position: z
|
||||
@@ -119,6 +121,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"rotate_component",
|
||||
"Rotate a PCB component to an absolute angle in degrees.",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
|
||||
angle: z.number().describe("Rotation angle in degrees (absolute, not relative)"),
|
||||
@@ -146,6 +149,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"delete_component",
|
||||
"Remove a component from the PCB by its reference designator.",
|
||||
{
|
||||
reference: z
|
||||
.string()
|
||||
@@ -171,6 +175,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"edit_component",
|
||||
"Edit properties of an existing PCB component (reference, value, footprint).",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
|
||||
newReference: z.string().optional().describe("Optional new reference designator"),
|
||||
@@ -202,6 +207,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"find_component",
|
||||
"Search for a PCB component by reference designator or value and return its position and properties.",
|
||||
{
|
||||
reference: z.string().optional().describe("Reference designator to search for"),
|
||||
value: z.string().optional().describe("Component value to search for"),
|
||||
@@ -231,6 +237,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_component_properties",
|
||||
"Return all properties of a PCB component (position, rotation, layer, value, footprint).",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
|
||||
},
|
||||
@@ -256,6 +263,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_component_annotation",
|
||||
"Add a text annotation or comment to a PCB component.",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component (e.g., 'R5')"),
|
||||
annotation: z.string().describe("Annotation or comment text to add"),
|
||||
@@ -288,6 +296,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"group_components",
|
||||
"Group multiple PCB components together by name for easier selection and manipulation.",
|
||||
{
|
||||
references: z.array(z.string()).describe("Reference designators of components to group"),
|
||||
groupName: z.string().describe("Name for the component group"),
|
||||
@@ -315,6 +324,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"replace_component",
|
||||
"Replace an existing PCB component with a different component type, optionally changing footprint and value.",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component to replace"),
|
||||
newComponentId: z.string().describe("ID of the new component to use"),
|
||||
@@ -346,6 +356,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_component_pads",
|
||||
"Return all pads of a PCB component with their positions, net assignments and sizes.",
|
||||
{
|
||||
reference: z.string().describe("Reference designator of the component (e.g., 'U1')"),
|
||||
unit: z.enum(["mm", "inch"]).optional().describe("Unit for coordinates (default: mm)"),
|
||||
@@ -373,6 +384,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_component_list",
|
||||
"Return a list of all components on the PCB, optionally filtered by layer or bounding box region.",
|
||||
{
|
||||
layer: z.string().optional().describe("Filter by layer (e.g., 'F.Cu', 'B.Cu')"),
|
||||
boundingBox: z
|
||||
@@ -411,6 +423,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_pad_position",
|
||||
"Return the exact XY position of a specific pad on a PCB component. Use this before routing to get accurate start/end coordinates.",
|
||||
{
|
||||
reference: z.string().describe("Component reference designator (e.g., 'U1')"),
|
||||
pad: z.string().describe("Pad number or name (e.g., '1', 'A1')"),
|
||||
@@ -440,6 +453,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"place_component_array",
|
||||
"Place a rectangular grid array of identical components on the PCB with configurable row/column spacing.",
|
||||
{
|
||||
componentId: z.string().describe("Component identifier"),
|
||||
startPosition: z
|
||||
@@ -500,6 +514,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"align_components",
|
||||
"Align multiple PCB components horizontally, vertically or on a grid with optional spacing.",
|
||||
{
|
||||
references: z.array(z.string()).describe("Array of component references to align"),
|
||||
alignmentType: z.enum(["horizontal", "vertical", "grid"]).describe("Type of alignment"),
|
||||
@@ -531,6 +546,7 @@ export function registerComponentTools(server: McpServer, callKicadScript: Comma
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"duplicate_component",
|
||||
"Duplicate an existing PCB component at an offset position, optionally with a new reference designator.",
|
||||
{
|
||||
reference: z.string().describe("Reference of component to duplicate"),
|
||||
offset: z
|
||||
|
||||
@@ -25,6 +25,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"set_design_rules",
|
||||
"Configure PCB design rules: clearance, track width, via dimensions and courtyard requirements.",
|
||||
{
|
||||
clearance: z.number().optional().describe("Minimum clearance between copper items (mm)"),
|
||||
trackWidth: z.number().optional().describe("Default track width (mm)"),
|
||||
@@ -65,25 +66,31 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
// Get Design Rules Tool
|
||||
// ------------------------------------------------------
|
||||
server.tool("get_design_rules", {}, async () => {
|
||||
logger.debug("Getting design rules");
|
||||
const result = await callKicadScript("get_design_rules", {});
|
||||
server.tool(
|
||||
"get_design_rules",
|
||||
"Return the current PCB design rules (clearance, track width, via sizes, courtyard settings).",
|
||||
{},
|
||||
async () => {
|
||||
logger.debug("Getting design rules");
|
||||
const result = await callKicadScript("get_design_rules", {});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(result),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(result),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Run DRC Tool
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"run_drc",
|
||||
"Run the KiCAD Design Rule Check (DRC) on the current PCB and return violations. Optionally save the report to a file.",
|
||||
{
|
||||
reportPath: z.string().optional().describe("Optional path to save the DRC report"),
|
||||
},
|
||||
@@ -107,6 +114,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"add_net_class",
|
||||
"Create a named net class with specific design rules (clearance, track width, via size) and assign nets to it.",
|
||||
{
|
||||
name: z.string().describe("Name of the net class"),
|
||||
description: z.string().optional().describe("Optional description of the net class"),
|
||||
@@ -170,6 +178,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"assign_net_to_class",
|
||||
"Assign a net to an existing net class to apply its specific design rules.",
|
||||
{
|
||||
net: z.string().describe("Name of the net"),
|
||||
netClass: z.string().describe("Name of the net class"),
|
||||
@@ -197,6 +206,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"set_layer_constraints",
|
||||
"Set per-layer design rule constraints (minimum track width, clearance and via dimensions).",
|
||||
{
|
||||
layer: z.string().describe("Layer name (e.g., 'F.Cu')"),
|
||||
minTrackWidth: z.number().optional().describe("Minimum track width for this layer (mm)"),
|
||||
@@ -230,6 +240,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"check_clearance",
|
||||
"Check the actual clearance between two PCB items (track, via, pad, zone or component) and report whether it meets the design rules.",
|
||||
{
|
||||
item1: z
|
||||
.object({
|
||||
@@ -289,6 +300,7 @@ export function registerDesignRuleTools(server: McpServer, callKicadScript: Comm
|
||||
// ------------------------------------------------------
|
||||
server.tool(
|
||||
"get_drc_violations",
|
||||
"Return the list of current DRC violations on the PCB, optionally filtered by severity (error, warning).",
|
||||
{
|
||||
severity: z
|
||||
.enum(["error", "warning", "all"])
|
||||
|
||||
@@ -25,6 +25,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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
|
||||
@@ -73,6 +74,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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
|
||||
@@ -112,6 +114,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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
|
||||
@@ -146,6 +149,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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"),
|
||||
@@ -188,6 +192,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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"),
|
||||
@@ -255,6 +260,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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)"),
|
||||
@@ -289,6 +295,7 @@ export function registerExportTools(server: McpServer, callKicadScript: CommandF
|
||||
// ------------------------------------------------------
|
||||
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"),
|
||||
|
||||
@@ -96,6 +96,7 @@ export const toolCategories: ToolCategory[] = [
|
||||
"delete_schematic_wire",
|
||||
"add_schematic_net_label",
|
||||
"delete_schematic_net_label",
|
||||
"add_no_connect",
|
||||
"connect_to_net",
|
||||
"connect_passthrough",
|
||||
"get_net_connections",
|
||||
|
||||
@@ -10,7 +10,6 @@ import { logger } from "../logger.js";
|
||||
import {
|
||||
getAllCategories,
|
||||
getCategory,
|
||||
getToolCategory,
|
||||
searchTools as registrySearchTools,
|
||||
getRegistryStats,
|
||||
} from "./registry.js";
|
||||
@@ -18,26 +17,10 @@ import {
|
||||
// Command function type for KiCAD script calls
|
||||
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
|
||||
|
||||
// Map to store tool execution handlers
|
||||
// This will be populated by registerToolHandler()
|
||||
const toolHandlers = new Map<string, (params: any) => Promise<any>>();
|
||||
|
||||
/**
|
||||
* Register a tool handler for execution via execute_tool
|
||||
* This should be called by each tool registration function
|
||||
*/
|
||||
export function registerToolHandler(
|
||||
toolName: string,
|
||||
handler: (params: any) => Promise<any>,
|
||||
): void {
|
||||
toolHandlers.set(toolName, handler);
|
||||
logger.debug(`Registered handler for routed tool: ${toolName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all router tools with the MCP server
|
||||
*/
|
||||
export function registerRouterTools(server: McpServer, callKicadScript: CommandFunction): void {
|
||||
export function registerRouterTools(server: McpServer, _callKicadScript: CommandFunction): void {
|
||||
logger.info("Registering router tools");
|
||||
|
||||
// ============================================================================
|
||||
@@ -45,6 +28,7 @@ export function registerRouterTools(server: McpServer, callKicadScript: CommandF
|
||||
// ============================================================================
|
||||
server.tool(
|
||||
"list_tool_categories",
|
||||
"List all available KiCAD tool categories with their descriptions and tool counts. Use this to discover which tools are available via the router.",
|
||||
{
|
||||
// No parameters
|
||||
},
|
||||
@@ -82,6 +66,7 @@ export function registerRouterTools(server: McpServer, callKicadScript: CommandF
|
||||
// ============================================================================
|
||||
server.tool(
|
||||
"get_category_tools",
|
||||
"Return all tools available in a specific category. Use list_tool_categories first to find valid category names.",
|
||||
{
|
||||
category: z.string().describe("Category name from list_tool_categories"),
|
||||
},
|
||||
@@ -133,136 +118,12 @@ export function registerRouterTools(server: McpServer, callKicadScript: CommandF
|
||||
},
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// execute_tool
|
||||
// ============================================================================
|
||||
server.tool(
|
||||
"execute_tool",
|
||||
{
|
||||
tool_name: z.string().describe("Tool name from get_category_tools"),
|
||||
params: z.record(z.unknown()).optional().describe("Tool parameters (optional)"),
|
||||
},
|
||||
async ({ tool_name, params }) => {
|
||||
logger.info(`Executing routed tool: ${tool_name}`);
|
||||
|
||||
// Check if tool exists in registry
|
||||
const category = getToolCategory(tool_name);
|
||||
|
||||
if (!category) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
error: `Unknown tool: ${tool_name}`,
|
||||
hint: "Use list_tool_categories and get_category_tools to find available tools",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Get the handler
|
||||
const handler = toolHandlers.get(tool_name);
|
||||
|
||||
if (!handler) {
|
||||
// Tool is in registry but handler not registered yet
|
||||
// This means the tool exists but hasn't been migrated to router pattern yet
|
||||
// Fall back to calling KiCAD script directly
|
||||
logger.warn(
|
||||
`Tool ${tool_name} in registry but no handler registered, falling back to direct call`,
|
||||
);
|
||||
|
||||
try {
|
||||
const result = await callKicadScript(tool_name, params || {});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
tool: tool_name,
|
||||
category: category,
|
||||
result: result,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
error: `Tool execution failed: ${(error as Error).message}`,
|
||||
tool: tool_name,
|
||||
category: category,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the tool via its handler
|
||||
try {
|
||||
const result = await handler(params || {});
|
||||
|
||||
// The handler already returns MCP-formatted response
|
||||
// Just add metadata
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
tool: tool_name,
|
||||
category: category,
|
||||
...result,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
error: `Tool execution failed: ${(error as Error).message}`,
|
||||
tool: tool_name,
|
||||
category: category,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// search_tools
|
||||
// ============================================================================
|
||||
server.tool(
|
||||
"search_tools",
|
||||
"Search all available KiCAD tools by keyword. Returns matching tool names and their categories.",
|
||||
{
|
||||
query: z.string().describe("Search term (e.g., 'gerber', 'zone', 'export', 'drc')"),
|
||||
},
|
||||
|
||||
@@ -578,6 +578,53 @@ edit_schematic_component and set its value to an empty string.`,
|
||||
},
|
||||
);
|
||||
|
||||
// Add no-connect flag
|
||||
server.tool(
|
||||
"add_no_connect",
|
||||
"Add a no-connect flag (X marker) to a pin that is intentionally left unconnected. " +
|
||||
"This suppresses ERC 'Pin not connected' errors for unused pins. " +
|
||||
"PREFERRED: supply componentRef + pinNumber to snap to the exact pin endpoint. " +
|
||||
"Alternatively supply position [x, y] in mm matching the pin endpoint exactly.",
|
||||
{
|
||||
schematicPath: z.string().describe("Path to the schematic file"),
|
||||
position: z
|
||||
.array(z.number())
|
||||
.length(2)
|
||||
.optional()
|
||||
.describe("Position [x, y] in mm. Required when componentRef/pinNumber are not given."),
|
||||
componentRef: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Component reference to snap to (e.g. U1, R1). Use with pinNumber."),
|
||||
pinNumber: z
|
||||
.union([z.string(), z.number()])
|
||||
.optional()
|
||||
.describe("Pin number or name on componentRef (e.g. '1', 'GND'). Use with componentRef."),
|
||||
},
|
||||
async (args: {
|
||||
schematicPath: string;
|
||||
position?: number[];
|
||||
componentRef?: string;
|
||||
pinNumber?: string | number;
|
||||
}) => {
|
||||
const result = await callKicadScript("add_no_connect", args);
|
||||
if (result.success) {
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Failed to add no-connect: ${result.message || "Unknown error"}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Connect pin to net
|
||||
server.tool(
|
||||
"connect_to_net",
|
||||
|
||||
Reference in New Issue
Block a user