feat: Add local symbol library search and 3rd party library support (#25)
Adds comprehensive local KiCad symbol library search functionality and fixes KICAD9_3RD_PARTY environment variable resolution. Features: - Symbol library search by name, LCSC ID, description, manufacturer, MPN - Support for 3rd party libraries installed via Plugin and Content Manager - New MCP tools: search_symbols, list_symbol_libraries, get_symbol_info - Enhanced library path resolution for KiCad 8 and 9 This enables users with locally installed JLCPCB libraries to search and use components directly. Co-authored-by: l3wi <l3wi@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import { registerDesignRuleTools } from './tools/design-rules.js';
|
||||
import { registerExportTools } from './tools/export.js';
|
||||
import { registerSchematicTools } from './tools/schematic.js';
|
||||
import { registerLibraryTools } from './tools/library.js';
|
||||
import { registerSymbolLibraryTools } from './tools/library-symbol.js';
|
||||
import { registerUITools } from './tools/ui.js';
|
||||
import { registerRouterTools } from './tools/router.js';
|
||||
|
||||
@@ -153,6 +154,7 @@ export class KiCADMcpServer {
|
||||
registerExportTools(this.server, this.callKicadScript.bind(this));
|
||||
registerSchematicTools(this.server, this.callKicadScript.bind(this));
|
||||
registerLibraryTools(this.server, this.callKicadScript.bind(this));
|
||||
registerSymbolLibraryTools(this.server, this.callKicadScript.bind(this));
|
||||
registerUITools(this.server, this.callKicadScript.bind(this));
|
||||
|
||||
// Register all resources
|
||||
|
||||
178
src/tools/library-symbol.ts
Normal file
178
src/tools/library-symbol.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Symbol Library tools for KiCAD MCP server
|
||||
* Provides search/browse access to local KiCad symbol libraries
|
||||
*/
|
||||
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function registerSymbolLibraryTools(server: McpServer, callKicadScript: Function) {
|
||||
// List available symbol libraries
|
||||
server.tool(
|
||||
"list_symbol_libraries",
|
||||
"List all available KiCAD symbol libraries from global sym-lib-table",
|
||||
{},
|
||||
async () => {
|
||||
const result = await callKicadScript("list_symbol_libraries", {});
|
||||
if (result.success && result.libraries) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Found ${result.count} symbol libraries:\n${result.libraries.join('\n')}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Failed to list symbol libraries: ${result.message || 'Unknown error'}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Search for symbols across all libraries
|
||||
server.tool(
|
||||
"search_symbols",
|
||||
`Search for symbols in local KiCAD symbol libraries.
|
||||
|
||||
Searches by: symbol name, LCSC ID, description, manufacturer, MPN, category.
|
||||
Use this to find components already in your local libraries (e.g., JLCPCB-KiCad-Library).
|
||||
|
||||
Returns symbol references that can be used directly in schematics.`,
|
||||
{
|
||||
query: z.string()
|
||||
.describe("Search query (e.g., 'ESP32', 'STM32F103', 'C8734' for LCSC ID)"),
|
||||
library: z.string().optional()
|
||||
.describe("Optional: filter to specific library name pattern (e.g., 'JLCPCB')"),
|
||||
limit: z.number().optional().default(20)
|
||||
.describe("Maximum number of results to return")
|
||||
},
|
||||
async (args: { query: string; library?: string; limit?: number }) => {
|
||||
const result = await callKicadScript("search_symbols", args);
|
||||
if (result.success && result.symbols) {
|
||||
if (result.symbols.length === 0) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `No symbols found matching "${args.query}"${args.library ? ` in libraries matching "${args.library}"` : ''}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
const symbolList = result.symbols.map((s: any) => {
|
||||
const parts = [`${s.full_ref}`];
|
||||
if (s.lcsc_id) parts.push(`LCSC: ${s.lcsc_id}`);
|
||||
if (s.description) parts.push(s.description);
|
||||
else if (s.value) parts.push(s.value);
|
||||
return parts.join(' | ');
|
||||
}).join('\n');
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Found ${result.count} symbols matching "${args.query}":\n\n${symbolList}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Failed to search symbols: ${result.message || 'Unknown error'}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// List symbols in a specific library
|
||||
server.tool(
|
||||
"list_library_symbols",
|
||||
"List all symbols in a specific KiCAD symbol library",
|
||||
{
|
||||
library: z.string()
|
||||
.describe("Library name (e.g., 'Device', 'PCM_JLCPCB-MCUs')")
|
||||
},
|
||||
async (args: { library: string }) => {
|
||||
const result = await callKicadScript("list_library_symbols", args);
|
||||
if (result.success && result.symbols) {
|
||||
const symbolList = result.symbols.map((s: any) => {
|
||||
const parts = [` - ${s.name}`];
|
||||
if (s.lcsc_id) parts.push(`(LCSC: ${s.lcsc_id})`);
|
||||
return parts.join(' ');
|
||||
}).join('\n');
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Library "${args.library}" contains ${result.count} symbols:\n${symbolList}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Failed to list symbols in library ${args.library}: ${result.message || 'Unknown error'}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Get detailed information about a specific symbol
|
||||
server.tool(
|
||||
"get_symbol_info",
|
||||
"Get detailed information about a specific symbol",
|
||||
{
|
||||
symbol: z.string()
|
||||
.describe("Symbol specification (e.g., 'Device:R' or 'PCM_JLCPCB-MCUs:STM32F103C8T6')")
|
||||
},
|
||||
async (args: { symbol: string }) => {
|
||||
const result = await callKicadScript("get_symbol_info", args);
|
||||
if (result.success && result.symbol_info) {
|
||||
const info = result.symbol_info;
|
||||
const details = [
|
||||
`Symbol: ${info.full_ref}`,
|
||||
info.value ? `Value: ${info.value}` : '',
|
||||
info.description ? `Description: ${info.description}` : '',
|
||||
info.lcsc_id ? `LCSC: ${info.lcsc_id}` : '',
|
||||
info.manufacturer ? `Manufacturer: ${info.manufacturer}` : '',
|
||||
info.mpn ? `MPN: ${info.mpn}` : '',
|
||||
info.footprint ? `Footprint: ${info.footprint}` : '',
|
||||
info.category ? `Category: ${info.category}` : '',
|
||||
info.lib_class ? `Class: ${info.lib_class}` : '',
|
||||
info.datasheet ? `Datasheet: ${info.datasheet}` : '',
|
||||
].filter(line => line).join('\n');
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: details
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Failed to get symbol info: ${result.message || 'Unknown error'}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -28,9 +28,10 @@ export function registerSchematicTools(server: McpServer, callKicadScript: Funct
|
||||
// Add component to schematic
|
||||
server.tool(
|
||||
"add_schematic_component",
|
||||
"Add a component to the schematic",
|
||||
"Add a component to the schematic. Symbol format is 'Library:SymbolName' (e.g., 'Device:R', 'EDA-MCP:ESP32-C3')",
|
||||
{
|
||||
symbol: z.string().describe("Symbol library reference"),
|
||||
schematicPath: z.string().describe("Path to the schematic file"),
|
||||
symbol: z.string().describe("Symbol library:name reference (e.g., Device:R, EDA-MCP:ESP32-C3)"),
|
||||
reference: z.string().describe("Component reference (e.g., R1, U1)"),
|
||||
value: z.string().optional().describe("Component value"),
|
||||
position: z.object({
|
||||
@@ -38,14 +39,41 @@ export function registerSchematicTools(server: McpServer, callKicadScript: Funct
|
||||
y: z.number()
|
||||
}).optional().describe("Position on schematic"),
|
||||
},
|
||||
async (args: any) => {
|
||||
const result = await callKicadScript("add_schematic_component", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
async (args: { schematicPath: string; symbol: string; reference: string; value?: string; position?: { x: number; y: number } }) => {
|
||||
// Transform to what Python backend expects
|
||||
const [library, symbolName] = args.symbol.includes(':')
|
||||
? args.symbol.split(':')
|
||||
: ['Device', args.symbol];
|
||||
|
||||
const transformed = {
|
||||
schematicPath: args.schematicPath,
|
||||
component: {
|
||||
library,
|
||||
type: symbolName,
|
||||
reference: args.reference,
|
||||
value: args.value,
|
||||
// Python expects flat x, y not nested position
|
||||
x: args.position?.x ?? 0,
|
||||
y: args.position?.y ?? 0
|
||||
}
|
||||
};
|
||||
|
||||
const result = await callKicadScript("add_schematic_component", transformed);
|
||||
if (result.success) {
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully added ${args.reference} (${args.symbol}) to schematic`
|
||||
}]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to add component: ${result.message || JSON.stringify(result)}`
|
||||
}]
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user