style: apply Prettier formatting to TS/JS/JSON/MD files

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>
This commit is contained in:
Eugene Mikhantyev
2026-03-29 13:05:50 +01:00
parent c44bd9205d
commit 7d50fa1d4c
82 changed files with 18314 additions and 17317 deletions

View File

@@ -1,354 +1,372 @@
/**
* Board resources for KiCAD MCP server
*
* These resources provide information about the PCB board
* to the LLM, enabling better context-aware assistance.
*/
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { logger } from '../logger.js';
import { createJsonResponse, createBinaryResponse } from '../utils/resource-helpers.js';
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
/**
* Register board resources with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerBoardResources(server: McpServer, callKicadScript: CommandFunction): void {
logger.info('Registering board resources');
// ------------------------------------------------------
// Board Information Resource
// ------------------------------------------------------
server.resource(
"board_info",
"kicad://board/info",
async (uri) => {
logger.debug('Retrieving board information');
const result = await callKicadScript("get_board_info", {});
if (!result.success) {
logger.error(`Failed to retrieve board information: ${result.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve board information",
details: result.errorDetails
}),
mimeType: "application/json"
}]
};
}
logger.debug('Successfully retrieved board information');
return {
contents: [{
uri: uri.href,
text: JSON.stringify(result),
mimeType: "application/json"
}]
};
}
);
// ------------------------------------------------------
// Layer List Resource
// ------------------------------------------------------
server.resource(
"layer_list",
"kicad://board/layers",
async (uri) => {
logger.debug('Retrieving layer list');
const result = await callKicadScript("get_layer_list", {});
if (!result.success) {
logger.error(`Failed to retrieve layer list: ${result.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve layer list",
details: result.errorDetails
}),
mimeType: "application/json"
}]
};
}
logger.debug(`Successfully retrieved ${result.layers?.length || 0} layers`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify(result),
mimeType: "application/json"
}]
};
}
);
// ------------------------------------------------------
// Board Extents Resource
// ------------------------------------------------------
server.resource(
"board_extents",
new ResourceTemplate("kicad://board/extents/{unit?}", {
list: async () => ({
resources: [
{ uri: "kicad://board/extents/mm", name: "Millimeters" },
{ uri: "kicad://board/extents/inch", name: "Inches" }
]
})
}),
async (uri, params) => {
const unit = params.unit || 'mm';
logger.debug(`Retrieving board extents in ${unit}`);
const result = await callKicadScript("get_board_extents", { unit });
if (!result.success) {
logger.error(`Failed to retrieve board extents: ${result.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve board extents",
details: result.errorDetails
}),
mimeType: "application/json"
}]
};
}
logger.debug('Successfully retrieved board extents');
return {
contents: [{
uri: uri.href,
text: JSON.stringify(result),
mimeType: "application/json"
}]
};
}
);
// ------------------------------------------------------
// Board 2D View Resource
// ------------------------------------------------------
server.resource(
"board_2d_view",
new ResourceTemplate("kicad://board/2d-view/{format?}", {
list: async () => ({
resources: [
{ uri: "kicad://board/2d-view/png", name: "PNG Format" },
{ uri: "kicad://board/2d-view/jpg", name: "JPEG Format" },
{ uri: "kicad://board/2d-view/svg", name: "SVG Format" }
]
})
}),
async (uri, params) => {
const format = (params.format || 'png') as 'png' | 'jpg' | 'svg';
const width = params.width ? parseInt(params.width as string) : undefined;
const height = params.height ? parseInt(params.height as string) : undefined;
// Handle layers parameter - could be string or array
const layers = typeof params.layers === 'string' ? params.layers.split(',') : params.layers;
logger.debug('Retrieving 2D board view');
const result = await callKicadScript("get_board_2d_view", {
layers,
width,
height,
format
});
if (!result.success) {
logger.error(`Failed to retrieve 2D board view: ${result.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve 2D board view",
details: result.errorDetails
}),
mimeType: "application/json"
}]
};
}
logger.debug('Successfully retrieved 2D board view');
if (format === 'svg') {
return {
contents: [{
uri: uri.href,
text: result.imageData,
mimeType: "image/svg+xml"
}]
};
} else {
return {
contents: [{
uri: uri.href,
blob: result.imageData,
mimeType: format === "jpg" ? "image/jpeg" : "image/png"
}]
};
}
}
);
// ------------------------------------------------------
// Board 3D View Resource
// ------------------------------------------------------
server.resource(
"board_3d_view",
new ResourceTemplate("kicad://board/3d-view/{angle?}", {
list: async () => ({
resources: [
{ uri: "kicad://board/3d-view/isometric", name: "Isometric View" },
{ uri: "kicad://board/3d-view/top", name: "Top View" },
{ uri: "kicad://board/3d-view/bottom", name: "Bottom View" }
]
})
}),
async (uri, params) => {
const angle = params.angle || 'isometric';
const width = params.width ? parseInt(params.width as string) : undefined;
const height = params.height ? parseInt(params.height as string) : undefined;
logger.debug(`Retrieving 3D board view from ${angle} angle`);
const result = await callKicadScript("get_board_3d_view", {
width,
height,
angle
});
if (!result.success) {
logger.error(`Failed to retrieve 3D board view: ${result.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve 3D board view",
details: result.errorDetails
}),
mimeType: "application/json"
}]
};
}
logger.debug('Successfully retrieved 3D board view');
return {
contents: [{
uri: uri.href,
blob: result.imageData,
mimeType: "image/png"
}]
};
}
);
// ------------------------------------------------------
// Board Statistics Resource
// ------------------------------------------------------
server.resource(
"board_statistics",
"kicad://board/statistics",
async (uri) => {
logger.debug('Generating board statistics');
// Get board info
const boardResult = await callKicadScript("get_board_info", {});
if (!boardResult.success) {
logger.error(`Failed to retrieve board information: ${boardResult.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to generate board statistics",
details: boardResult.errorDetails
}),
mimeType: "application/json"
}]
};
}
// Get component list
const componentsResult = await callKicadScript("get_component_list", {});
if (!componentsResult.success) {
logger.error(`Failed to retrieve component list: ${componentsResult.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to generate board statistics",
details: componentsResult.errorDetails
}),
mimeType: "application/json"
}]
};
}
// Get nets list
const netsResult = await callKicadScript("get_nets_list", {});
if (!netsResult.success) {
logger.error(`Failed to retrieve nets list: ${netsResult.errorDetails}`);
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
error: "Failed to generate board statistics",
details: netsResult.errorDetails
}),
mimeType: "application/json"
}]
};
}
// Combine all information into statistics
const statistics = {
board: {
size: boardResult.size,
layers: boardResult.layers?.length || 0,
title: boardResult.title
},
components: {
count: componentsResult.components?.length || 0,
types: countComponentTypes(componentsResult.components || [])
},
nets: {
count: netsResult.nets?.length || 0
}
};
logger.debug('Successfully generated board statistics');
return {
contents: [{
uri: uri.href,
text: JSON.stringify(statistics),
mimeType: "application/json"
}]
};
}
);
logger.info('Board resources registered');
}
/**
* Helper function to count component types
*/
function countComponentTypes(components: any[]): Record<string, number> {
const typeCounts: Record<string, number> = {};
for (const component of components) {
const type = component.value?.split(' ')[0] || 'Unknown';
typeCounts[type] = (typeCounts[type] || 0) + 1;
}
return typeCounts;
}
/**
* Board resources for KiCAD MCP server
*
* These resources provide information about the PCB board
* to the LLM, enabling better context-aware assistance.
*/
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { logger } from "../logger.js";
import { createJsonResponse, createBinaryResponse } from "../utils/resource-helpers.js";
// Command function type for KiCAD script calls
type CommandFunction = (command: string, params: Record<string, unknown>) => Promise<any>;
/**
* Register board resources with the MCP server
*
* @param server MCP server instance
* @param callKicadScript Function to call KiCAD script commands
*/
export function registerBoardResources(server: McpServer, callKicadScript: CommandFunction): void {
logger.info("Registering board resources");
// ------------------------------------------------------
// Board Information Resource
// ------------------------------------------------------
server.resource("board_info", "kicad://board/info", async (uri) => {
logger.debug("Retrieving board information");
const result = await callKicadScript("get_board_info", {});
if (!result.success) {
logger.error(`Failed to retrieve board information: ${result.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve board information",
details: result.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
logger.debug("Successfully retrieved board information");
return {
contents: [
{
uri: uri.href,
text: JSON.stringify(result),
mimeType: "application/json",
},
],
};
});
// ------------------------------------------------------
// Layer List Resource
// ------------------------------------------------------
server.resource("layer_list", "kicad://board/layers", async (uri) => {
logger.debug("Retrieving layer list");
const result = await callKicadScript("get_layer_list", {});
if (!result.success) {
logger.error(`Failed to retrieve layer list: ${result.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve layer list",
details: result.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
logger.debug(`Successfully retrieved ${result.layers?.length || 0} layers`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify(result),
mimeType: "application/json",
},
],
};
});
// ------------------------------------------------------
// Board Extents Resource
// ------------------------------------------------------
server.resource(
"board_extents",
new ResourceTemplate("kicad://board/extents/{unit?}", {
list: async () => ({
resources: [
{ uri: "kicad://board/extents/mm", name: "Millimeters" },
{ uri: "kicad://board/extents/inch", name: "Inches" },
],
}),
}),
async (uri, params) => {
const unit = params.unit || "mm";
logger.debug(`Retrieving board extents in ${unit}`);
const result = await callKicadScript("get_board_extents", { unit });
if (!result.success) {
logger.error(`Failed to retrieve board extents: ${result.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve board extents",
details: result.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
logger.debug("Successfully retrieved board extents");
return {
contents: [
{
uri: uri.href,
text: JSON.stringify(result),
mimeType: "application/json",
},
],
};
},
);
// ------------------------------------------------------
// Board 2D View Resource
// ------------------------------------------------------
server.resource(
"board_2d_view",
new ResourceTemplate("kicad://board/2d-view/{format?}", {
list: async () => ({
resources: [
{ uri: "kicad://board/2d-view/png", name: "PNG Format" },
{ uri: "kicad://board/2d-view/jpg", name: "JPEG Format" },
{ uri: "kicad://board/2d-view/svg", name: "SVG Format" },
],
}),
}),
async (uri, params) => {
const format = (params.format || "png") as "png" | "jpg" | "svg";
const width = params.width ? parseInt(params.width as string) : undefined;
const height = params.height ? parseInt(params.height as string) : undefined;
// Handle layers parameter - could be string or array
const layers = typeof params.layers === "string" ? params.layers.split(",") : params.layers;
logger.debug("Retrieving 2D board view");
const result = await callKicadScript("get_board_2d_view", {
layers,
width,
height,
format,
});
if (!result.success) {
logger.error(`Failed to retrieve 2D board view: ${result.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve 2D board view",
details: result.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
logger.debug("Successfully retrieved 2D board view");
if (format === "svg") {
return {
contents: [
{
uri: uri.href,
text: result.imageData,
mimeType: "image/svg+xml",
},
],
};
} else {
return {
contents: [
{
uri: uri.href,
blob: result.imageData,
mimeType: format === "jpg" ? "image/jpeg" : "image/png",
},
],
};
}
},
);
// ------------------------------------------------------
// Board 3D View Resource
// ------------------------------------------------------
server.resource(
"board_3d_view",
new ResourceTemplate("kicad://board/3d-view/{angle?}", {
list: async () => ({
resources: [
{ uri: "kicad://board/3d-view/isometric", name: "Isometric View" },
{ uri: "kicad://board/3d-view/top", name: "Top View" },
{ uri: "kicad://board/3d-view/bottom", name: "Bottom View" },
],
}),
}),
async (uri, params) => {
const angle = params.angle || "isometric";
const width = params.width ? parseInt(params.width as string) : undefined;
const height = params.height ? parseInt(params.height as string) : undefined;
logger.debug(`Retrieving 3D board view from ${angle} angle`);
const result = await callKicadScript("get_board_3d_view", {
width,
height,
angle,
});
if (!result.success) {
logger.error(`Failed to retrieve 3D board view: ${result.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to retrieve 3D board view",
details: result.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
logger.debug("Successfully retrieved 3D board view");
return {
contents: [
{
uri: uri.href,
blob: result.imageData,
mimeType: "image/png",
},
],
};
},
);
// ------------------------------------------------------
// Board Statistics Resource
// ------------------------------------------------------
server.resource("board_statistics", "kicad://board/statistics", async (uri) => {
logger.debug("Generating board statistics");
// Get board info
const boardResult = await callKicadScript("get_board_info", {});
if (!boardResult.success) {
logger.error(`Failed to retrieve board information: ${boardResult.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to generate board statistics",
details: boardResult.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
// Get component list
const componentsResult = await callKicadScript("get_component_list", {});
if (!componentsResult.success) {
logger.error(`Failed to retrieve component list: ${componentsResult.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to generate board statistics",
details: componentsResult.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
// Get nets list
const netsResult = await callKicadScript("get_nets_list", {});
if (!netsResult.success) {
logger.error(`Failed to retrieve nets list: ${netsResult.errorDetails}`);
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
error: "Failed to generate board statistics",
details: netsResult.errorDetails,
}),
mimeType: "application/json",
},
],
};
}
// Combine all information into statistics
const statistics = {
board: {
size: boardResult.size,
layers: boardResult.layers?.length || 0,
title: boardResult.title,
},
components: {
count: componentsResult.components?.length || 0,
types: countComponentTypes(componentsResult.components || []),
},
nets: {
count: netsResult.nets?.length || 0,
},
};
logger.debug("Successfully generated board statistics");
return {
contents: [
{
uri: uri.href,
text: JSON.stringify(statistics),
mimeType: "application/json",
},
],
};
});
logger.info("Board resources registered");
}
/**
* Helper function to count component types
*/
function countComponentTypes(components: any[]): Record<string, number> {
const typeCounts: Record<string, number> = {};
for (const component of components) {
const type = component.value?.split(" ")[0] || "Unknown";
typeCounts[type] = (typeCounts[type] || 0) + 1;
}
return typeCounts;
}