Add Prettier as a dev dependency with .prettierrc.json config and .prettierignore. Hook added via mirrors-prettier in pre-commit config. All TypeScript, JSON, Markdown, and YAML files auto-formatted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
180 lines
5.7 KiB
TypeScript
180 lines
5.7 KiB
TypeScript
/**
|
|
* Library tools for KiCAD MCP server
|
|
* Provides access to KiCAD footprint libraries and symbols
|
|
*/
|
|
|
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
import { z } from "zod";
|
|
|
|
export function registerLibraryTools(server: McpServer, callKicadScript: Function) {
|
|
// List available footprint libraries
|
|
server.tool(
|
|
"list_libraries",
|
|
"List all available KiCAD footprint libraries",
|
|
{
|
|
search_paths: z
|
|
.array(z.string())
|
|
.optional()
|
|
.describe("Optional additional search paths for libraries"),
|
|
},
|
|
async (args: { search_paths?: string[] }) => {
|
|
const result = await callKicadScript("list_libraries", args);
|
|
if (result.success && result.libraries) {
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Found ${result.libraries.length} footprint libraries:\n${result.libraries.join("\n")}`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Failed to list libraries: ${result.message || "Unknown error"}`,
|
|
},
|
|
],
|
|
};
|
|
},
|
|
);
|
|
|
|
// Search for footprints across all libraries
|
|
server.tool(
|
|
"search_footprints",
|
|
"Search for footprints matching a pattern across all libraries",
|
|
{
|
|
search_term: z.string().describe("Search term or pattern to match footprint names"),
|
|
library: z.string().optional().describe("Optional specific library to search in"),
|
|
limit: z.number().optional().default(50).describe("Maximum number of results to return"),
|
|
},
|
|
async (args: { search_term: string; library?: string; limit?: number }) => {
|
|
const result = await callKicadScript("search_footprints", {
|
|
pattern: args.search_term,
|
|
library: args.library,
|
|
limit: args.limit,
|
|
});
|
|
if (result.success && result.footprints) {
|
|
const footprintList = result.footprints
|
|
.map(
|
|
(fp: any) =>
|
|
`${fp.full_name || fp.library + ":" + fp.footprint}${fp.description ? " - " + fp.description : ""}`,
|
|
)
|
|
.join("\n");
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Found ${result.footprints.length} matching footprints:\n${footprintList}`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Failed to search footprints: ${result.message || "Unknown error"}`,
|
|
},
|
|
],
|
|
};
|
|
},
|
|
);
|
|
|
|
// List footprints in a specific library
|
|
server.tool(
|
|
"list_library_footprints",
|
|
"List all footprints in a specific KiCAD library",
|
|
{
|
|
library_name: z.string().describe("Name of the library to list footprints from"),
|
|
filter: z.string().optional().describe("Optional filter pattern for footprint names"),
|
|
limit: z.number().optional().default(100).describe("Maximum number of footprints to list"),
|
|
},
|
|
async (args: { library_name: string; filter?: string; limit?: number }) => {
|
|
const result = await callKicadScript("list_library_footprints", args);
|
|
if (result.success && result.footprints) {
|
|
const footprintList = result.footprints.map((fp: string) => ` - ${fp}`).join("\n");
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Library ${args.library_name} contains ${result.footprints.length} footprints:\n${footprintList}`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Failed to list footprints in library ${args.library_name}: ${result.message || "Unknown error"}`,
|
|
},
|
|
],
|
|
};
|
|
},
|
|
);
|
|
|
|
// Get detailed information about a specific footprint
|
|
server.tool(
|
|
"get_footprint_info",
|
|
"Get detailed information about a specific footprint",
|
|
{
|
|
library_name: z.string().describe("Name of the library containing the footprint"),
|
|
footprint_name: z.string().describe("Name of the footprint to get information about"),
|
|
},
|
|
async (args: { library_name: string; footprint_name: string }) => {
|
|
const result = await callKicadScript("get_footprint_info", args);
|
|
if (result.success && result.info) {
|
|
const info = result.info;
|
|
|
|
// pads is a list of {number, type, shape} objects
|
|
const padsArray: Array<{ number: string; type: string; shape: string }> = Array.isArray(
|
|
info.pads,
|
|
)
|
|
? info.pads
|
|
: [];
|
|
const padsSummary = padsArray.length
|
|
? `${padsArray.length} pads: ${padsArray.map((p) => p.number).join(", ")}`
|
|
: "";
|
|
const padsDetail = padsArray.length
|
|
? padsArray.map((p) => ` pad ${p.number}: ${p.type} ${p.shape}`).join("\n")
|
|
: "";
|
|
|
|
const details = [
|
|
`Footprint: ${info.name}`,
|
|
`Library: ${info.library}`,
|
|
info.description ? `Description: ${info.description}` : "",
|
|
info.keywords ? `Keywords: ${info.keywords}` : "",
|
|
padsSummary,
|
|
padsDetail,
|
|
info.layers ? `Layers used: ${info.layers.join(", ")}` : "",
|
|
info.courtyard
|
|
? `Courtyard size: ${info.courtyard.width}mm x ${info.courtyard.height}mm`
|
|
: "",
|
|
info.attributes ? `Attributes: ${JSON.stringify(info.attributes)}` : "",
|
|
]
|
|
.filter((line) => line)
|
|
.join("\n");
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: details,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Failed to get footprint info: ${result.message || "Unknown error"}`,
|
|
},
|
|
],
|
|
};
|
|
},
|
|
);
|
|
}
|