Update repository with project files and documentation
- Added comprehensive documentation (BUILD_AND_TEST, CLIENT_CONFIG, KNOWN_ISSUES, ROADMAP, etc.) - Updated core functionality for board outline, size, and utilities - Added new tools for project, routing, schematic, and UI management - Included TypeScript SDK with full MCP implementation - Updated configuration examples for all platforms - Added changelog and status tracking - Improved Python utilities with KiCAD process management - Enhanced resource helpers and server capabilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -107,11 +107,11 @@ export function registerLibraryResources(server: McpServer, callKicadScript: Com
|
||||
);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Component Details Resource
|
||||
// Library Component Details Resource
|
||||
// ------------------------------------------------------
|
||||
server.resource(
|
||||
"component_details",
|
||||
new ResourceTemplate("kicad://component/{componentId}/{library?}", {
|
||||
"library_component_details",
|
||||
new ResourceTemplate("kicad://library/component/{componentId}/{library?}", {
|
||||
list: undefined
|
||||
}),
|
||||
async (uri, params) => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
||||
import express from 'express';
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { logger } from './logger.js';
|
||||
|
||||
// Import tool registration functions
|
||||
@@ -16,6 +17,7 @@ import { registerComponentTools } from './tools/component.js';
|
||||
import { registerRoutingTools } from './tools/routing.js';
|
||||
import { registerDesignRuleTools } from './tools/design-rules.js';
|
||||
import { registerExportTools } from './tools/export.js';
|
||||
import { registerUITools } from './tools/ui.js';
|
||||
|
||||
// Import resource registration functions
|
||||
import { registerProjectResources } from './resources/project.js';
|
||||
@@ -28,6 +30,46 @@ import { registerComponentPrompts } from './prompts/component.js';
|
||||
import { registerRoutingPrompts } from './prompts/routing.js';
|
||||
import { registerDesignPrompts } from './prompts/design.js';
|
||||
|
||||
/**
|
||||
* Find the Python executable to use
|
||||
* Prioritizes virtual environment if available, falls back to system Python
|
||||
*/
|
||||
function findPythonExecutable(scriptPath: string): string {
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
// Get the project root (parent of the python/ directory)
|
||||
const projectRoot = dirname(dirname(scriptPath));
|
||||
|
||||
// Check for virtual environment
|
||||
const venvPaths = [
|
||||
join(projectRoot, 'venv', isWindows ? 'Scripts' : 'bin', isWindows ? 'python.exe' : 'python'),
|
||||
join(projectRoot, '.venv', isWindows ? 'Scripts' : 'bin', isWindows ? 'python.exe' : 'python'),
|
||||
];
|
||||
|
||||
for (const venvPath of venvPaths) {
|
||||
if (existsSync(venvPath)) {
|
||||
logger.info(`Found virtual environment Python at: ${venvPath}`);
|
||||
return venvPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to system Python or environment-specified Python
|
||||
if (isWindows && process.env.KICAD_PYTHON) {
|
||||
// Allow override via KICAD_PYTHON environment variable
|
||||
return process.env.KICAD_PYTHON;
|
||||
} else if (isWindows && process.env.PYTHONPATH?.includes('KiCad')) {
|
||||
// Windows: Try KiCAD's bundled Python
|
||||
const kicadPython = 'C:\\Program Files\\KiCad\\9.0\\bin\\python.exe';
|
||||
if (existsSync(kicadPython)) {
|
||||
return kicadPython;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to system Python
|
||||
logger.info('Using system Python (no venv found)');
|
||||
return isWindows ? 'python.exe' : 'python3';
|
||||
}
|
||||
|
||||
/**
|
||||
* KiCAD MCP Server class
|
||||
*/
|
||||
@@ -85,6 +127,7 @@ export class KiCADMcpServer {
|
||||
registerRoutingTools(this.server, this.callKicadScript.bind(this));
|
||||
registerDesignRuleTools(this.server, this.callKicadScript.bind(this));
|
||||
registerExportTools(this.server, this.callKicadScript.bind(this));
|
||||
registerUITools(this.server, this.callKicadScript.bind(this));
|
||||
|
||||
// Register all resources
|
||||
registerProjectResources(this.server, this.callKicadScript.bind(this));
|
||||
@@ -109,9 +152,8 @@ export class KiCADMcpServer {
|
||||
|
||||
// Start the Python process for KiCAD scripting
|
||||
logger.info(`Starting Python process with script: ${this.kicadScriptPath}`);
|
||||
const pythonExe = process.env.PYTHONPATH ?
|
||||
'C:\\Program Files\\KiCad\\9.0\\bin\\python.exe' : 'python';
|
||||
|
||||
const pythonExe = findPythonExecutable(this.kicadScriptPath);
|
||||
|
||||
logger.info(`Using Python executable: ${pythonExe}`);
|
||||
this.pythonProcess = spawn(pythonExe, [this.kicadScriptPath], {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
|
||||
@@ -167,11 +167,15 @@ export function registerBoardTools(server: McpServer, callKicadScript: CommandFu
|
||||
},
|
||||
async ({ shape, params }) => {
|
||||
logger.debug(`Adding ${shape} board outline`);
|
||||
// Flatten params and rename x/y to centerX/centerY for Python compatibility
|
||||
const { x, y, ...otherParams } = params;
|
||||
const result = await callKicadScript("add_board_outline", {
|
||||
shape,
|
||||
params
|
||||
centerX: x,
|
||||
centerY: y,
|
||||
...otherParams
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
|
||||
79
src/tools/project.ts
Normal file
79
src/tools/project.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Project management tools for KiCAD MCP server
|
||||
*/
|
||||
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function registerProjectTools(server: McpServer, callKicadScript: Function) {
|
||||
// Create project tool
|
||||
server.tool(
|
||||
"create_project",
|
||||
"Create a new KiCAD project",
|
||||
{
|
||||
path: z.string().describe("Project directory path"),
|
||||
name: z.string().describe("Project name"),
|
||||
},
|
||||
async (args: { path: string; name: string }) => {
|
||||
const result = await callKicadScript("create_project", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Open project tool
|
||||
server.tool(
|
||||
"open_project",
|
||||
"Open an existing KiCAD project",
|
||||
{
|
||||
filename: z.string().describe("Path to .kicad_pro or .kicad_pcb file"),
|
||||
},
|
||||
async (args: { filename: string }) => {
|
||||
const result = await callKicadScript("open_project", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Save project tool
|
||||
server.tool(
|
||||
"save_project",
|
||||
"Save the current KiCAD project",
|
||||
{
|
||||
path: z.string().optional().describe("Optional new path to save to"),
|
||||
},
|
||||
async (args: { path?: string }) => {
|
||||
const result = await callKicadScript("save_project", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Get project info tool
|
||||
server.tool(
|
||||
"get_project_info",
|
||||
"Get information about the current KiCAD project",
|
||||
{},
|
||||
async () => {
|
||||
const result = await callKicadScript("get_project_info", {});
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
101
src/tools/routing.ts
Normal file
101
src/tools/routing.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Routing tools for KiCAD MCP server
|
||||
*/
|
||||
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function registerRoutingTools(server: McpServer, callKicadScript: Function) {
|
||||
// Add net tool
|
||||
server.tool(
|
||||
"add_net",
|
||||
"Create a new net on the PCB",
|
||||
{
|
||||
name: z.string().describe("Net name"),
|
||||
netClass: z.string().optional().describe("Net class name"),
|
||||
},
|
||||
async (args: { name: string; netClass?: string }) => {
|
||||
const result = await callKicadScript("add_net", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Route trace tool
|
||||
server.tool(
|
||||
"route_trace",
|
||||
"Route a trace between two points",
|
||||
{
|
||||
start: z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
unit: z.string().optional()
|
||||
}).describe("Start position"),
|
||||
end: z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
unit: z.string().optional()
|
||||
}).describe("End position"),
|
||||
layer: z.string().describe("PCB layer"),
|
||||
width: z.number().describe("Trace width in mm"),
|
||||
net: z.string().describe("Net name"),
|
||||
},
|
||||
async (args: any) => {
|
||||
const result = await callKicadScript("route_trace", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Add via tool
|
||||
server.tool(
|
||||
"add_via",
|
||||
"Add a via to the PCB",
|
||||
{
|
||||
position: z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
unit: z.string().optional()
|
||||
}).describe("Via position"),
|
||||
net: z.string().describe("Net name"),
|
||||
viaType: z.string().optional().describe("Via type (through, blind, buried)"),
|
||||
},
|
||||
async (args: any) => {
|
||||
const result = await callKicadScript("add_via", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Add copper pour tool
|
||||
server.tool(
|
||||
"add_copper_pour",
|
||||
"Add a copper pour (ground/power plane) to the PCB",
|
||||
{
|
||||
layer: z.string().describe("PCB layer"),
|
||||
net: z.string().describe("Net name"),
|
||||
clearance: z.number().optional().describe("Clearance in mm"),
|
||||
},
|
||||
async (args: any) => {
|
||||
const result = await callKicadScript("add_copper_pour", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
76
src/tools/schematic.ts
Normal file
76
src/tools/schematic.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Schematic tools for KiCAD MCP server
|
||||
*/
|
||||
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function registerSchematicTools(server: McpServer, callKicadScript: Function) {
|
||||
// Create schematic tool
|
||||
server.tool(
|
||||
"create_schematic",
|
||||
"Create a new schematic",
|
||||
{
|
||||
name: z.string().describe("Schematic name"),
|
||||
path: z.string().optional().describe("Optional path"),
|
||||
},
|
||||
async (args: { name: string; path?: string }) => {
|
||||
const result = await callKicadScript("create_schematic", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Add component to schematic
|
||||
server.tool(
|
||||
"add_schematic_component",
|
||||
"Add a component to the schematic",
|
||||
{
|
||||
symbol: z.string().describe("Symbol library reference"),
|
||||
reference: z.string().describe("Component reference (e.g., R1, U1)"),
|
||||
value: z.string().optional().describe("Component value"),
|
||||
position: z.object({
|
||||
x: z.number(),
|
||||
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)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Connect components with wire
|
||||
server.tool(
|
||||
"add_wire",
|
||||
"Add a wire connection in the schematic",
|
||||
{
|
||||
start: z.object({
|
||||
x: z.number(),
|
||||
y: z.number()
|
||||
}).describe("Start position"),
|
||||
end: z.object({
|
||||
x: z.number(),
|
||||
y: z.number()
|
||||
}).describe("End position"),
|
||||
},
|
||||
async (args: any) => {
|
||||
const result = await callKicadScript("add_wire", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
48
src/tools/ui.ts
Normal file
48
src/tools/ui.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* UI/Process management tools for KiCAD MCP server
|
||||
*/
|
||||
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { z } from 'zod';
|
||||
import { logger } from '../logger.js';
|
||||
|
||||
export function registerUITools(server: McpServer, callKicadScript: Function) {
|
||||
// Check if KiCAD UI is running
|
||||
server.tool(
|
||||
"check_kicad_ui",
|
||||
"Check if KiCAD UI is currently running",
|
||||
{},
|
||||
async () => {
|
||||
logger.info('Checking KiCAD UI status');
|
||||
const result = await callKicadScript("check_kicad_ui", {});
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Launch KiCAD UI
|
||||
server.tool(
|
||||
"launch_kicad_ui",
|
||||
"Launch KiCAD UI, optionally with a project file",
|
||||
{
|
||||
projectPath: z.string().optional().describe("Optional path to .kicad_pcb file to open"),
|
||||
autoLaunch: z.boolean().optional().describe("Whether to launch KiCAD if not running (default: true)")
|
||||
},
|
||||
async (args: { projectPath?: string; autoLaunch?: boolean }) => {
|
||||
logger.info(`Launching KiCAD UI${args.projectPath ? ' with project: ' + args.projectPath : ''}`);
|
||||
const result = await callKicadScript("launch_kicad_ui", args);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
logger.info('UI management tools registered');
|
||||
}
|
||||
61
src/utils/resource-helpers.ts
Normal file
61
src/utils/resource-helpers.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Resource helper utilities for MCP resources
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a JSON response for MCP resources
|
||||
*
|
||||
* @param data Data to serialize as JSON
|
||||
* @param uri Optional URI for the resource
|
||||
* @returns MCP resource response object
|
||||
*/
|
||||
export function createJsonResponse(data: any, uri?: string) {
|
||||
return {
|
||||
contents: [{
|
||||
uri: uri || "data:application/json",
|
||||
mimeType: "application/json",
|
||||
text: JSON.stringify(data, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a binary response for MCP resources
|
||||
*
|
||||
* @param data Binary data (Buffer or base64 string)
|
||||
* @param mimeType MIME type of the binary data
|
||||
* @param uri Optional URI for the resource
|
||||
* @returns MCP resource response object
|
||||
*/
|
||||
export function createBinaryResponse(data: Buffer | string, mimeType: string, uri?: string) {
|
||||
const blob = typeof data === 'string' ? data : data.toString('base64');
|
||||
|
||||
return {
|
||||
contents: [{
|
||||
uri: uri || `data:${mimeType}`,
|
||||
mimeType: mimeType,
|
||||
blob: blob
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error response for MCP resources
|
||||
*
|
||||
* @param error Error message
|
||||
* @param details Optional error details
|
||||
* @param uri Optional URI for the resource
|
||||
* @returns MCP resource error response
|
||||
*/
|
||||
export function createErrorResponse(error: string, details?: string, uri?: string) {
|
||||
return {
|
||||
contents: [{
|
||||
uri: uri || "data:application/json",
|
||||
mimeType: "application/json",
|
||||
text: JSON.stringify({
|
||||
error,
|
||||
details
|
||||
}, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user