@jlcpcb/mcp 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
2
  var __create = Object.create;
4
3
  var __getProtoOf = Object.getPrototypeOf;
5
4
  var __defProp = Object.defineProperty;
@@ -26,7 +25,6 @@ var __export = (target, all) => {
26
25
  set: (newValue) => all[name] = () => newValue
27
26
  });
28
27
  };
29
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
28
 
31
29
  // ../../node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js
32
30
  var require_code = __commonJS((exports) => {
@@ -11947,6 +11945,9 @@ var require_dist = __commonJS((exports, module) => {
11947
11945
  exports.default = formatsPlugin;
11948
11946
  });
11949
11947
 
11948
+ // src/index.ts
11949
+ import { createRequire } from "module";
11950
+
11950
11951
  // ../../node_modules/zod/v3/external.js
11951
11952
  var exports_external = {};
11952
11953
  __export(exports_external, {
@@ -21890,19 +21891,26 @@ class StdioServerTransport {
21890
21891
  }
21891
21892
  // ../core/dist/index.js
21892
21893
  import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
21893
- import { existsSync } from "fs";
21894
- import { join as join2 } from "path";
21895
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
21896
21894
  import { existsSync as existsSync2 } from "fs";
21897
21895
  import { join as join3 } from "path";
21898
- import { homedir, platform } from "os";
21896
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
21897
+ import { existsSync as existsSync3 } from "fs";
21898
+ import { join as join4 } from "path";
21899
+ import { homedir as homedir2, platform as platform2 } from "os";
21899
21900
  import { readdir, readFile, writeFile, mkdir, access, stat } from "fs/promises";
21900
21901
  import { join, dirname, extname, basename } from "path";
21902
+ import { existsSync } from "fs";
21903
+ import { homedir, platform } from "os";
21904
+ import { join as join2 } from "path";
21901
21905
  import { execSync } from "child_process";
21902
- import { existsSync as existsSync3 } from "fs";
21903
- import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
21904
- import { homedir as homedir2, platform as platform2 } from "os";
21905
- import { join as join4 } from "path";
21906
+ import { existsSync as existsSync4 } from "fs";
21907
+ import { readFile as readFile4, readdir as readdir2, unlink } from "fs/promises";
21908
+ import { homedir as homedir3, platform as platform3 } from "os";
21909
+ import { join as join5 } from "path";
21910
+ import { createServer } from "http";
21911
+ import { readFileSync } from "fs";
21912
+ import { join as join6, dirname as dirname3 } from "path";
21913
+ import { fileURLToPath } from "url";
21906
21914
  var __defProp2 = Object.defineProperty;
21907
21915
  var __export2 = (target, all) => {
21908
21916
  for (var name in all)
@@ -21930,9 +21938,16 @@ function containsKeyword(text, keyword) {
21930
21938
  return false;
21931
21939
  }
21932
21940
  function getLibraryCategory(prefix, category, description) {
21933
- const normalizedPrefix = prefix.toUpperCase();
21934
- if (PREFIX_CATEGORY_MAP[normalizedPrefix]) {
21935
- return PREFIX_CATEGORY_MAP[normalizedPrefix];
21941
+ const cleanedPrefix = prefix.replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
21942
+ if (cleanedPrefix.length >= 2) {
21943
+ const twoCharPrefix = cleanedPrefix.slice(0, 2);
21944
+ if (PREFIX_CATEGORY_MAP[twoCharPrefix]) {
21945
+ return PREFIX_CATEGORY_MAP[twoCharPrefix];
21946
+ }
21947
+ }
21948
+ const singleCharPrefix = cleanedPrefix.slice(0, 1);
21949
+ if (PREFIX_CATEGORY_MAP[singleCharPrefix]) {
21950
+ return PREFIX_CATEGORY_MAP[singleCharPrefix];
21936
21951
  }
21937
21952
  const searchText = [category || "", description || ""].join(" ");
21938
21953
  if (searchText.trim()) {
@@ -21996,6 +22011,7 @@ var init_category_router = __esm(() => {
21996
22011
  X: "Crystals",
21997
22012
  J: "Connectors",
21998
22013
  P: "Connectors",
22014
+ RJ: "Connectors",
21999
22015
  K: "Misc",
22000
22016
  F: "Misc"
22001
22017
  };
@@ -22241,6 +22257,48 @@ var init_category_router = __esm(() => {
22241
22257
  "ceramic resonator"
22242
22258
  ]
22243
22259
  },
22260
+ {
22261
+ category: "Connectors",
22262
+ keywords: [
22263
+ "connector",
22264
+ "header",
22265
+ "socket",
22266
+ "terminal",
22267
+ "terminal block",
22268
+ "jack",
22269
+ "plug",
22270
+ "receptacle",
22271
+ "usb-c",
22272
+ "usb type-c",
22273
+ "micro usb",
22274
+ "mini usb",
22275
+ "usb-a",
22276
+ "usb-b",
22277
+ "hdmi connector",
22278
+ "rj45",
22279
+ "rj11",
22280
+ "barrel jack",
22281
+ "dc jack",
22282
+ "audio jack",
22283
+ "jst",
22284
+ "jst-xh",
22285
+ "jst-ph",
22286
+ "molex",
22287
+ "dupont",
22288
+ "pin header",
22289
+ "female header",
22290
+ "fpc",
22291
+ "ffc",
22292
+ "sim card",
22293
+ "sd card",
22294
+ "microsd",
22295
+ "pogo pin",
22296
+ "spring contact",
22297
+ "test point",
22298
+ "ethernet connector",
22299
+ "modular connector"
22300
+ ]
22301
+ },
22244
22302
  {
22245
22303
  category: "Transistors",
22246
22304
  keywords: [
@@ -22341,46 +22399,6 @@ var init_category_router = __esm(() => {
22341
22399
  "chip resistor"
22342
22400
  ]
22343
22401
  },
22344
- {
22345
- category: "Connectors",
22346
- keywords: [
22347
- "connector",
22348
- "header",
22349
- "socket",
22350
- "terminal",
22351
- "terminal block",
22352
- "jack",
22353
- "plug",
22354
- "receptacle",
22355
- "usb-c",
22356
- "usb type-c",
22357
- "micro usb",
22358
- "mini usb",
22359
- "usb-a",
22360
- "usb-b",
22361
- "hdmi connector",
22362
- "rj45",
22363
- "rj11",
22364
- "barrel jack",
22365
- "dc jack",
22366
- "audio jack",
22367
- "jst",
22368
- "jst-xh",
22369
- "jst-ph",
22370
- "molex",
22371
- "dupont",
22372
- "pin header",
22373
- "female header",
22374
- "fpc",
22375
- "ffc",
22376
- "sim card",
22377
- "sd card",
22378
- "microsd",
22379
- "pogo pin",
22380
- "spring contact",
22381
- "test point"
22382
- ]
22383
- },
22384
22402
  {
22385
22403
  category: "ICs",
22386
22404
  keywords: [
@@ -22474,9 +22492,9 @@ function addLibraryToTable(tableContent, libraryName, libraryPath, type, descrip
22474
22492
  `;
22475
22493
  }
22476
22494
  async function ensureSymLibTable(projectDir, symbolLibPath, libraryName = DEFAULT_LIBRARY_NAME, description = DEFAULT_LIBRARY_DESCRIPTION) {
22477
- const tablePath = join2(projectDir, "sym-lib-table");
22495
+ const tablePath = join3(projectDir, "sym-lib-table");
22478
22496
  const relativePath = symbolLibPath.startsWith(projectDir) ? "${KIPRJMOD}" + symbolLibPath.slice(projectDir.length) : symbolLibPath;
22479
- if (!existsSync(tablePath)) {
22497
+ if (!existsSync2(tablePath)) {
22480
22498
  const content = generateSymLibTable(relativePath, libraryName, description);
22481
22499
  await writeFile2(tablePath, content, "utf-8");
22482
22500
  return { created: true, modified: false, path: tablePath };
@@ -22490,9 +22508,9 @@ async function ensureSymLibTable(projectDir, symbolLibPath, libraryName = DEFAUL
22490
22508
  return { created: false, modified: true, path: tablePath };
22491
22509
  }
22492
22510
  async function ensureFpLibTable(projectDir, footprintLibPath, libraryName = DEFAULT_LIBRARY_NAME, description = DEFAULT_LIBRARY_DESCRIPTION) {
22493
- const tablePath = join2(projectDir, "fp-lib-table");
22511
+ const tablePath = join3(projectDir, "fp-lib-table");
22494
22512
  const relativePath = footprintLibPath.startsWith(projectDir) ? "${KIPRJMOD}" + footprintLibPath.slice(projectDir.length) : footprintLibPath;
22495
- if (!existsSync(tablePath)) {
22513
+ if (!existsSync2(tablePath)) {
22496
22514
  const content = generateFpLibTable(relativePath, libraryName, description);
22497
22515
  await writeFile2(tablePath, content, "utf-8");
22498
22516
  return { created: true, modified: false, path: tablePath };
@@ -22513,39 +22531,40 @@ var DEFAULT_LIBRARY_DESCRIPTION = "JLC-MCP Component Library (LCSC/EasyEDA)";
22513
22531
  var init_lib_table = () => {};
22514
22532
  var exports_global_lib_table = {};
22515
22533
  __export2(exports_global_lib_table, {
22516
- ensureGlobalLibraryTables: () => ensureGlobalLibraryTables
22534
+ ensureGlobalLibraryTables: () => ensureGlobalLibraryTables,
22535
+ ensureGlobalEasyEDALibrary: () => ensureGlobalEasyEDALibrary
22517
22536
  });
22518
- function detectKicadVersion() {
22519
- const home = homedir();
22520
- const baseDir = join3(home, "Documents", "KiCad");
22537
+ function detectKicadVersion2() {
22538
+ const home = homedir2();
22539
+ const baseDir = join4(home, "Documents", "KiCad");
22521
22540
  for (const version2 of KICAD_VERSIONS2) {
22522
- if (existsSync2(join3(baseDir, version2))) {
22541
+ if (existsSync3(join4(baseDir, version2))) {
22523
22542
  return version2;
22524
22543
  }
22525
22544
  }
22526
22545
  return "9.0";
22527
22546
  }
22528
- function getKicadConfigDir(version2) {
22529
- const home = homedir();
22530
- const plat = platform();
22547
+ function getKicadConfigDir2(version2) {
22548
+ const home = homedir2();
22549
+ const plat = platform2();
22531
22550
  if (plat === "darwin") {
22532
- return join3(home, "Library", "Preferences", "kicad", version2);
22551
+ return join4(home, "Library", "Preferences", "kicad", version2);
22533
22552
  } else if (plat === "win32") {
22534
- return join3(process.env.APPDATA || join3(home, "AppData", "Roaming"), "kicad", version2);
22553
+ return join4(process.env.APPDATA || join4(home, "AppData", "Roaming"), "kicad", version2);
22535
22554
  } else {
22536
- return join3(home, ".config", "kicad", version2);
22555
+ return join4(home, ".config", "kicad", version2);
22537
22556
  }
22538
22557
  }
22539
22558
  function get3rdPartyBaseDir(version2) {
22540
- const home = homedir();
22541
- const plat = platform();
22559
+ const home = homedir2();
22560
+ const plat = platform2();
22542
22561
  if (plat === "linux") {
22543
- return join3(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE);
22562
+ return join4(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE);
22544
22563
  }
22545
- return join3(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE);
22564
+ return join4(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE);
22546
22565
  }
22547
22566
  function getSymbolLibPath(category, version2) {
22548
- return join3(get3rdPartyBaseDir(version2), "symbols", getLibraryFilename(category));
22567
+ return join4(get3rdPartyBaseDir(version2), "symbols", getLibraryFilename(category));
22549
22568
  }
22550
22569
  function getSymbolLibUri(category) {
22551
22570
  return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${getLibraryFilename(category)}`;
@@ -22586,11 +22605,11 @@ function generateFpLibTable2() {
22586
22605
  `;
22587
22606
  }
22588
22607
  async function ensureGlobalSymLibTable(version2) {
22589
- const configDir = getKicadConfigDir(version2);
22590
- const tablePath = join3(configDir, "sym-lib-table");
22608
+ const configDir = getKicadConfigDir2(version2);
22609
+ const tablePath = join4(configDir, "sym-lib-table");
22591
22610
  const categories = getAllCategories();
22592
22611
  await mkdir2(configDir, { recursive: true });
22593
- if (!existsSync2(tablePath)) {
22612
+ if (!existsSync3(tablePath)) {
22594
22613
  const content2 = generateSymLibTable2();
22595
22614
  await writeFile3(tablePath, content2, "utf-8");
22596
22615
  return {
@@ -22630,11 +22649,11 @@ async function ensureGlobalSymLibTable(version2) {
22630
22649
  };
22631
22650
  }
22632
22651
  async function ensureGlobalFpLibTable(version2) {
22633
- const configDir = getKicadConfigDir(version2);
22634
- const tablePath = join3(configDir, "fp-lib-table");
22652
+ const configDir = getKicadConfigDir2(version2);
22653
+ const tablePath = join4(configDir, "fp-lib-table");
22635
22654
  const name = LIBRARY_PREFIX2;
22636
22655
  await mkdir2(configDir, { recursive: true });
22637
- if (!existsSync2(tablePath)) {
22656
+ if (!existsSync3(tablePath)) {
22638
22657
  const content2 = generateFpLibTable2();
22639
22658
  await writeFile3(tablePath, content2, "utf-8");
22640
22659
  return {
@@ -22668,13 +22687,13 @@ async function ensureGlobalFpLibTable(version2) {
22668
22687
  }
22669
22688
  async function ensureLibraryStubs(version2) {
22670
22689
  const baseDir = get3rdPartyBaseDir(version2);
22671
- const symbolsDir = join3(baseDir, "symbols");
22672
- const footprintsDir = join3(baseDir, "footprints", getFootprintDirName());
22673
- const models3dDir = join3(baseDir, "3dmodels", get3DModelsDirName());
22690
+ const symbolsDir = join4(baseDir, "symbols");
22691
+ const footprintsDir = join4(baseDir, "footprints", getFootprintDirName());
22692
+ const models3dDir = join4(baseDir, "3dmodels", get3DModelsDirName());
22674
22693
  const directoriesCreated = [];
22675
22694
  const symbolsCreated = [];
22676
22695
  for (const dir of [symbolsDir, footprintsDir, models3dDir]) {
22677
- if (!existsSync2(dir)) {
22696
+ if (!existsSync3(dir)) {
22678
22697
  await mkdir2(dir, { recursive: true });
22679
22698
  directoriesCreated.push(dir);
22680
22699
  }
@@ -22683,16 +22702,61 @@ async function ensureLibraryStubs(version2) {
22683
22702
  const emptyContent = generateEmptySymbolLibrary();
22684
22703
  for (const category of categories) {
22685
22704
  const filePath = getSymbolLibPath(category, version2);
22686
- if (!existsSync2(filePath)) {
22705
+ if (!existsSync3(filePath)) {
22687
22706
  await writeFile3(filePath, emptyContent, "utf-8");
22688
22707
  symbolsCreated.push(filePath);
22689
22708
  }
22690
22709
  }
22691
22710
  return { symbolsCreated, directoriesCreated };
22692
22711
  }
22712
+ function getEasyEDASymbolLibUri() {
22713
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${EASYEDA_SYMBOL_LIBRARY_NAME}`;
22714
+ }
22715
+ function getEasyEDAFootprintLibUri() {
22716
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${EASYEDA_FOOTPRINT_LIBRARY_NAME}`;
22717
+ }
22718
+ async function ensureGlobalEasyEDALibrary() {
22719
+ const version2 = detectKicadVersion2();
22720
+ const configDir = getKicadConfigDir2(version2);
22721
+ await mkdir2(configDir, { recursive: true });
22722
+ const symTablePath = join4(configDir, "sym-lib-table");
22723
+ if (existsSync3(symTablePath)) {
22724
+ let content = await readFile3(symTablePath, "utf-8");
22725
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
22726
+ const uri = getEasyEDASymbolLibUri();
22727
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "sym", EASYEDA_LIBRARY_DESCRIPTION);
22728
+ await writeFile3(symTablePath, content, "utf-8");
22729
+ }
22730
+ } else {
22731
+ const uri = getEasyEDASymbolLibUri();
22732
+ const content = `(sym_lib_table
22733
+ (version 7)
22734
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
22735
+ )
22736
+ `;
22737
+ await writeFile3(symTablePath, content, "utf-8");
22738
+ }
22739
+ const fpTablePath = join4(configDir, "fp-lib-table");
22740
+ if (existsSync3(fpTablePath)) {
22741
+ let content = await readFile3(fpTablePath, "utf-8");
22742
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
22743
+ const uri = getEasyEDAFootprintLibUri();
22744
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "fp", EASYEDA_LIBRARY_DESCRIPTION);
22745
+ await writeFile3(fpTablePath, content, "utf-8");
22746
+ }
22747
+ } else {
22748
+ const uri = getEasyEDAFootprintLibUri();
22749
+ const content = `(fp_lib_table
22750
+ (version 7)
22751
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
22752
+ )
22753
+ `;
22754
+ await writeFile3(fpTablePath, content, "utf-8");
22755
+ }
22756
+ }
22693
22757
  async function ensureGlobalLibraryTables() {
22694
22758
  const errors22 = [];
22695
- const version2 = detectKicadVersion();
22759
+ const version2 = detectKicadVersion2();
22696
22760
  let symLibTable = {
22697
22761
  path: "",
22698
22762
  created: false,
@@ -22742,6 +22806,10 @@ var LIBRARY_PREFIX2 = "JLC-MCP";
22742
22806
  var LIBRARY_NAMESPACE = "jlc_mcp";
22743
22807
  var KICAD_3RD_PARTY_VAR = "${KICAD9_3RD_PARTY}";
22744
22808
  var LIBRARY_DESCRIPTION = "Autogenerated by JLC-MCP";
22809
+ var EASYEDA_LIBRARY_NAME = "EasyEDA";
22810
+ var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
22811
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
22812
+ var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
22745
22813
  var init_global_lib_table = __esm(() => {
22746
22814
  init_category_router();
22747
22815
  init_lib_table();
@@ -26716,6 +26784,9 @@ var coerce2 = {
26716
26784
  };
26717
26785
  var NEVER3 = INVALID2;
26718
26786
  var LCSCPartNumberSchema = exports_external2.string().regex(/^C\d+$/, "Invalid LCSC part number format (expected C followed by digits)");
26787
+ var EasyEDAUuidSchema = exports_external2.string().regex(/^[a-f0-9]{32}$/i, "Invalid EasyEDA UUID (expected 32-character hex string)").transform((s) => s.toLowerCase());
26788
+ var ComponentIdSchema = exports_external2.union([LCSCPartNumberSchema, EasyEDAUuidSchema]);
26789
+ var SafePathSchema = exports_external2.string().min(1, "Path cannot be empty").refine((p) => !p.includes(".."), "Path traversal (..) not allowed").refine((p) => !p.includes("\x00"), "Null bytes not allowed in path");
26719
26790
  var PackageSchema = exports_external2.string().min(1, "Package cannot be empty");
26720
26791
  var PriceTierSchema = exports_external2.object({
26721
26792
  quantity: exports_external2.number().positive(),
@@ -26791,6 +26862,9 @@ var KiCadPadShapeSchema = exports_external2.enum([
26791
26862
  "roundrect",
26792
26863
  "custom"
26793
26864
  ]);
26865
+ function isLcscId(id) {
26866
+ return LCSCPartNumberSchema.safeParse(id).success;
26867
+ }
26794
26868
  async function ensureDir(path) {
26795
26869
  await mkdir(path, { recursive: true });
26796
26870
  }
@@ -26872,7 +26946,58 @@ var logger = new Logger({ prefix: "ai-eda" });
26872
26946
  function createLogger(packageName) {
26873
26947
  return logger.child(packageName);
26874
26948
  }
26949
+ var KICAD_LAYERS = {
26950
+ F_CU: "F.Cu",
26951
+ B_CU: "B.Cu",
26952
+ IN1_CU: "In1.Cu",
26953
+ IN2_CU: "In2.Cu",
26954
+ IN3_CU: "In3.Cu",
26955
+ IN4_CU: "In4.Cu",
26956
+ F_ADHES: "F.Adhes",
26957
+ F_PASTE: "F.Paste",
26958
+ F_SILKS: "F.SilkS",
26959
+ F_MASK: "F.Mask",
26960
+ F_CRTYD: "F.CrtYd",
26961
+ F_FAB: "F.Fab",
26962
+ B_ADHES: "B.Adhes",
26963
+ B_PASTE: "B.Paste",
26964
+ B_SILKS: "B.SilkS",
26965
+ B_MASK: "B.Mask",
26966
+ B_CRTYD: "B.CrtYd",
26967
+ B_FAB: "B.Fab",
26968
+ EDGE_CUTS: "Edge.Cuts",
26969
+ MARGIN: "Margin",
26970
+ DWGS_USER: "Dwgs.User",
26971
+ CMTS_USER: "Cmts.User",
26972
+ ECO1_USER: "Eco1.User",
26973
+ ECO2_USER: "Eco2.User"
26974
+ };
26975
+ var KICAD_DEFAULTS = {
26976
+ TEXT_SIZE: 1.27,
26977
+ TEXT_THICKNESS: 0.15,
26978
+ WIRE_WIDTH: 0.25,
26979
+ GRID_SCHEMATIC: 2.54,
26980
+ GRID_PCB: 0.25,
26981
+ PIN_LENGTH: 1.27,
26982
+ PIN_NAME_OFFSET: 0
26983
+ };
26984
+ var KICAD_SYMBOL_VERSION = "20241209";
26985
+ var KICAD_FOOTPRINT_VERSION = "20241209";
26986
+ var KICAD_VERSIONS = ["9.0", "8.0"];
26987
+ function detectKicadVersion() {
26988
+ const home = homedir();
26989
+ const baseDir = join2(home, "Documents", "KiCad");
26990
+ for (const version2 of KICAD_VERSIONS) {
26991
+ if (existsSync(join2(baseDir, version2))) {
26992
+ return version2;
26993
+ }
26994
+ }
26995
+ return "9.0";
26996
+ }
26875
26997
  var logger3 = createLogger("jlc-api");
26998
+ function stripTemperaturePrefix(description) {
26999
+ return description.replace(/^-?\d+℃~[+-]?\d+℃\s*/, "");
27000
+ }
26876
27001
  var JLCPCB_SEARCH_API = "https://jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList/v2";
26877
27002
 
26878
27003
  class JLCClient {
@@ -26918,7 +27043,7 @@ class JLCClient {
26918
27043
  package: c.componentSpecificationEn || "",
26919
27044
  price: c.componentPrices?.[0]?.productPrice,
26920
27045
  stock: c.stockCount,
26921
- description: c.describe,
27046
+ description: stripTemperaturePrefix(c.describe),
26922
27047
  productUrl: c.lcscGoodsUrl,
26923
27048
  datasheetPdf: c.dataManualUrl,
26924
27049
  category: c.componentTypeEn,
@@ -27158,6 +27283,25 @@ function parseSymbolPath(data) {
27158
27283
  return null;
27159
27284
  }
27160
27285
  }
27286
+ function parseSymbolText(data) {
27287
+ try {
27288
+ const f = data.split("~");
27289
+ const isPinPart = f[f.length - 1] === "pinpart" || f.some((field) => field === "pinpart");
27290
+ return {
27291
+ x: safeParseFloat(f[2]),
27292
+ y: safeParseFloat(f[3]),
27293
+ rotation: safeParseFloat(f[4]),
27294
+ color: f[5] || "#000000",
27295
+ fontSize: safeParseFloat(f[7], 7),
27296
+ textType: f[11] || "",
27297
+ text: f[12] || "",
27298
+ id: f[15] || f[13] || "",
27299
+ isPinPart
27300
+ };
27301
+ } catch {
27302
+ return null;
27303
+ }
27304
+ }
27161
27305
  function parsePad(data) {
27162
27306
  try {
27163
27307
  const f = data.split("~");
@@ -27325,6 +27469,7 @@ function parseSymbolShapes(shapes) {
27325
27469
  const polylines = [];
27326
27470
  const polygons = [];
27327
27471
  const paths = [];
27472
+ const texts = [];
27328
27473
  for (const line of shapes) {
27329
27474
  if (typeof line !== "string")
27330
27475
  continue;
@@ -27378,10 +27523,15 @@ function parseSymbolShapes(shapes) {
27378
27523
  paths.push(path);
27379
27524
  break;
27380
27525
  }
27381
- case "T":
27526
+ case "T": {
27527
+ const text = parseSymbolText(line);
27528
+ if (text && text.text.trim())
27529
+ texts.push(text);
27382
27530
  break;
27531
+ }
27383
27532
  }
27384
27533
  }
27534
+ associatePinNamesFromTexts(pins, texts);
27385
27535
  return {
27386
27536
  pins,
27387
27537
  rectangles,
@@ -27390,8 +27540,29 @@ function parseSymbolShapes(shapes) {
27390
27540
  arcs,
27391
27541
  polylines,
27392
27542
  polygons,
27393
- paths
27394
- };
27543
+ paths,
27544
+ texts
27545
+ };
27546
+ }
27547
+ function associatePinNamesFromTexts(pins, texts) {
27548
+ const pinpartTexts = texts.filter((t) => t.isPinPart && t.text.trim());
27549
+ for (const text of pinpartTexts) {
27550
+ const textContent = text.text.trim();
27551
+ const jMatch = textContent.match(/^J(\d+)(?:\/(.+))?$/i);
27552
+ if (jMatch) {
27553
+ const pinNum = jMatch[1];
27554
+ const pinFunction = jMatch[2] || textContent;
27555
+ const pin = pins.find((p) => p.number === pinNum);
27556
+ if (pin && (!pin.name || pin.name === pinNum)) {
27557
+ pin.name = pinFunction || textContent;
27558
+ }
27559
+ continue;
27560
+ }
27561
+ const numMatch = textContent.match(/^(\d+)$/);
27562
+ if (numMatch) {
27563
+ continue;
27564
+ }
27565
+ }
27395
27566
  }
27396
27567
  function parseFootprintShapes(shapes) {
27397
27568
  const pads = [];
@@ -27478,7 +27649,7 @@ function parseFootprintShapes(shapes) {
27478
27649
  }
27479
27650
  }
27480
27651
  }
27481
- const type = pads.some((p) => p.holeRadius > 0) ? "tht" : "smd";
27652
+ const type = pads.some((p) => p.holeRadius > 0 || p.shape === "POLYGON" && p.isPlated) ? "tht" : "smd";
27482
27653
  return {
27483
27654
  name: "Unknown",
27484
27655
  type,
@@ -27579,6 +27750,7 @@ class EasyEDAClient {
27579
27750
  polylines: symbolData.polylines,
27580
27751
  polygons: symbolData.polygons,
27581
27752
  paths: symbolData.paths,
27753
+ texts: symbolData.texts,
27582
27754
  origin: symbolOrigin
27583
27755
  },
27584
27756
  footprint: {
@@ -27752,6 +27924,7 @@ class EasyEDACommunityClient {
27752
27924
  polylines: symbolData.polylines,
27753
27925
  polygons: symbolData.polygons,
27754
27926
  paths: symbolData.paths,
27927
+ texts: symbolData.texts,
27755
27928
  origin: symbolOrigin,
27756
27929
  head: dataStr.head || {}
27757
27930
  },
@@ -27778,14 +27951,14 @@ class EasyEDACommunityClient {
27778
27951
  }
27779
27952
  }
27780
27953
  var easyedaCommunityClient = new EasyEDACommunityClient;
27781
- function isLcscId(id) {
27954
+ function isLcscId2(id) {
27782
27955
  return /^C\d+$/.test(id);
27783
27956
  }
27784
27957
  function createComponentService() {
27785
27958
  return {
27786
27959
  async search(query, options = {}) {
27787
27960
  const { source = "lcsc", limit = 20, inStock, basicOnly } = options;
27788
- if (source === "easyeda-community") {
27961
+ if (source === "community" || source === "easyeda-community") {
27789
27962
  const results = await easyedaCommunityClient.search({
27790
27963
  query,
27791
27964
  limit
@@ -27802,7 +27975,7 @@ function createComponentService() {
27802
27975
  return jlcClient.search(query, { limit, inStock, basicOnly });
27803
27976
  },
27804
27977
  async fetch(id) {
27805
- if (isLcscId(id)) {
27978
+ if (isLcscId2(id)) {
27806
27979
  return easyedaClient.getComponentData(id);
27807
27980
  }
27808
27981
  return null;
@@ -27830,43 +28003,6 @@ function createComponentService() {
27830
28003
  }
27831
28004
  };
27832
28005
  }
27833
- var KICAD_LAYERS = {
27834
- F_CU: "F.Cu",
27835
- B_CU: "B.Cu",
27836
- IN1_CU: "In1.Cu",
27837
- IN2_CU: "In2.Cu",
27838
- IN3_CU: "In3.Cu",
27839
- IN4_CU: "In4.Cu",
27840
- F_ADHES: "F.Adhes",
27841
- F_PASTE: "F.Paste",
27842
- F_SILKS: "F.SilkS",
27843
- F_MASK: "F.Mask",
27844
- F_CRTYD: "F.CrtYd",
27845
- F_FAB: "F.Fab",
27846
- B_ADHES: "B.Adhes",
27847
- B_PASTE: "B.Paste",
27848
- B_SILKS: "B.SilkS",
27849
- B_MASK: "B.Mask",
27850
- B_CRTYD: "B.CrtYd",
27851
- B_FAB: "B.Fab",
27852
- EDGE_CUTS: "Edge.Cuts",
27853
- MARGIN: "Margin",
27854
- DWGS_USER: "Dwgs.User",
27855
- CMTS_USER: "Cmts.User",
27856
- ECO1_USER: "Eco1.User",
27857
- ECO2_USER: "Eco2.User"
27858
- };
27859
- var KICAD_DEFAULTS = {
27860
- TEXT_SIZE: 1.27,
27861
- TEXT_THICKNESS: 0.15,
27862
- WIRE_WIDTH: 0.25,
27863
- GRID_SCHEMATIC: 2.54,
27864
- GRID_PCB: 0.25,
27865
- PIN_LENGTH: 1.27,
27866
- PIN_NAME_OFFSET: 0
27867
- };
27868
- var KICAD_SYMBOL_VERSION = "20241209";
27869
- var KICAD_FOOTPRINT_VERSION = "20241209";
27870
28006
  var PATTERNS = {
27871
28007
  resistor: /(\d+(?:\.\d+)?)\s*([kKmMgGrR]?)\s*(?:ohm|Ohm|OHM|Ω|R)?/i,
27872
28008
  capacitor: /(\d+(?:\.\d+)?)\s*([pnuμmM]?)[Ff]?\s*(?:[\/\s]*(\d+)\s*[Vv])?/i,
@@ -28276,11 +28412,34 @@ function parseSvgArcPath(path) {
28276
28412
  return null;
28277
28413
  }
28278
28414
  }
28279
- var CATEGORY_TO_PREFIX = {
28280
- Resistors: "R",
28281
- Capacitors: "C",
28282
- Inductors: "L",
28283
- Diodes: "D"
28415
+ function interpolateArc(params, segmentsPerQuarter = 4) {
28416
+ const center = svgArcToCenter(params);
28417
+ if (!center) {
28418
+ return [{ x: params.x2, y: params.y2 }];
28419
+ }
28420
+ const { cx, cy, rx, ry, startAngle, deltaAngle } = center;
28421
+ const phiRad = params.phi * Math.PI / 180;
28422
+ const cosPhi = Math.cos(phiRad);
28423
+ const sinPhi = Math.sin(phiRad);
28424
+ const arcDegrees = Math.abs(deltaAngle) * (180 / Math.PI);
28425
+ const numSegments = Math.max(2, Math.ceil(arcDegrees / 90 * segmentsPerQuarter));
28426
+ const points = [];
28427
+ for (let i = 1;i <= numSegments; i++) {
28428
+ const t = i / numSegments;
28429
+ const angle = startAngle + deltaAngle * t;
28430
+ const px = rx * Math.cos(angle);
28431
+ const py = ry * Math.sin(angle);
28432
+ const x = cosPhi * px - sinPhi * py + cx;
28433
+ const y = sinPhi * px + cosPhi * py + cy;
28434
+ points.push({ x, y });
28435
+ }
28436
+ return points;
28437
+ }
28438
+ var CATEGORY_TO_PREFIX = {
28439
+ Resistors: "R",
28440
+ Capacitors: "C",
28441
+ Inductors: "L",
28442
+ Diodes: "D"
28284
28443
  };
28285
28444
  var EE_TO_MM = 0.254;
28286
28445
  var IC_PIN_LENGTH = 2.54;
@@ -28338,7 +28497,12 @@ class SymbolConverter {
28338
28497
  return output;
28339
28498
  }
28340
28499
  hasRenderableShapes(symbol) {
28341
- return symbol.rectangles.length > 0 || symbol.circles.length > 0 || symbol.ellipses.length > 0 || symbol.polylines.length > 0 || symbol.polygons.length > 0 || symbol.arcs.length > 0 || symbol.paths.length > 0;
28500
+ const pinNamePattern = /^J\d+(?:\/|$)/i;
28501
+ const hasDecorativeText = (symbol.texts || []).some((t) => {
28502
+ const text = t.text.trim();
28503
+ return text && !pinNamePattern.test(text);
28504
+ });
28505
+ return symbol.rectangles.length > 0 || symbol.circles.length > 0 || symbol.ellipses.length > 0 || symbol.polylines.length > 0 || symbol.polygons.length > 0 || symbol.arcs.length > 0 || symbol.paths.length > 0 || hasDecorativeText;
28342
28506
  }
28343
28507
  generateFromShapes(component, name) {
28344
28508
  const { info, symbol } = component;
@@ -28372,6 +28536,18 @@ class SymbolConverter {
28372
28536
  if (pathOutput)
28373
28537
  output += pathOutput;
28374
28538
  }
28539
+ const pinNamePattern = /^J\d+(?:\/|$)/i;
28540
+ const decorativeTexts = (symbol.texts || []).filter((t) => {
28541
+ const text = t.text.trim();
28542
+ if (!text)
28543
+ return false;
28544
+ return !pinNamePattern.test(text);
28545
+ });
28546
+ for (const text of decorativeTexts) {
28547
+ const textOutput = this.convertText(text, origin);
28548
+ if (textOutput)
28549
+ output += textOutput;
28550
+ }
28375
28551
  output += ` )
28376
28552
  `;
28377
28553
  output += ` (symbol "${name}_1_1"
@@ -28556,6 +28732,24 @@ class SymbolConverter {
28556
28732
  `;
28557
28733
  return output;
28558
28734
  }
28735
+ convertText(text, origin) {
28736
+ if (!text.text || text.text.trim() === "")
28737
+ return null;
28738
+ const x = this.convertX(text.x, origin.x);
28739
+ const y = this.convertY(text.y, origin.y);
28740
+ const fontSize = roundTo(Math.max(text.fontSize * EE_TO_MM * 0.8, 0.5), 3);
28741
+ const rotation = (360 - text.rotation) % 360;
28742
+ const content = text.text.replace(/"/g, "'").replace(/\\/g, "");
28743
+ return ` (text "${content}"
28744
+ (at ${x} ${y} ${rotation})
28745
+ (effects
28746
+ (font
28747
+ (size ${fontSize} ${fontSize})
28748
+ )
28749
+ )
28750
+ )
28751
+ `;
28752
+ }
28559
28753
  parseSvgPath(pathStr, origin) {
28560
28754
  const points = [];
28561
28755
  let firstPoint = null;
@@ -29542,8 +29736,8 @@ var KI_PAD_LAYER_SMD = {
29542
29736
  11: '"*.Cu" "*.Paste" "*.Mask"'
29543
29737
  };
29544
29738
  var KI_PAD_LAYER_THT = {
29545
- 1: '"F.Cu" "F.Mask"',
29546
- 2: '"B.Cu" "B.Mask"',
29739
+ 1: '"*.Cu" "*.Mask"',
29740
+ 2: '"*.Cu" "*.Mask"',
29547
29741
  11: '"*.Cu" "*.Mask"'
29548
29742
  };
29549
29743
  var KI_PAD_SHAPE = {
@@ -29753,15 +29947,20 @@ class FootprintConverter {
29753
29947
  `;
29754
29948
  return output;
29755
29949
  }
29756
- generatePolygonPad(pad, origin, layers) {
29950
+ generatePolygonPad(pad, origin, _layers) {
29757
29951
  const x = convertX(pad.centerX, origin.x);
29758
29952
  const y = convertY(pad.centerY, origin.y);
29759
29953
  const points = parsePoints(pad.points);
29760
29954
  if (points.length < 3) {
29761
29955
  return this.generatePad({ ...pad, shape: "RECT", points: "" }, origin);
29762
29956
  }
29763
- const isSmd = pad.holeRadius === 0;
29957
+ let holeRadius = pad.holeRadius;
29958
+ if (holeRadius === 0 && pad.layerId === 11 && pad.shape === "POLYGON") {
29959
+ holeRadius = this.calculateDrillRadiusFromPolygon(points, pad.centerX, pad.centerY);
29960
+ }
29961
+ const isSmd = holeRadius === 0;
29764
29962
  const padType = isSmd ? "smd" : "thru_hole";
29963
+ const layers = getPadLayers(pad.layerId, isSmd);
29765
29964
  const polyPoints = points.map((p) => ({
29766
29965
  x: roundTo(toMM(p.x - pad.centerX), 2),
29767
29966
  y: roundTo(toMM(p.y - pad.centerY), 2)
@@ -29773,7 +29972,7 @@ class FootprintConverter {
29773
29972
  output += ` (size 0.01 0.01)
29774
29973
  `;
29775
29974
  if (!isSmd) {
29776
- const drillDiameter = roundTo(toMM(pad.holeRadius * 2), 4);
29975
+ const drillDiameter = roundTo(toMM(holeRadius * 2), 4);
29777
29976
  if (pad.holeLength && pad.holeLength > 0) {
29778
29977
  const holeH = roundTo(toMM(pad.holeLength), 4);
29779
29978
  output += ` (drill oval ${drillDiameter} ${holeH})
@@ -29807,6 +30006,25 @@ class FootprintConverter {
29807
30006
  `;
29808
30007
  return output;
29809
30008
  }
30009
+ calculateDrillRadiusFromPolygon(points, centerX, centerY) {
30010
+ if (points.length < 3)
30011
+ return 0;
30012
+ let minX = Infinity, maxX = -Infinity;
30013
+ let minY = Infinity, maxY = -Infinity;
30014
+ for (const pt of points) {
30015
+ const relX = pt.x - centerX;
30016
+ const relY = pt.y - centerY;
30017
+ minX = Math.min(minX, relX);
30018
+ maxX = Math.max(maxX, relX);
30019
+ minY = Math.min(minY, relY);
30020
+ maxY = Math.max(maxY, relY);
30021
+ }
30022
+ const width = maxX - minX;
30023
+ const height = maxY - minY;
30024
+ const minDim = Math.min(width, height);
30025
+ const drillDiameter = minDim * 0.6;
30026
+ return drillDiameter / 2;
30027
+ }
29810
30028
  generateHole(hole, origin) {
29811
30029
  const x = convertX(hole.centerX, origin.x);
29812
30030
  const y = convertY(hole.centerY, origin.y);
@@ -29933,9 +30151,9 @@ class FootprintConverter {
29933
30151
  const fontSize = roundTo(toMM(text.fontSize), 2);
29934
30152
  const rotation = text.rotation || 0;
29935
30153
  let justify = "";
29936
- if (x < -0.5) {
30154
+ if (text.type === "L") {
29937
30155
  justify = "left";
29938
- } else if (x > 0.5) {
30156
+ } else if (text.type === "R") {
29939
30157
  justify = "right";
29940
30158
  }
29941
30159
  return ` (fp_text user "${this.escapeString(text.text)}"
@@ -29952,6 +30170,9 @@ ${justify ? ` (justify ${justify})
29952
30170
  `;
29953
30171
  }
29954
30172
  generateSolidRegion(region, origin) {
30173
+ if (region.layerId === 99 || region.layerId === 100 || region.layerId === 101) {
30174
+ return "";
30175
+ }
29955
30176
  const layer = getLayer(region.layerId);
29956
30177
  const points = this.parseSvgPathToPoints(region.path, origin);
29957
30178
  if (points.length < 3)
@@ -29984,15 +30205,125 @@ ${justify ? ` (justify ${justify})
29984
30205
  }
29985
30206
  parseSvgPathToPoints(path, origin) {
29986
30207
  const points = [];
29987
- const commandRegex = /([ML])\s*([\d.-]+)[,\s]+([\d.-]+)/gi;
30208
+ let currentX = 0, currentY = 0;
30209
+ let startX = 0, startY = 0;
30210
+ const commandRegex = /([MLHVCSQTAZ])\s*([^MLHVCSQTAZ]*)/gi;
29988
30211
  let match;
29989
30212
  while ((match = commandRegex.exec(path)) !== null) {
29990
- const x = parseFloat(match[2]);
29991
- const y = parseFloat(match[3]);
29992
- points.push({
29993
- x: convertX(x, origin.x),
29994
- y: convertY(y, origin.y)
29995
- });
30213
+ const cmd = match[1].toUpperCase();
30214
+ const args = match[2].trim().split(/[\s,]+/).map(parseFloat).filter((n) => !isNaN(n));
30215
+ switch (cmd) {
30216
+ case "M":
30217
+ if (args.length >= 2) {
30218
+ currentX = args[0];
30219
+ currentY = args[1];
30220
+ if (points.length === 0) {
30221
+ startX = currentX;
30222
+ startY = currentY;
30223
+ }
30224
+ points.push({
30225
+ x: convertX(currentX, origin.x),
30226
+ y: convertY(currentY, origin.y)
30227
+ });
30228
+ }
30229
+ break;
30230
+ case "L":
30231
+ if (args.length >= 2) {
30232
+ currentX = args[0];
30233
+ currentY = args[1];
30234
+ points.push({
30235
+ x: convertX(currentX, origin.x),
30236
+ y: convertY(currentY, origin.y)
30237
+ });
30238
+ }
30239
+ break;
30240
+ case "H":
30241
+ if (args.length >= 1) {
30242
+ currentX = args[0];
30243
+ points.push({
30244
+ x: convertX(currentX, origin.x),
30245
+ y: convertY(currentY, origin.y)
30246
+ });
30247
+ }
30248
+ break;
30249
+ case "V":
30250
+ if (args.length >= 1) {
30251
+ currentY = args[0];
30252
+ points.push({
30253
+ x: convertX(currentX, origin.x),
30254
+ y: convertY(currentY, origin.y)
30255
+ });
30256
+ }
30257
+ break;
30258
+ case "C":
30259
+ if (args.length >= 6) {
30260
+ currentX = args[4];
30261
+ currentY = args[5];
30262
+ points.push({
30263
+ x: convertX(currentX, origin.x),
30264
+ y: convertY(currentY, origin.y)
30265
+ });
30266
+ }
30267
+ break;
30268
+ case "S":
30269
+ if (args.length >= 4) {
30270
+ currentX = args[2];
30271
+ currentY = args[3];
30272
+ points.push({
30273
+ x: convertX(currentX, origin.x),
30274
+ y: convertY(currentY, origin.y)
30275
+ });
30276
+ }
30277
+ break;
30278
+ case "Q":
30279
+ if (args.length >= 4) {
30280
+ currentX = args[2];
30281
+ currentY = args[3];
30282
+ points.push({
30283
+ x: convertX(currentX, origin.x),
30284
+ y: convertY(currentY, origin.y)
30285
+ });
30286
+ }
30287
+ break;
30288
+ case "T":
30289
+ if (args.length >= 2) {
30290
+ currentX = args[0];
30291
+ currentY = args[1];
30292
+ points.push({
30293
+ x: convertX(currentX, origin.x),
30294
+ y: convertY(currentY, origin.y)
30295
+ });
30296
+ }
30297
+ break;
30298
+ case "A":
30299
+ if (args.length >= 7) {
30300
+ const arcParams = {
30301
+ x1: currentX,
30302
+ y1: currentY,
30303
+ rx: args[0],
30304
+ ry: args[1],
30305
+ phi: args[2],
30306
+ largeArc: args[3] === 1,
30307
+ sweep: args[4] === 1,
30308
+ x2: args[5],
30309
+ y2: args[6]
30310
+ };
30311
+ const arcPoints = interpolateArc(arcParams, 4);
30312
+ for (const pt of arcPoints) {
30313
+ points.push({
30314
+ x: convertX(pt.x, origin.x),
30315
+ y: convertY(pt.y, origin.y)
30316
+ });
30317
+ }
30318
+ currentX = args[5];
30319
+ currentY = args[6];
30320
+ }
30321
+ break;
30322
+ case "Z":
30323
+ currentX = startX;
30324
+ currentY = startY;
30325
+ break;
30326
+ }
29996
30327
  }
29997
30328
  return points;
29998
30329
  }
@@ -30159,63 +30490,56 @@ init_category_router();
30159
30490
  var FOOTPRINT_LIBRARY_NAME = getFootprintDirName();
30160
30491
  var MODELS_3D_NAME = get3DModelsDirName();
30161
30492
  var LIBRARY_NAMESPACE2 = "jlc_mcp";
30162
- var KICAD_VERSIONS3 = ["9.0", "8.0"];
30163
- var EASYEDA_LIBRARY_NAME = "EasyEDA";
30164
- var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
30165
- var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
30166
- var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
30167
- function detectKicadVersion2() {
30168
- const home = homedir2();
30169
- const baseDir = join4(home, "Documents", "KiCad");
30170
- for (const version2 of KICAD_VERSIONS3) {
30171
- if (existsSync3(join4(baseDir, version2))) {
30172
- return version2;
30173
- }
30174
- }
30175
- return "9.0";
30176
- }
30493
+ var EASYEDA_LIBRARY_NAME2 = "EasyEDA";
30494
+ var EASYEDA_SYMBOL_LIBRARY_NAME2 = "EasyEDA.kicad_sym";
30495
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME2 = "EasyEDA.pretty";
30496
+ var EASYEDA_LIBRARY_DESCRIPTION2 = "EasyEDA Community Component Library";
30497
+ var EASYEDA_LOCAL_LIBRARY_NAME = "EasyEDA-local";
30498
+ var EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME = "EasyEDA-local.kicad_sym";
30499
+ var EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME = "EasyEDA-local.pretty";
30500
+ var EASYEDA_LOCAL_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library (Project-local)";
30177
30501
  function getGlobalLibraryPaths() {
30178
- const home = homedir2();
30179
- const version2 = detectKicadVersion2();
30180
- const plat = platform2();
30502
+ const home = homedir3();
30503
+ const version2 = detectKicadVersion();
30504
+ const plat = platform3();
30181
30505
  let base;
30182
30506
  if (plat === "linux") {
30183
- base = join4(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30507
+ base = join5(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30184
30508
  } else {
30185
- base = join4(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30509
+ base = join5(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30186
30510
  }
30187
30511
  return {
30188
30512
  base,
30189
- symbolsDir: join4(base, "symbols"),
30190
- footprintsDir: join4(base, "footprints"),
30191
- models3dDir: join4(base, "3dmodels"),
30192
- footprintDir: join4(base, "footprints", FOOTPRINT_LIBRARY_NAME),
30193
- models3dFullDir: join4(base, "3dmodels", MODELS_3D_NAME)
30513
+ symbolsDir: join5(base, "symbols"),
30514
+ footprintsDir: join5(base, "footprints"),
30515
+ models3dDir: join5(base, "3dmodels"),
30516
+ footprintDir: join5(base, "footprints", FOOTPRINT_LIBRARY_NAME),
30517
+ models3dFullDir: join5(base, "3dmodels", MODELS_3D_NAME)
30194
30518
  };
30195
30519
  }
30196
30520
  function getProjectLibraryPaths(projectPath) {
30197
- const librariesDir = join4(projectPath, "libraries");
30521
+ const librariesDir = join5(projectPath, "libraries");
30198
30522
  return {
30199
30523
  base: librariesDir,
30200
- symbolsDir: join4(librariesDir, "symbols"),
30201
- footprintsDir: join4(librariesDir, "footprints"),
30202
- models3dDir: join4(librariesDir, "3dmodels"),
30203
- footprintDir: join4(librariesDir, "footprints", FOOTPRINT_LIBRARY_NAME),
30204
- models3dFullDir: join4(librariesDir, "3dmodels", MODELS_3D_NAME)
30524
+ symbolsDir: join5(librariesDir, "symbols"),
30525
+ footprintsDir: join5(librariesDir, "footprints"),
30526
+ models3dDir: join5(librariesDir, "3dmodels"),
30527
+ footprintDir: join5(librariesDir, "footprints", FOOTPRINT_LIBRARY_NAME),
30528
+ models3dFullDir: join5(librariesDir, "3dmodels", MODELS_3D_NAME)
30205
30529
  };
30206
30530
  }
30207
- function isLcscId2(id) {
30531
+ function isLcscId3(id) {
30208
30532
  return /^C\d+$/.test(id);
30209
30533
  }
30210
- function getKicadConfigDir2(version2) {
30211
- const home = homedir2();
30212
- const plat = platform2();
30534
+ function getKicadConfigDir3(version2) {
30535
+ const home = homedir3();
30536
+ const plat = platform3();
30213
30537
  if (plat === "darwin") {
30214
- return join4(home, "Library", "Preferences", "kicad", version2);
30538
+ return join5(home, "Library", "Preferences", "kicad", version2);
30215
30539
  } else if (plat === "win32") {
30216
- return join4(process.env.APPDATA || join4(home, "AppData", "Roaming"), "kicad", version2);
30540
+ return join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "kicad", version2);
30217
30541
  } else {
30218
- return join4(home, ".config", "kicad", version2);
30542
+ return join5(home, ".config", "kicad", version2);
30219
30543
  }
30220
30544
  }
30221
30545
  async function parseSymbolLibrary(filePath, libraryName, category, models3dDir) {
@@ -30241,8 +30565,8 @@ async function parseSymbolLibrary(filePath, libraryName, category, models3dDir)
30241
30565
  const name = nameParts.length > 1 ? nameParts[1] : fullSymbolRef;
30242
30566
  const fpMatch = symbolBlock.match(/\(property\s+"Footprint"\s+"([^"]+)"/);
30243
30567
  const footprintRef = fpMatch ? fpMatch[1] : "";
30244
- const model3dPath = join4(models3dDir, `${name}.step`);
30245
- const has3dModel = existsSync3(model3dPath);
30568
+ const model3dPath = join5(models3dDir, `${name}.step`);
30569
+ const has3dModel = existsSync4(model3dPath);
30246
30570
  if (lcscId) {
30247
30571
  components.push({
30248
30572
  lcscId,
@@ -30258,6 +30582,42 @@ async function parseSymbolLibrary(filePath, libraryName, category, models3dDir)
30258
30582
  } catch {}
30259
30583
  return components;
30260
30584
  }
30585
+ function removeSymbolFromLibrary(content, symbolName) {
30586
+ const lines = content.split(`
30587
+ `);
30588
+ const result = [];
30589
+ let depth = 0;
30590
+ let inTargetSymbol = false;
30591
+ let symbolStartDepth = 0;
30592
+ for (const line of lines) {
30593
+ const openCount = (line.match(/\(/g) || []).length;
30594
+ const closeCount = (line.match(/\)/g) || []).length;
30595
+ const symbolMatch = line.match(/^\s*\(symbol\s+"([^"]+)"/);
30596
+ if (symbolMatch && !inTargetSymbol) {
30597
+ const fullRef = symbolMatch[1];
30598
+ const nameOnly = fullRef.includes(":") ? fullRef.split(":")[1] : fullRef;
30599
+ if (nameOnly === symbolName || nameOnly.startsWith(`${symbolName}_`)) {
30600
+ inTargetSymbol = true;
30601
+ symbolStartDepth = depth;
30602
+ depth += openCount - closeCount;
30603
+ continue;
30604
+ }
30605
+ }
30606
+ if (inTargetSymbol) {
30607
+ depth += openCount - closeCount;
30608
+ if (depth <= symbolStartDepth) {
30609
+ inTargetSymbol = false;
30610
+ }
30611
+ continue;
30612
+ }
30613
+ depth += openCount - closeCount;
30614
+ result.push(line);
30615
+ }
30616
+ return result.join(`
30617
+ `).replace(/\n{3,}/g, `
30618
+
30619
+ `);
30620
+ }
30261
30621
  function adaptCommunityComponent(component) {
30262
30622
  const symbolHead = component.symbol.head;
30263
30623
  const cPara = symbolHead?.c_para || {};
@@ -30280,6 +30640,7 @@ function adaptCommunityComponent(component) {
30280
30640
  polylines: component.symbol.polylines,
30281
30641
  polygons: component.symbol.polygons,
30282
30642
  paths: component.symbol.paths,
30643
+ texts: component.symbol.texts || [],
30283
30644
  origin: component.symbol.origin
30284
30645
  },
30285
30646
  footprint: {
@@ -30303,14 +30664,11 @@ function adaptCommunityComponent(component) {
30303
30664
  function createLibraryService() {
30304
30665
  return {
30305
30666
  async install(id, options = {}) {
30306
- const isCommunityComponent = !isLcscId2(id);
30307
- if (isCommunityComponent && !options.projectPath) {
30308
- throw new Error("EasyEDA community components require projectPath for local storage");
30309
- }
30310
- const isGlobal = !isCommunityComponent && !options.projectPath;
30667
+ const isCommunityComponent = !isLcscId3(id);
30668
+ const isGlobal = !options.projectPath;
30311
30669
  const paths = isGlobal ? getGlobalLibraryPaths() : getProjectLibraryPaths(options.projectPath);
30312
30670
  let component = null;
30313
- if (isLcscId2(id)) {
30671
+ if (isLcscId3(id)) {
30314
30672
  component = await easyedaClient.getComponentData(id);
30315
30673
  } else {
30316
30674
  const communityComponent = await easyedaCommunityClient.getComponent(id);
@@ -30350,13 +30708,27 @@ function createLibraryService() {
30350
30708
  let category;
30351
30709
  let modelPath;
30352
30710
  if (isCommunityComponent) {
30353
- const librariesDir = join4(options.projectPath, "libraries");
30354
- const symbolsDir = join4(librariesDir, "symbols");
30355
- footprintDir = join4(librariesDir, "footprints", EASYEDA_FOOTPRINT_LIBRARY_NAME);
30356
- models3dDir = join4(librariesDir, "3dmodels", "EasyEDA.3dshapes");
30711
+ const libName = isGlobal ? EASYEDA_LIBRARY_NAME2 : EASYEDA_LOCAL_LIBRARY_NAME;
30712
+ const symLibFile = isGlobal ? EASYEDA_SYMBOL_LIBRARY_NAME2 : EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME;
30713
+ const fpLibDir = isGlobal ? EASYEDA_FOOTPRINT_LIBRARY_NAME2 : EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME;
30714
+ const libDesc = isGlobal ? EASYEDA_LIBRARY_DESCRIPTION2 : EASYEDA_LOCAL_LIBRARY_DESCRIPTION;
30715
+ const models3dDirName = isGlobal ? "EasyEDA.3dshapes" : "EasyEDA-local.3dshapes";
30716
+ let symbolsDir;
30717
+ let easyedaModelsDir;
30718
+ if (isGlobal) {
30719
+ symbolsDir = paths.symbolsDir;
30720
+ footprintDir = join5(paths.footprintsDir, fpLibDir);
30721
+ easyedaModelsDir = join5(paths.models3dDir, models3dDirName);
30722
+ } else {
30723
+ const librariesDir = join5(options.projectPath, "libraries");
30724
+ symbolsDir = join5(librariesDir, "symbols");
30725
+ footprintDir = join5(librariesDir, "footprints", fpLibDir);
30726
+ easyedaModelsDir = join5(librariesDir, "3dmodels", models3dDirName);
30727
+ }
30728
+ models3dDir = easyedaModelsDir;
30357
30729
  await ensureDir(symbolsDir);
30358
30730
  await ensureDir(footprintDir);
30359
- symbolFile = join4(symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME);
30731
+ symbolFile = join5(symbolsDir, symLibFile);
30360
30732
  symbolName = component.info.name.replace(/[^a-zA-Z0-9_-]/g, "_");
30361
30733
  const include3d = options.include3d ?? true;
30362
30734
  let modelRelativePath;
@@ -30365,40 +30737,49 @@ function createLibraryService() {
30365
30737
  const model = await easyedaCommunityClient.get3DModel(component.model3d.uuid, "step");
30366
30738
  if (model) {
30367
30739
  const modelFilename = `${symbolName}.step`;
30368
- modelPath = join4(models3dDir, modelFilename);
30740
+ modelPath = join5(models3dDir, modelFilename);
30369
30741
  await writeBinary(modelPath, model);
30370
- modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/EasyEDA.3dshapes/${modelFilename}`;
30742
+ if (isGlobal) {
30743
+ modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${models3dDirName}/${modelFilename}`;
30744
+ } else {
30745
+ modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/${models3dDirName}/${modelFilename}`;
30746
+ }
30371
30747
  }
30372
30748
  }
30373
30749
  const footprint = footprintConverter.convert(component, {
30374
- libraryName: EASYEDA_LIBRARY_NAME,
30750
+ libraryName: libName,
30375
30751
  include3DModel: !!modelRelativePath,
30376
30752
  modelPath: modelRelativePath
30377
30753
  });
30378
- footprintPath = join4(footprintDir, `${symbolName}.kicad_mod`);
30379
- footprintRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
30754
+ footprintPath = join5(footprintDir, `${symbolName}.kicad_mod`);
30755
+ footprintRef = `${libName}:${symbolName}`;
30380
30756
  await writeText(footprintPath, footprint);
30381
30757
  component.info.package = footprintRef;
30382
- await ensureSymLibTable(options.projectPath, symbolFile, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
30383
- await ensureFpLibTable(options.projectPath, footprintDir, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
30384
- symbolRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
30758
+ if (isGlobal) {
30759
+ const { ensureGlobalEasyEDALibrary: ensureGlobalEasyEDALibrary2 } = await Promise.resolve().then(() => (init_global_lib_table(), exports_global_lib_table));
30760
+ await ensureGlobalEasyEDALibrary2();
30761
+ } else {
30762
+ await ensureSymLibTable(options.projectPath, symbolFile, libName, libDesc);
30763
+ await ensureFpLibTable(options.projectPath, footprintDir, libName, libDesc);
30764
+ }
30765
+ symbolRef = `${libName}:${symbolName}`;
30385
30766
  } else {
30386
30767
  category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
30387
30768
  const symbolLibraryFilename = getLibraryFilename(category);
30388
- symbolFile = join4(paths.symbolsDir, symbolLibraryFilename);
30769
+ symbolFile = join5(paths.symbolsDir, symbolLibraryFilename);
30389
30770
  footprintDir = paths.footprintDir;
30390
30771
  models3dDir = paths.models3dFullDir;
30391
30772
  await ensureDir(paths.symbolsDir);
30392
30773
  await ensureDir(paths.footprintDir);
30393
30774
  symbolName = symbolConverter.getSymbolName(component);
30394
- const include3d = options.include3d ?? false;
30775
+ const include3d = options.include3d ?? true;
30395
30776
  let modelRelativePath;
30396
30777
  if (include3d && component.model3d) {
30397
30778
  await ensureDir(models3dDir);
30398
30779
  const model = await easyedaClient.get3DModel(component.model3d.uuid, "step");
30399
30780
  if (model) {
30400
30781
  const modelFilename = `${symbolName}.step`;
30401
- modelPath = join4(models3dDir, modelFilename);
30782
+ modelPath = join5(models3dDir, modelFilename);
30402
30783
  await writeBinary(modelPath, model);
30403
30784
  modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${MODELS_3D_NAME}/${modelFilename}`;
30404
30785
  }
@@ -30410,7 +30791,7 @@ function createLibraryService() {
30410
30791
  if (footprintResult.type === "reference") {
30411
30792
  footprintRef = footprintResult.reference;
30412
30793
  } else {
30413
- footprintPath = join4(footprintDir, `${footprintResult.name}.kicad_mod`);
30794
+ footprintPath = join5(footprintDir, `${footprintResult.name}.kicad_mod`);
30414
30795
  footprintRef = getFootprintReference(footprintResult.name);
30415
30796
  await writeText(footprintPath, footprintResult.content);
30416
30797
  }
@@ -30423,12 +30804,12 @@ function createLibraryService() {
30423
30804
  }
30424
30805
  let symbolContent;
30425
30806
  let symbolAction;
30426
- if (existsSync3(symbolFile)) {
30807
+ if (existsSync4(symbolFile)) {
30427
30808
  const existingContent = await readFile4(symbolFile, "utf-8");
30428
30809
  if (symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
30429
30810
  if (options.force) {
30430
30811
  symbolContent = symbolConverter.replaceInLibrary(existingContent, component, {
30431
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
30812
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
30432
30813
  symbolName: isCommunityComponent ? symbolName : undefined
30433
30814
  });
30434
30815
  symbolAction = "replaced";
@@ -30438,14 +30819,14 @@ function createLibraryService() {
30438
30819
  }
30439
30820
  } else {
30440
30821
  symbolContent = symbolConverter.appendToLibrary(existingContent, component, {
30441
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
30822
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
30442
30823
  symbolName: isCommunityComponent ? symbolName : undefined
30443
30824
  });
30444
30825
  symbolAction = "appended";
30445
30826
  }
30446
30827
  } else {
30447
30828
  symbolContent = symbolConverter.convert(component, {
30448
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
30829
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
30449
30830
  symbolName: isCommunityComponent ? symbolName : undefined
30450
30831
  });
30451
30832
  symbolAction = "created";
@@ -30515,9 +30896,9 @@ function createLibraryService() {
30515
30896
  if (options.category && options.category !== category)
30516
30897
  continue;
30517
30898
  const libraryFilename = getLibraryFilename(category);
30518
- const libraryPath = join4(paths.symbolsDir, libraryFilename);
30899
+ const libraryPath = join5(paths.symbolsDir, libraryFilename);
30519
30900
  const libraryName = `JLC-MCP-${category}`;
30520
- if (existsSync3(libraryPath)) {
30901
+ if (existsSync4(libraryPath)) {
30521
30902
  const components = await parseSymbolLibrary(libraryPath, libraryName, category, paths.models3dFullDir);
30522
30903
  allComponents.push(...components);
30523
30904
  }
@@ -30532,13 +30913,13 @@ function createLibraryService() {
30532
30913
  await ensureGlobalLibraryTables2();
30533
30914
  },
30534
30915
  async getStatus() {
30535
- const version2 = detectKicadVersion2();
30916
+ const version2 = detectKicadVersion();
30536
30917
  const paths = getGlobalLibraryPaths();
30537
- const configDir = getKicadConfigDir2(version2);
30538
- const symLibTablePath = join4(configDir, "sym-lib-table");
30539
- const fpLibTablePath = join4(configDir, "fp-lib-table");
30918
+ const configDir = getKicadConfigDir3(version2);
30919
+ const symLibTablePath = join5(configDir, "sym-lib-table");
30920
+ const fpLibTablePath = join5(configDir, "fp-lib-table");
30540
30921
  let installed = false;
30541
- if (existsSync3(paths.symbolsDir)) {
30922
+ if (existsSync4(paths.symbolsDir)) {
30542
30923
  try {
30543
30924
  const files = await readdir2(paths.symbolsDir);
30544
30925
  installed = files.some((f) => f.endsWith(".kicad_sym"));
@@ -30547,7 +30928,7 @@ function createLibraryService() {
30547
30928
  }
30548
30929
  }
30549
30930
  let linked = false;
30550
- if (existsSync3(symLibTablePath)) {
30931
+ if (existsSync4(symLibTablePath)) {
30551
30932
  try {
30552
30933
  const content = await readFile4(symLibTablePath, "utf-8");
30553
30934
  linked = content.includes("JLC-MCP");
@@ -30570,148 +30951,437 @@ function createLibraryService() {
30570
30951
  fpLibTable: fpLibTablePath
30571
30952
  }
30572
30953
  };
30954
+ },
30955
+ async isEasyEDAInstalled(componentName) {
30956
+ const paths = getGlobalLibraryPaths();
30957
+ const easyedaLibPath = join5(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
30958
+ if (!existsSync4(easyedaLibPath)) {
30959
+ return false;
30960
+ }
30961
+ try {
30962
+ const content = await readFile4(easyedaLibPath, "utf-8");
30963
+ const sanitizedName = componentName.replace(/[^a-zA-Z0-9_-]/g, "_");
30964
+ const pattern = new RegExp(`\\(symbol\\s+"${sanitizedName}"`, "m");
30965
+ return pattern.test(content);
30966
+ } catch {
30967
+ return false;
30968
+ }
30969
+ },
30970
+ async regenerate(options = {}) {
30971
+ const include3d = options.include3d ?? true;
30972
+ const installed = await this.listInstalled({ projectPath: options.projectPath });
30973
+ const results = {
30974
+ total: installed.length,
30975
+ success: 0,
30976
+ failed: 0,
30977
+ components: []
30978
+ };
30979
+ for (let i = 0;i < installed.length; i++) {
30980
+ const component = installed[i];
30981
+ options.onProgress?.(i + 1, installed.length, component, "start");
30982
+ try {
30983
+ await this.install(component.lcscId, {
30984
+ projectPath: options.projectPath,
30985
+ include3d,
30986
+ force: true
30987
+ });
30988
+ results.success++;
30989
+ results.components.push({
30990
+ id: component.lcscId,
30991
+ name: component.name,
30992
+ status: "success"
30993
+ });
30994
+ options.onProgress?.(i + 1, installed.length, component, "success");
30995
+ } catch (error2) {
30996
+ const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
30997
+ results.failed++;
30998
+ results.components.push({
30999
+ id: component.lcscId,
31000
+ name: component.name,
31001
+ status: "failed",
31002
+ error: errorMessage
31003
+ });
31004
+ options.onProgress?.(i + 1, installed.length, component, "error", errorMessage);
31005
+ }
31006
+ }
31007
+ return results;
31008
+ },
31009
+ async remove(lcscId, options = {}) {
31010
+ const paths = options.projectPath ? getProjectLibraryPaths(options.projectPath) : getGlobalLibraryPaths();
31011
+ const installed = await this.listInstalled({ projectPath: options.projectPath });
31012
+ const component = installed.find((c) => c.lcscId === lcscId);
31013
+ if (!component) {
31014
+ throw new Error(`Component ${lcscId} not found in library`);
31015
+ }
31016
+ const result = {
31017
+ success: false,
31018
+ lcscId,
31019
+ symbolRemoved: false,
31020
+ footprintRemoved: false,
31021
+ model3dRemoved: false
31022
+ };
31023
+ const libraryFilename = getLibraryFilename(component.category);
31024
+ const symbolLibPath = join5(paths.symbolsDir, libraryFilename);
31025
+ if (existsSync4(symbolLibPath)) {
31026
+ const content = await readFile4(symbolLibPath, "utf-8");
31027
+ const newContent = removeSymbolFromLibrary(content, component.name);
31028
+ await writeText(symbolLibPath, newContent);
31029
+ result.symbolRemoved = true;
31030
+ }
31031
+ if (component.footprintRef?.startsWith("JLC-MCP:")) {
31032
+ const fpName = component.footprintRef.split(":")[1];
31033
+ const footprintPath = join5(paths.footprintDir, `${fpName}.kicad_mod`);
31034
+ if (existsSync4(footprintPath)) {
31035
+ await unlink(footprintPath);
31036
+ result.footprintRemoved = true;
31037
+ }
31038
+ }
31039
+ if (component.has3dModel) {
31040
+ const modelPath = join5(paths.models3dFullDir, `${component.name}.step`);
31041
+ if (existsSync4(modelPath)) {
31042
+ await unlink(modelPath);
31043
+ result.model3dRemoved = true;
31044
+ }
31045
+ }
31046
+ result.success = result.symbolRemoved;
31047
+ return result;
30573
31048
  }
30574
31049
  };
30575
31050
  }
30576
31051
  init_lib_table();
30577
31052
  init_category_router();
30578
31053
  init_global_lib_table();
30579
-
30580
- // src/tools/search.ts
30581
- var searchComponentsTool = {
30582
- name: "component_search",
30583
- description: "Search the JLC/JLCPCB component database by keyword. Returns components with LCSC part numbers (JLC's preferred supplier for assembly). Includes prices, stock levels, and whether parts are in the Basic Parts Library (lower assembly cost).",
30584
- inputSchema: {
30585
- type: "object",
30586
- properties: {
30587
- query: {
30588
- type: "string",
30589
- description: 'Search query (e.g., "ESP32", "STM32F103", "0805 100nF")'
30590
- },
30591
- limit: {
30592
- type: "number",
30593
- description: "Maximum number of results (default: 10, max: 50)"
30594
- },
30595
- in_stock: {
30596
- type: "boolean",
30597
- description: "Only show in-stock items (default: false)"
30598
- },
30599
- basic_only: {
30600
- type: "boolean",
30601
- description: "Only show JLCPCB Basic Parts Library components (lower assembly cost, default: false)"
30602
- }
30603
- },
30604
- required: ["query"]
31054
+ var logger7 = createLogger("http-routes");
31055
+ var __filename2 = fileURLToPath(import.meta.url);
31056
+ var __dirname2 = dirname3(__filename2);
31057
+ var htmlCache = null;
31058
+ function getHtmlPage() {
31059
+ if (htmlCache)
31060
+ return htmlCache;
31061
+ try {
31062
+ const possiblePaths = [
31063
+ join6(__dirname2, "assets/search.html"),
31064
+ join6(__dirname2, "../dist/assets/search.html"),
31065
+ join6(__dirname2, "../assets/search.html"),
31066
+ join6(__dirname2, "../assets/search-built.html"),
31067
+ join6(process.cwd(), "dist/assets/search.html"),
31068
+ join6(process.cwd(), "packages/core/dist/assets/search.html"),
31069
+ join6(__dirname2, "../../dist/assets/search.html"),
31070
+ join6(__dirname2, "../../../core/dist/assets/search.html"),
31071
+ join6(__dirname2, "../../../../packages/core/dist/assets/search.html"),
31072
+ join6(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
31073
+ ];
31074
+ for (const path of possiblePaths) {
31075
+ try {
31076
+ htmlCache = readFileSync(path, "utf-8");
31077
+ logger7.debug(`Loaded HTML from: ${path}`);
31078
+ return htmlCache;
31079
+ } catch {}
31080
+ }
31081
+ throw new Error("HTML file not found");
31082
+ } catch (error2) {
31083
+ logger7.error("Failed to load HTML page:", error2);
31084
+ return `<!DOCTYPE html>
31085
+ <html>
31086
+ <head><title>Error</title></head>
31087
+ <body>
31088
+ <h1>Error: Search page not found</h1>
31089
+ <p>The search page has not been built. Run: bun run build</p>
31090
+ </body>
31091
+ </html>`;
30605
31092
  }
30606
- };
30607
- var SearchParamsSchema = exports_external.object({
30608
- query: exports_external.string().min(1),
30609
- limit: exports_external.number().min(1).max(50).default(10),
30610
- in_stock: exports_external.boolean().optional(),
30611
- basic_only: exports_external.boolean().optional()
30612
- });
30613
- async function handleSearchComponents(args) {
30614
- const params = SearchParamsSchema.parse(args);
30615
- const results = await jlcClient.search(params.query, {
30616
- limit: params.limit,
30617
- inStock: params.in_stock,
30618
- basicOnly: params.basic_only
30619
- });
30620
- return {
30621
- content: [{
30622
- type: "text",
30623
- text: JSON.stringify(results, null, 2)
30624
- }]
30625
- };
30626
31093
  }
30627
-
30628
- // src/tools/details.ts
30629
- var getComponentTool = {
30630
- name: "component_get",
30631
- description: "Get detailed component information by LCSC part number (e.g., C2040). LCSC is JLC PCB's preferred supplier - components with LCSC IDs are available for JLC assembly. Returns symbol pins, footprint pads, manufacturer info, datasheet URL, and 3D model reference.",
30632
- inputSchema: {
30633
- type: "object",
30634
- properties: {
30635
- lcsc_id: {
30636
- type: "string",
30637
- description: "LCSC part number (e.g., C2040, C14663)"
30638
- }
30639
- },
30640
- required: ["lcsc_id"]
31094
+ async function handleRequest(req, res) {
31095
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
31096
+ const pathname = url.pathname;
31097
+ logger7.debug(`${req.method} ${pathname}`);
31098
+ if (pathname === "/" || pathname === "/index.html") {
31099
+ const query = url.searchParams.get("q") || undefined;
31100
+ serveHtml(res, query);
31101
+ } else if (pathname === "/api/search") {
31102
+ await handleSearch(url, res);
31103
+ } else if (pathname.startsWith("/api/component/")) {
31104
+ const uuid2 = pathname.replace("/api/component/", "");
31105
+ await handleComponent(uuid2, res);
31106
+ } else if (pathname === "/health") {
31107
+ res.writeHead(200, { "Content-Type": "application/json" });
31108
+ res.end(JSON.stringify({ status: "ok" }));
31109
+ } else {
31110
+ res.writeHead(404, { "Content-Type": "application/json" });
31111
+ res.end(JSON.stringify({ error: "Not found" }));
30641
31112
  }
30642
- };
30643
- var GetComponentParamsSchema = exports_external.object({
30644
- lcsc_id: exports_external.string().regex(/^C\d+$/, "Invalid LCSC part number")
30645
- });
30646
- async function handleGetComponent(args) {
30647
- const params = GetComponentParamsSchema.parse(args);
30648
- const component = await easyedaClient.getComponentData(params.lcsc_id);
30649
- if (!component) {
30650
- return {
30651
- content: [{
30652
- type: "text",
30653
- text: `Component ${params.lcsc_id} not found`
30654
- }],
30655
- isError: true
30656
- };
31113
+ }
31114
+ function serveHtml(res, initialQuery) {
31115
+ let html = getHtmlPage();
31116
+ if (initialQuery) {
31117
+ const script = `<script>window.__INITIAL_QUERY__ = ${JSON.stringify(initialQuery)};</script>`;
31118
+ html = html.replace("</head>", `${script}</head>`);
30657
31119
  }
30658
- const summary = {
30659
- info: component.info,
30660
- symbol: {
30661
- pin_count: component.symbol.pins.length,
30662
- pins: component.symbol.pins
30663
- },
30664
- footprint: {
30665
- name: component.footprint.name,
30666
- type: component.footprint.type,
30667
- pad_count: component.footprint.pads.length,
30668
- pads: component.footprint.pads
30669
- },
30670
- has_3d_model: !!component.model3d,
30671
- model_3d_uuid: component.model3d?.uuid
30672
- };
30673
- return {
30674
- content: [{
30675
- type: "text",
30676
- text: JSON.stringify(summary, null, 2)
30677
- }]
30678
- };
31120
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
31121
+ res.end(html);
30679
31122
  }
30680
-
30681
- // src/tools/library.ts
30682
- var libraryService = createLibraryService();
30683
- var componentService = createComponentService();
30684
- var getSymbolKicadTool = {
30685
- name: "library_get_symbol",
30686
- description: "Get a KiCad-compatible symbol definition by LCSC part number. Returns the symbol in .kicad_sym format. LCSC is JLC PCB's preferred supplier for assembly.",
30687
- inputSchema: {
30688
- type: "object",
30689
- properties: {
30690
- lcsc_id: {
30691
- type: "string",
30692
- description: "LCSC part number (e.g., C2040)"
31123
+ async function handleSearch(url, res) {
31124
+ const query = url.searchParams.get("q") || "";
31125
+ const source = url.searchParams.get("source") || "user";
31126
+ const page = parseInt(url.searchParams.get("page") || "1", 10);
31127
+ const limit = parseInt(url.searchParams.get("limit") || "20", 10);
31128
+ if (!query) {
31129
+ res.writeHead(400, { "Content-Type": "application/json" });
31130
+ res.end(JSON.stringify({ error: "Missing query parameter" }));
31131
+ return;
31132
+ }
31133
+ try {
31134
+ const allResults = await easyedaCommunityClient.search({
31135
+ query,
31136
+ source,
31137
+ limit: Math.min(limit * page + limit, 100)
31138
+ });
31139
+ const startIndex = (page - 1) * limit;
31140
+ const endIndex = startIndex + limit;
31141
+ const results = allResults.slice(startIndex, endIndex);
31142
+ const totalPages = Math.ceil(allResults.length / limit);
31143
+ res.writeHead(200, { "Content-Type": "application/json" });
31144
+ res.end(JSON.stringify({
31145
+ results,
31146
+ pagination: {
31147
+ page,
31148
+ limit,
31149
+ total: allResults.length,
31150
+ totalPages,
31151
+ hasNext: page < totalPages,
31152
+ hasPrev: page > 1
30693
31153
  }
31154
+ }));
31155
+ } catch (error2) {
31156
+ logger7.error("Search error:", error2);
31157
+ res.writeHead(500, { "Content-Type": "application/json" });
31158
+ res.end(JSON.stringify({ error: "Search failed" }));
31159
+ }
31160
+ }
31161
+ function communityToComponentData(community) {
31162
+ const cPara = community.symbol.head?.c_para ?? {};
31163
+ const fpCPara = community.footprint.head?.c_para ?? {};
31164
+ return {
31165
+ info: {
31166
+ name: community.title || cPara.name || "Unknown",
31167
+ prefix: cPara.pre || cPara.Prefix || "U",
31168
+ package: fpCPara.package || community.footprint.name,
31169
+ manufacturer: cPara.Manufacturer || cPara.BOM_Manufacturer,
31170
+ description: community.description || cPara.BOM_Manufacturer_Part,
31171
+ category: cPara.package
30694
31172
  },
30695
- required: ["lcsc_id"]
31173
+ symbol: community.symbol,
31174
+ footprint: community.footprint,
31175
+ model3d: community.model3d,
31176
+ rawData: community.rawData
31177
+ };
31178
+ }
31179
+ async function handleComponent(uuid2, res) {
31180
+ if (!uuid2) {
31181
+ res.writeHead(400, { "Content-Type": "application/json" });
31182
+ res.end(JSON.stringify({ error: "Missing UUID" }));
31183
+ return;
30696
31184
  }
30697
- };
30698
- var getFootprintKicadTool = {
30699
- name: "library_get_footprint",
30700
- description: "Get a KiCad-compatible footprint definition by LCSC part number. Returns the footprint in .kicad_mod format. LCSC is JLC PCB's preferred supplier for assembly.",
31185
+ try {
31186
+ const component2 = await easyedaCommunityClient.getComponent(uuid2);
31187
+ if (!component2) {
31188
+ res.writeHead(404, { "Content-Type": "application/json" });
31189
+ res.end(JSON.stringify({ error: "Component not found" }));
31190
+ return;
31191
+ }
31192
+ const componentData = communityToComponentData(component2);
31193
+ let symbolSexpr = "";
31194
+ let footprintSexpr = "";
31195
+ try {
31196
+ symbolSexpr = symbolConverter.convertToSymbolEntry(componentData);
31197
+ } catch (e) {
31198
+ logger7.warn("Symbol conversion failed:", e);
31199
+ }
31200
+ try {
31201
+ footprintSexpr = footprintConverter.convert(componentData);
31202
+ } catch (e) {
31203
+ logger7.warn("Footprint conversion failed:", e);
31204
+ }
31205
+ res.writeHead(200, { "Content-Type": "application/json" });
31206
+ res.end(JSON.stringify({
31207
+ uuid: component2.uuid,
31208
+ title: component2.title,
31209
+ description: component2.description,
31210
+ owner: component2.owner,
31211
+ symbolSexpr,
31212
+ footprintSexpr,
31213
+ model3d: component2.model3d
31214
+ }));
31215
+ } catch (error2) {
31216
+ logger7.error("Component fetch error:", error2);
31217
+ res.writeHead(500, { "Content-Type": "application/json" });
31218
+ res.end(JSON.stringify({ error: "Failed to fetch component" }));
31219
+ }
31220
+ }
31221
+ var logger8 = createLogger("http-server");
31222
+ var DEFAULT_PORT = 3847;
31223
+ var serverInstance = null;
31224
+ function startHttpServer(options = {}) {
31225
+ if (serverInstance) {
31226
+ logger8.debug("HTTP server already running");
31227
+ const port2 = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
31228
+ options.onReady?.(`http://localhost:${port2}`);
31229
+ return port2;
31230
+ }
31231
+ const port = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
31232
+ serverInstance = createServer(async (req, res) => {
31233
+ res.setHeader("Access-Control-Allow-Origin", "*");
31234
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
31235
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
31236
+ if (req.method === "OPTIONS") {
31237
+ res.writeHead(204);
31238
+ res.end();
31239
+ return;
31240
+ }
31241
+ try {
31242
+ await handleRequest(req, res);
31243
+ } catch (error2) {
31244
+ logger8.error("Request error:", error2);
31245
+ res.writeHead(500, { "Content-Type": "application/json" });
31246
+ res.end(JSON.stringify({ error: "Internal server error" }));
31247
+ }
31248
+ });
31249
+ serverInstance.listen(port, () => {
31250
+ const url = `http://localhost:${port}`;
31251
+ logger8.info(`HTTP server listening on ${url}`);
31252
+ options.onReady?.(url);
31253
+ });
31254
+ serverInstance.on("error", (error2) => {
31255
+ if (error2.code === "EADDRINUSE") {
31256
+ logger8.warn(`Port ${port} already in use, HTTP server not started`);
31257
+ } else {
31258
+ logger8.error("HTTP server error:", error2);
31259
+ }
31260
+ });
31261
+ return port;
31262
+ }
31263
+ var EE_TO_MM3 = 10 * 0.0254;
31264
+ var EE_TO_MM4 = 10 * 0.0254;
31265
+ var EE_TO_MM5 = 10 * 0.0254;
31266
+
31267
+ // src/services.ts
31268
+ var componentService = null;
31269
+ var libraryService = null;
31270
+ function getComponentService() {
31271
+ if (!componentService) {
31272
+ componentService = createComponentService();
31273
+ }
31274
+ return componentService;
31275
+ }
31276
+ function getLibraryService() {
31277
+ if (!libraryService) {
31278
+ libraryService = createLibraryService();
31279
+ }
31280
+ return libraryService;
31281
+ }
31282
+
31283
+ // src/tools/search.ts
31284
+ var componentSearchTool = {
31285
+ name: "component_search",
31286
+ description: `Search for electronic components by keyword.
31287
+
31288
+ Sources:
31289
+ - "lcsc" (default): Search JLCPCB/LCSC official parts library. Returns components with LCSC IDs available for JLC assembly.
31290
+ - "community": Search EasyEDA community library for user-contributed parts (Arduino modules, XIAO, custom breakouts).
31291
+
31292
+ Returns full component details for selection including:
31293
+ - LCSC ID, manufacturer part number, manufacturer name
31294
+ - Description, package, datasheet URL
31295
+ - Price, stock, library type (basic/extended)
31296
+ - Key attributes (resistance, voltage, etc.)`,
30701
31297
  inputSchema: {
30702
31298
  type: "object",
30703
31299
  properties: {
30704
- lcsc_id: {
31300
+ query: {
30705
31301
  type: "string",
30706
- description: "LCSC part number (e.g., C2040)"
31302
+ description: 'Search query (e.g., "ESP32", "STM32F103", "0805 100nF", "XIAO RP2040")'
31303
+ },
31304
+ source: {
31305
+ type: "string",
31306
+ enum: ["lcsc", "community"],
31307
+ description: 'Search source: "lcsc" for official parts (default), "community" for EasyEDA user-contributed'
31308
+ },
31309
+ limit: {
31310
+ type: "number",
31311
+ description: "Maximum number of results (default: 10, max: 50)"
31312
+ },
31313
+ in_stock: {
31314
+ type: "boolean",
31315
+ description: "Only show in-stock items (default: false, LCSC only)"
31316
+ },
31317
+ basic_only: {
31318
+ type: "boolean",
31319
+ description: "Only show JLCPCB Basic Parts Library components (lower assembly cost, LCSC only)"
30707
31320
  }
30708
31321
  },
30709
- required: ["lcsc_id"]
31322
+ required: ["query"]
30710
31323
  }
30711
31324
  };
30712
- var fetchLibraryTool = {
30713
- name: "library_fetch",
30714
- description: `Fetch a component and add it to KiCad libraries.
31325
+ var SearchParamsSchema = exports_external.object({
31326
+ query: exports_external.string().min(1),
31327
+ source: exports_external.enum(["lcsc", "community"]).default("lcsc"),
31328
+ limit: exports_external.number().min(1).max(50).default(10),
31329
+ in_stock: exports_external.boolean().optional(),
31330
+ basic_only: exports_external.boolean().optional()
31331
+ });
31332
+ async function handleComponentSearch(args) {
31333
+ const params = SearchParamsSchema.parse(args);
31334
+ const results = await getComponentService().search(params.query, {
31335
+ source: params.source,
31336
+ limit: params.limit,
31337
+ inStock: params.in_stock,
31338
+ basicOnly: params.basic_only
31339
+ });
31340
+ if (results.length === 0) {
31341
+ return {
31342
+ content: [{
31343
+ type: "text",
31344
+ text: JSON.stringify({
31345
+ success: true,
31346
+ query: params.query,
31347
+ source: params.source,
31348
+ count: 0,
31349
+ results: [],
31350
+ hint: params.source === "lcsc" ? 'Try searching with source: "community" for user-contributed parts' : 'Try searching with source: "lcsc" for official JLCPCB parts'
31351
+ })
31352
+ }]
31353
+ };
31354
+ }
31355
+ const compactResults = results.map((r) => ({
31356
+ lcsc_id: r.lcscId,
31357
+ name: r.name,
31358
+ manufacturer: r.manufacturer,
31359
+ description: r.description,
31360
+ package: r.package,
31361
+ datasheet: r.datasheetPdf,
31362
+ stock: r.stock,
31363
+ price: r.price,
31364
+ library_type: r.libraryType,
31365
+ category: r.category,
31366
+ attributes: r.attributes
31367
+ }));
31368
+ return {
31369
+ content: [{
31370
+ type: "text",
31371
+ text: JSON.stringify({
31372
+ success: true,
31373
+ query: params.query,
31374
+ source: params.source,
31375
+ count: results.length,
31376
+ results: compactResults
31377
+ })
31378
+ }]
31379
+ };
31380
+ }
31381
+ // src/tools/library.ts
31382
+ var libraryInstallTool = {
31383
+ name: "library_install",
31384
+ description: `Install a component to KiCad libraries.
30715
31385
 
30716
31386
  Accepts:
30717
31387
  - LCSC part numbers (e.g., C2040) → global JLC-MCP libraries
@@ -30721,11 +31391,7 @@ LCSC components are routed to category-based global libraries:
30721
31391
  - JLC-MCP-Resistors.kicad_sym, JLC-MCP-Capacitors.kicad_sym, JLC-MCP-ICs.kicad_sym, etc.
30722
31392
  - Stored at ~/Documents/KiCad/{version}/3rdparty/jlc_mcp/
30723
31393
 
30724
- EasyEDA community components are stored project-locally:
30725
- - <project>/libraries/symbols/EasyEDA.kicad_sym
30726
- - <project>/libraries/footprints/EasyEDA.pretty/
30727
-
30728
- Returns symbol_ref and footprint_ref for immediate use with add_schematic_component.`,
31394
+ Returns symbol_ref and footprint_ref for use with schematic placement.`,
30729
31395
  inputSchema: {
30730
31396
  type: "object",
30731
31397
  properties: {
@@ -30739,89 +31405,47 @@ Returns symbol_ref and footprint_ref for immediate use with add_schematic_compon
30739
31405
  },
30740
31406
  include_3d: {
30741
31407
  type: "boolean",
30742
- description: "Include 3D model if available (default: false for LCSC, true for EasyEDA)"
31408
+ description: "Include 3D model if available (default: false)"
31409
+ },
31410
+ force: {
31411
+ type: "boolean",
31412
+ description: "Reinstall even if already exists (default: false)"
30743
31413
  }
30744
31414
  },
30745
31415
  required: ["id"]
30746
31416
  }
30747
31417
  };
30748
- var get3DModelTool = {
30749
- name: "library_get_3d_model",
30750
- description: "Download a 3D model for a component. Requires the model UUID from component_get. Returns the model as base64-encoded STEP data.",
31418
+ var libraryGetComponentTool = {
31419
+ name: "library_get_component",
31420
+ description: `Get metadata for an installed component's symbol and footprint.
31421
+
31422
+ Returns symbol reference, footprint reference, file paths, and pin/pad counts.
31423
+ Does NOT return full file contents to minimize token usage.
31424
+
31425
+ Use this to verify installation or get references for schematic placement.`,
30751
31426
  inputSchema: {
30752
31427
  type: "object",
30753
31428
  properties: {
30754
- uuid: {
30755
- type: "string",
30756
- description: "3D model UUID from component_get result"
30757
- },
30758
- format: {
31429
+ id: {
30759
31430
  type: "string",
30760
- enum: ["step", "obj"],
30761
- description: 'Model format: "step" or "obj" (default: step)'
31431
+ description: "LCSC part number (e.g., C2040)"
30762
31432
  }
30763
31433
  },
30764
- required: ["uuid"]
31434
+ required: ["id"]
30765
31435
  }
30766
31436
  };
30767
- var LibraryParamsSchema = exports_external.object({
30768
- lcsc_id: exports_external.string().regex(/^C\d+$/, "Invalid LCSC part number")
30769
- });
30770
- var FetchLibraryParamsSchema = exports_external.object({
31437
+ var LibraryInstallParamsSchema = exports_external.object({
30771
31438
  id: exports_external.string().min(1),
30772
- project_path: exports_external.string().min(1).optional(),
30773
- include_3d: exports_external.boolean().optional()
31439
+ project_path: SafePathSchema.optional(),
31440
+ include_3d: exports_external.boolean().optional(),
31441
+ force: exports_external.boolean().optional()
30774
31442
  });
30775
- var Model3DParamsSchema = exports_external.object({
30776
- uuid: exports_external.string().min(1),
30777
- format: exports_external.enum(["step", "obj"]).default("step")
31443
+ var LibraryGetComponentParamsSchema = exports_external.object({
31444
+ id: LCSCPartNumberSchema
30778
31445
  });
30779
- async function handleGetSymbolKicad(args) {
30780
- const params = LibraryParamsSchema.parse(args);
30781
- const component = await easyedaClient.getComponentData(params.lcsc_id);
30782
- if (!component) {
30783
- return {
30784
- content: [{
30785
- type: "text",
30786
- text: `Component ${params.lcsc_id} not found`
30787
- }],
30788
- isError: true
30789
- };
30790
- }
30791
- const symbol = symbolConverter.convert(component);
30792
- return {
30793
- content: [{
30794
- type: "text",
30795
- text: symbol
30796
- }]
30797
- };
30798
- }
30799
- async function handleGetFootprintKicad(args) {
30800
- const params = LibraryParamsSchema.parse(args);
30801
- const component = await easyedaClient.getComponentData(params.lcsc_id);
30802
- if (!component) {
30803
- return {
30804
- content: [{
30805
- type: "text",
30806
- text: `Component ${params.lcsc_id} not found`
30807
- }],
30808
- isError: true
30809
- };
30810
- }
30811
- const footprint = footprintConverter.convert(component);
30812
- return {
30813
- content: [{
30814
- type: "text",
30815
- text: footprint
30816
- }]
30817
- };
30818
- }
30819
- function isLcscId3(id) {
30820
- return /^C\d+$/.test(id);
30821
- }
30822
- async function handleFetchLibrary(args) {
30823
- const params = FetchLibraryParamsSchema.parse(args);
30824
- const isCommunityComponent = !isLcscId3(params.id);
31446
+ async function handleLibraryInstall(args) {
31447
+ const params = LibraryInstallParamsSchema.parse(args);
31448
+ const isCommunityComponent = !isLcscId(params.id);
30825
31449
  if (isCommunityComponent && !params.project_path) {
30826
31450
  return {
30827
31451
  content: [{
@@ -30837,9 +31461,10 @@ async function handleFetchLibrary(args) {
30837
31461
  };
30838
31462
  }
30839
31463
  try {
30840
- const result = await libraryService.install(params.id, {
31464
+ const result = await getLibraryService().install(params.id, {
30841
31465
  projectPath: params.project_path,
30842
- include3d: params.include_3d
31466
+ include3d: params.include_3d,
31467
+ force: params.force
30843
31468
  });
30844
31469
  return {
30845
31470
  content: [{
@@ -30847,22 +31472,23 @@ async function handleFetchLibrary(args) {
30847
31472
  text: JSON.stringify({
30848
31473
  success: true,
30849
31474
  id: params.id,
30850
- source: result.source,
30851
- storage_mode: result.storageMode,
30852
- category: result.category,
30853
- symbol_name: result.symbolName,
31475
+ installed: true,
30854
31476
  symbol_ref: result.symbolRef,
30855
31477
  footprint_ref: result.footprintRef,
30856
- footprint_type: result.footprintType,
30857
- datasheet: result.datasheet,
31478
+ category: result.category,
30858
31479
  files: {
30859
31480
  symbol_library: result.files.symbolLibrary,
30860
31481
  footprint: result.files.footprint,
30861
31482
  model_3d: result.files.model3d
30862
31483
  },
30863
- symbol_action: result.symbolAction,
30864
- validation_data: result.validationData
30865
- }, null, 2)
31484
+ validation: result.validationData ? {
31485
+ pin_pad_match: result.validationData.checks.pin_pad_count_match,
31486
+ pin_count: result.validationData.symbol.pin_count,
31487
+ pad_count: result.validationData.footprint.pad_count,
31488
+ has_power_pins: result.validationData.checks.has_power_pins,
31489
+ has_ground_pins: result.validationData.checks.has_ground_pins
31490
+ } : undefined
31491
+ })
30866
31492
  }]
30867
31493
  };
30868
31494
  } catch (error2) {
@@ -30872,76 +31498,202 @@ async function handleFetchLibrary(args) {
30872
31498
  text: JSON.stringify({
30873
31499
  success: false,
30874
31500
  error: error2 instanceof Error ? error2.message : "Unknown error",
30875
- id: params.id,
30876
- source: isCommunityComponent ? "easyeda_community" : "lcsc"
31501
+ id: params.id
30877
31502
  })
30878
31503
  }],
30879
31504
  isError: true
30880
31505
  };
30881
31506
  }
30882
31507
  }
30883
- async function handleGet3DModel(args) {
30884
- const params = Model3DParamsSchema.parse(args);
30885
- const model = await easyedaClient.get3DModel(params.uuid, params.format);
30886
- if (!model) {
31508
+ async function handleLibraryGetComponent(args) {
31509
+ const params = LibraryGetComponentParamsSchema.parse(args);
31510
+ try {
31511
+ const status = await getLibraryService().getStatus();
31512
+ if (!status.installed) {
31513
+ return {
31514
+ content: [{
31515
+ type: "text",
31516
+ text: JSON.stringify({
31517
+ success: false,
31518
+ error: "JLC-MCP libraries not installed. Run library_install first.",
31519
+ id: params.id
31520
+ })
31521
+ }],
31522
+ isError: true
31523
+ };
31524
+ }
31525
+ const installed = await getLibraryService().listInstalled();
31526
+ const component = installed.find((c) => c.lcscId === params.id);
31527
+ if (!component) {
31528
+ return {
31529
+ content: [{
31530
+ type: "text",
31531
+ text: JSON.stringify({
31532
+ success: false,
31533
+ error: `Component ${params.id} is not installed`,
31534
+ id: params.id,
31535
+ hint: "Use library_install to add this component first"
31536
+ })
31537
+ }],
31538
+ isError: true
31539
+ };
31540
+ }
30887
31541
  return {
30888
31542
  content: [{
30889
31543
  type: "text",
30890
- text: `3D model ${params.uuid} not found`
30891
- }],
30892
- isError: true
31544
+ text: JSON.stringify({
31545
+ success: true,
31546
+ id: params.id,
31547
+ installed: true,
31548
+ symbol_ref: component.symbolRef,
31549
+ footprint_ref: component.footprintRef,
31550
+ category: component.category,
31551
+ symbol_library: component.library,
31552
+ name: component.name,
31553
+ has_3d_model: component.has3dModel
31554
+ })
31555
+ }]
30893
31556
  };
31557
+ } catch (error2) {
31558
+ return {
31559
+ content: [{
31560
+ type: "text",
31561
+ text: JSON.stringify({
31562
+ success: false,
31563
+ error: error2 instanceof Error ? error2.message : "Unknown error",
31564
+ id: params.id
31565
+ })
31566
+ }],
31567
+ isError: true
31568
+ };
31569
+ }
31570
+ }
31571
+
31572
+ // src/tools/batch.ts
31573
+ var libraryBatchInstallTool = {
31574
+ name: "library_batch_install",
31575
+ description: `Install multiple components to KiCad libraries in a single call.
31576
+
31577
+ Accepts up to 10 LCSC part numbers. Components are installed in parallel.
31578
+ Returns a summary of installed, skipped (already installed), and failed components.
31579
+
31580
+ Use this when you need to install a bill of materials or multiple components at once.`,
31581
+ inputSchema: {
31582
+ type: "object",
31583
+ properties: {
31584
+ ids: {
31585
+ type: "array",
31586
+ items: { type: "string" },
31587
+ maxItems: 10,
31588
+ description: 'Array of LCSC part numbers (e.g., ["C2040", "C5446", "C14663"])'
31589
+ },
31590
+ force: {
31591
+ type: "boolean",
31592
+ description: "Reinstall even if already exists (default: false)"
31593
+ },
31594
+ include_3d: {
31595
+ type: "boolean",
31596
+ description: "Include 3D models if available (default: false)"
31597
+ }
31598
+ },
31599
+ required: ["ids"]
31600
+ }
31601
+ };
31602
+ var BatchInstallParamsSchema = exports_external.object({
31603
+ ids: exports_external.array(LCSCPartNumberSchema).min(1).max(10),
31604
+ force: exports_external.boolean().optional(),
31605
+ include_3d: exports_external.boolean().optional()
31606
+ });
31607
+ async function handleLibraryBatchInstall(args) {
31608
+ const params = BatchInstallParamsSchema.parse(args);
31609
+ const results = [];
31610
+ let installed = 0;
31611
+ let skipped = 0;
31612
+ let failed = 0;
31613
+ const installPromises = params.ids.map(async (id) => {
31614
+ try {
31615
+ const result = await getLibraryService().install(id, {
31616
+ include3d: params.include_3d,
31617
+ force: params.force
31618
+ });
31619
+ if (result.symbolAction === "exists") {
31620
+ return {
31621
+ id,
31622
+ status: "skipped",
31623
+ symbol_ref: result.symbolRef,
31624
+ footprint_ref: result.footprintRef,
31625
+ reason: "already installed"
31626
+ };
31627
+ }
31628
+ return {
31629
+ id,
31630
+ status: "installed",
31631
+ symbol_ref: result.symbolRef,
31632
+ footprint_ref: result.footprintRef
31633
+ };
31634
+ } catch (error2) {
31635
+ return {
31636
+ id,
31637
+ status: "failed",
31638
+ error: error2 instanceof Error ? error2.message : "Unknown error"
31639
+ };
31640
+ }
31641
+ });
31642
+ const installResults = await Promise.all(installPromises);
31643
+ for (const result of installResults) {
31644
+ results.push(result);
31645
+ if (result.status === "installed")
31646
+ installed++;
31647
+ else if (result.status === "skipped")
31648
+ skipped++;
31649
+ else
31650
+ failed++;
30894
31651
  }
30895
31652
  return {
30896
31653
  content: [{
30897
31654
  type: "text",
30898
- text: `3D model downloaded (${model.length} bytes, ${params.format.toUpperCase()} format)
30899
-
30900
- Base64 data:
30901
- ${model.toString("base64").slice(0, 500)}...`
31655
+ text: JSON.stringify({
31656
+ success: failed < params.ids.length,
31657
+ summary: {
31658
+ total: params.ids.length,
31659
+ installed,
31660
+ skipped,
31661
+ failed
31662
+ },
31663
+ results
31664
+ })
30902
31665
  }]
30903
31666
  };
30904
31667
  }
30905
31668
 
30906
31669
  // src/tools/library-update.ts
30907
- import { existsSync as existsSync4, readdirSync } from "fs";
31670
+ import { existsSync as existsSync5, readdirSync } from "fs";
30908
31671
  import { readFile as readFile5 } from "fs/promises";
30909
- import { homedir as homedir3, platform as platform3 } from "os";
30910
- import { join as join5 } from "path";
30911
- var KICAD_VERSIONS = ["9.0", "8.0"];
31672
+ import { homedir as homedir4, platform as platform4 } from "os";
31673
+ import { join as join7 } from "path";
30912
31674
  var LIBRARY_NAMESPACE3 = "jlc_mcp";
30913
- function detectKicadVersion3() {
30914
- const home = homedir3();
30915
- const baseDir = join5(home, "Documents", "KiCad");
30916
- for (const version2 of KICAD_VERSIONS) {
30917
- if (existsSync4(join5(baseDir, version2))) {
30918
- return version2;
30919
- }
30920
- }
30921
- return "9.0";
30922
- }
30923
31675
  function getLibraryPaths(projectPath) {
30924
31676
  if (projectPath) {
30925
- const librariesDir = join5(projectPath, "libraries");
31677
+ const librariesDir = join7(projectPath, "libraries");
30926
31678
  return {
30927
- symbolsDir: join5(librariesDir, "symbols"),
30928
- footprintDir: join5(librariesDir, "footprints", getFootprintDirName()),
30929
- models3dDir: join5(librariesDir, "3dmodels", get3DModelsDirName())
31679
+ symbolsDir: join7(librariesDir, "symbols"),
31680
+ footprintDir: join7(librariesDir, "footprints", getFootprintDirName()),
31681
+ models3dDir: join7(librariesDir, "3dmodels", get3DModelsDirName())
30930
31682
  };
30931
31683
  }
30932
- const home = homedir3();
30933
- const version2 = detectKicadVersion3();
30934
- const plat = platform3();
31684
+ const home = homedir4();
31685
+ const version2 = detectKicadVersion();
31686
+ const plat = platform4();
30935
31687
  let base;
30936
31688
  if (plat === "linux") {
30937
- base = join5(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31689
+ base = join7(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
30938
31690
  } else {
30939
- base = join5(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31691
+ base = join7(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
30940
31692
  }
30941
31693
  return {
30942
- symbolsDir: join5(base, "symbols"),
30943
- footprintDir: join5(base, "footprints", getFootprintDirName()),
30944
- models3dDir: join5(base, "3dmodels", get3DModelsDirName())
31694
+ symbolsDir: join7(base, "symbols"),
31695
+ footprintDir: join7(base, "footprints", getFootprintDirName()),
31696
+ models3dDir: join7(base, "3dmodels", get3DModelsDirName())
30945
31697
  };
30946
31698
  }
30947
31699
  function extractLcscIdsFromLibrary(content) {
@@ -30962,12 +31714,12 @@ function generateEmptyLibrary() {
30962
31714
  `;
30963
31715
  }
30964
31716
  function findJlcLibraries(symbolsDir) {
30965
- if (!existsSync4(symbolsDir)) {
31717
+ if (!existsSync5(symbolsDir)) {
30966
31718
  return [];
30967
31719
  }
30968
31720
  try {
30969
31721
  const files = readdirSync(symbolsDir);
30970
- return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join5(symbolsDir, f));
31722
+ return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join7(symbolsDir, f));
30971
31723
  } catch {
30972
31724
  return [];
30973
31725
  }
@@ -31034,7 +31786,7 @@ async function handleUpdateLibrary(args) {
31034
31786
  libraries_to_create: allCategories.map((cat) => ({
31035
31787
  category: cat,
31036
31788
  filename: getLibraryFilename(cat),
31037
- path: join5(paths.symbolsDir, getLibraryFilename(cat))
31789
+ path: join7(paths.symbolsDir, getLibraryFilename(cat))
31038
31790
  }))
31039
31791
  }, null, 2)
31040
31792
  }]
@@ -31047,7 +31799,7 @@ async function handleUpdateLibrary(args) {
31047
31799
  const createdLibraries = [];
31048
31800
  for (const category of allCategories) {
31049
31801
  const filename = getLibraryFilename(category);
31050
- const filepath = join5(paths.symbolsDir, filename);
31802
+ const filepath = join7(paths.symbolsDir, filename);
31051
31803
  await writeText(filepath, emptyContent);
31052
31804
  createdLibraries.push(filepath);
31053
31805
  }
@@ -31124,7 +31876,7 @@ async function handleUpdateLibrary(args) {
31124
31876
  if (!params.dry_run && footprintResult.type === "generated") {
31125
31877
  await ensureDir(paths.footprintDir);
31126
31878
  const footprintName = footprintResult.name + "_" + lcscId;
31127
- const footprintPath = join5(paths.footprintDir, `${footprintName}.kicad_mod`);
31879
+ const footprintPath = join7(paths.footprintDir, `${footprintName}.kicad_mod`);
31128
31880
  await writeText(footprintPath, footprintResult.content);
31129
31881
  }
31130
31882
  results.push({
@@ -31151,7 +31903,7 @@ async function handleUpdateLibrary(args) {
31151
31903
  await ensureDir(paths.symbolsDir);
31152
31904
  for (const [category, entries] of categorySymbols) {
31153
31905
  const filename = getLibraryFilename(category);
31154
- const filepath = join5(paths.symbolsDir, filename);
31906
+ const filepath = join7(paths.symbolsDir, filename);
31155
31907
  const header = `(kicad_symbol_lib
31156
31908
  (version 20241209)
31157
31909
  (generator "jlc-mcp")
@@ -31179,53 +31931,41 @@ async function handleUpdateLibrary(args) {
31179
31931
  success: true,
31180
31932
  dry_run: params.dry_run,
31181
31933
  summary: {
31182
- total_components: allLcscIds.size,
31934
+ total: allLcscIds.size,
31183
31935
  updated: successful.length,
31184
- failed: failed.length,
31185
- libraries_generated: categorySymbols.size
31936
+ failed: failed.length
31186
31937
  },
31187
31938
  by_category: Object.fromEntries(byCategory),
31188
31939
  footprint_stats: footprintStats,
31189
- libraries_written: params.dry_run ? [] : Array.from(categorySymbols.keys()).map((cat) => join5(paths.symbolsDir, getLibraryFilename(cat))),
31190
- failed_components: failed.map((f) => ({
31940
+ failed_sample: failed.slice(0, 5).map((f) => ({
31191
31941
  lcsc_id: f.lcscId,
31192
31942
  error: f.error
31193
- }))
31194
- }, null, 2)
31943
+ })),
31944
+ has_more_failures: failed.length > 5
31945
+ })
31195
31946
  }]
31196
31947
  };
31197
31948
  }
31198
31949
 
31199
31950
  // src/tools/library-fix.ts
31200
- import { existsSync as existsSync6 } from "fs";
31951
+ import { existsSync as existsSync7 } from "fs";
31201
31952
  import { readFile as readFile7 } from "fs/promises";
31202
- import { homedir as homedir5 } from "os";
31203
- import { join as join7 } from "path";
31204
- var KICAD_VERSIONS4 = ["9.0", "8.0"];
31205
- function detectKicadVersion5() {
31206
- const home = homedir5();
31207
- const baseDir = join7(home, "Documents", "KiCad");
31208
- for (const version2 of KICAD_VERSIONS4) {
31209
- if (existsSync6(join7(baseDir, version2))) {
31210
- return version2;
31211
- }
31212
- }
31213
- return "9.0";
31214
- }
31953
+ import { homedir as homedir6 } from "os";
31954
+ import { join as join9 } from "path";
31215
31955
  function getLibraryPaths2(projectPath) {
31216
31956
  if (projectPath) {
31217
- const librariesDir = join7(projectPath, "libraries");
31957
+ const librariesDir = join9(projectPath, "libraries");
31218
31958
  return {
31219
- symbolsDir: join7(librariesDir, "symbols"),
31220
- footprintDir: join7(librariesDir, "footprints", getFootprintDirName())
31959
+ symbolsDir: join9(librariesDir, "symbols"),
31960
+ footprintDir: join9(librariesDir, "footprints", getFootprintDirName())
31221
31961
  };
31222
31962
  }
31223
- const home = homedir5();
31224
- const version2 = detectKicadVersion5();
31225
- const base = join7(home, "Documents", "KiCad", version2);
31963
+ const home = homedir6();
31964
+ const version2 = detectKicadVersion();
31965
+ const base = join9(home, "Documents", "KiCad", version2);
31226
31966
  return {
31227
- symbolsDir: join7(base, "symbols"),
31228
- footprintDir: join7(base, "footprints", getFootprintDirName())
31967
+ symbolsDir: join9(base, "symbols"),
31968
+ footprintDir: join9(base, "footprints", getFootprintDirName())
31229
31969
  };
31230
31970
  }
31231
31971
  var PinElectricalType = exports_external.enum([
@@ -31393,8 +32133,8 @@ async function handleFixLibrary(args) {
31393
32133
  }
31394
32134
  const category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
31395
32135
  const symbolLibraryFilename = getLibraryFilename(category);
31396
- const symbolFile = join7(paths.symbolsDir, symbolLibraryFilename);
31397
- if (!existsSync6(symbolFile) && !params.force) {
32136
+ const symbolFile = join9(paths.symbolsDir, symbolLibraryFilename);
32137
+ if (!existsSync7(symbolFile) && !params.force) {
31398
32138
  return {
31399
32139
  content: [{
31400
32140
  type: "text",
@@ -31408,7 +32148,7 @@ async function handleFixLibrary(args) {
31408
32148
  isError: true
31409
32149
  };
31410
32150
  }
31411
- if (existsSync6(symbolFile) && !params.force) {
32151
+ if (existsSync7(symbolFile) && !params.force) {
31412
32152
  const existingContent = await readFile7(symbolFile, "utf-8");
31413
32153
  if (!symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
31414
32154
  return {
@@ -31495,14 +32235,14 @@ async function handleFixLibrary(args) {
31495
32235
  const footprintName = footprintResult.name + "_" + params.lcsc_id;
31496
32236
  footprintRef = getFootprintReference2(footprintName);
31497
32237
  await ensureDir(paths.footprintDir);
31498
- const footprintPath = join7(paths.footprintDir, `${footprintName}.kicad_mod`);
32238
+ const footprintPath = join9(paths.footprintDir, `${footprintName}.kicad_mod`);
31499
32239
  await writeText(footprintPath, footprintResult.content);
31500
32240
  }
31501
32241
  component.info.package = footprintRef;
31502
32242
  await ensureDir(paths.symbolsDir);
31503
32243
  let symbolContent;
31504
32244
  let symbolAction;
31505
- if (existsSync6(symbolFile)) {
32245
+ if (existsSync7(symbolFile)) {
31506
32246
  const existingContent = await readFile7(symbolFile, "utf-8");
31507
32247
  symbolContent = symbolConverter.replaceInLibrary(existingContent, component);
31508
32248
  symbolAction = "replaced";
@@ -31531,711 +32271,43 @@ async function handleFixLibrary(args) {
31531
32271
  };
31532
32272
  }
31533
32273
 
31534
- // src/tools/easyeda.ts
31535
- import { join as join8 } from "path";
31536
- import { execSync as execSync2 } from "child_process";
31537
- import { tmpdir } from "os";
31538
- var easyedaSearchTool = {
31539
- name: "easyeda_search",
31540
- description: "Search EasyEDA community library for user-contributed symbols and footprints. Use this for parts not in LCSC official library (e.g., XIAO, Arduino modules, custom breakouts). Returns results with UUIDs and optionally opens an HTML preview.",
31541
- inputSchema: {
31542
- type: "object",
31543
- properties: {
31544
- query: {
31545
- type: "string",
31546
- description: 'Search term (e.g., "XIAO RP2040", "ESP32-C3 module")'
31547
- },
31548
- source: {
31549
- type: "string",
31550
- enum: ["user", "lcsc", "easyeda", "all"],
31551
- description: 'Filter by source. "user" for community-contributed (default)'
31552
- },
31553
- limit: {
31554
- type: "number",
31555
- description: "Max results to return (default: 20)"
31556
- },
31557
- open_preview: {
31558
- type: "boolean",
31559
- description: "Generate and open HTML preview in browser",
31560
- default: true
31561
- }
31562
- },
31563
- required: ["query"]
31564
- }
31565
- };
31566
- var easyedaGet3DModelTool = {
31567
- name: "easyeda_get_3d_model",
31568
- description: "Download 3D model for an EasyEDA community component. Requires the model UUID from easyeda_get.",
31569
- inputSchema: {
31570
- type: "object",
31571
- properties: {
31572
- uuid: {
31573
- type: "string",
31574
- description: "3D model UUID from easyeda_get result"
31575
- },
31576
- format: {
31577
- type: "string",
31578
- enum: ["step", "obj"],
31579
- description: 'Model format: "step" or "obj" (default: step)'
31580
- }
31581
- },
31582
- required: ["uuid"]
31583
- }
31584
- };
31585
- var EasyedaSearchParamsSchema = exports_external.object({
31586
- query: exports_external.string().min(1),
31587
- source: exports_external.enum(["user", "lcsc", "easyeda", "all"]).optional(),
31588
- limit: exports_external.number().min(1).max(100).optional(),
31589
- open_preview: exports_external.boolean().optional()
31590
- });
31591
- var EasyedaGet3DModelParamsSchema = exports_external.object({
31592
- uuid: exports_external.string().min(1),
31593
- format: exports_external.enum(["step", "obj"]).default("step")
31594
- });
31595
- async function handleEasyedaSearch(args) {
31596
- const params = EasyedaSearchParamsSchema.parse(args);
31597
- const openPreview = params.open_preview ?? true;
31598
- const results = await easyedaCommunityClient.search({
31599
- query: params.query,
31600
- source: params.source,
31601
- limit: params.limit || 20
31602
- });
31603
- if (results.length === 0) {
31604
- return {
31605
- content: [
31606
- {
31607
- type: "text",
31608
- text: `No results found for "${params.query}"`
31609
- }
31610
- ]
31611
- };
31612
- }
31613
- let output = `Found ${results.length} results for "${params.query}":
31614
-
31615
- `;
31616
- output += `| # | Title | Package | Owner | UUID |
31617
- `;
31618
- output += `|---|-------|---------|-------|------|
31619
- `;
31620
- for (let i = 0;i < results.length; i++) {
31621
- const r = results[i];
31622
- output += `| ${i + 1} | ${r.title} | ${r.package} | ${r.owner.nickname || r.owner.username} | ${r.uuid} |
31623
- `;
31624
- }
31625
- output += "\nUse `library_fetch` with the UUID to add component to global JLC-MCP libraries.";
31626
- output += "\nUse `easyeda_fetch` with the UUID to add to project-local EasyEDA library.";
31627
- if (openPreview) {
31628
- const { filepath, browserOpened } = await generateHtmlPreview(params.query, results);
31629
- if (browserOpened) {
31630
- output += `
31631
-
31632
- HTML preview opened in browser.`;
31633
- } else {
31634
- output += `
31635
-
31636
- Could not open browser automatically.`;
31637
- }
31638
- output += `
31639
- Preview file: ${filepath}`;
31640
- }
31641
- return {
31642
- content: [
31643
- {
31644
- type: "text",
31645
- text: output
31646
- }
31647
- ]
31648
- };
31649
- }
31650
- async function handleEasyedaGet3DModel(args) {
31651
- const params = EasyedaGet3DModelParamsSchema.parse(args);
31652
- const model = await easyedaCommunityClient.get3DModel(params.uuid, params.format);
31653
- if (!model) {
31654
- return {
31655
- content: [
31656
- {
31657
- type: "text",
31658
- text: `3D model ${params.uuid} not found`
31659
- }
31660
- ],
31661
- isError: true
31662
- };
31663
- }
31664
- return {
31665
- content: [
31666
- {
31667
- type: "text",
31668
- text: `3D model downloaded (${model.length} bytes, ${params.format.toUpperCase()} format)
31669
-
31670
- Base64 data:
31671
- ${model.toString("base64").slice(0, 500)}...`
31672
- }
31673
- ]
31674
- };
31675
- }
31676
- async function generateHtmlPreview(query, results) {
31677
- const timestamp = Date.now();
31678
- const filename = `easyeda-search-${timestamp}.html`;
31679
- const filepath = join8(tmpdir(), filename);
31680
- const noImageSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 150" style="background:#2a2a2a"><text x="100" y="75" text-anchor="middle" fill="#666" font-size="12">No Preview</text></svg>`;
31681
- const noImageDataUri = `data:image/svg+xml,${encodeURIComponent(noImageSvg)}`;
31682
- const cardsPromises = results.slice(0, 10).map(async (r) => {
31683
- const symbolImageUrl = `https://image.easyeda.com/components/${r.uuid}.png`;
31684
- let footprintSvg = "";
31685
- try {
31686
- const component = await easyedaCommunityClient.getComponent(r.uuid);
31687
- if (component) {
31688
- const rawData = component.rawData;
31689
- const fpDataStr = rawData?.packageDetail?.dataStr || rawData?.dataStr;
31690
- if (fpDataStr?.shape) {
31691
- footprintSvg = generateFootprintSvg(fpDataStr);
31692
- }
31693
- }
31694
- } catch {}
31695
- const footprintDataUri = footprintSvg ? `data:image/svg+xml,${encodeURIComponent(footprintSvg)}` : noImageDataUri;
31696
- return `
31697
- <div class="card">
31698
- <div class="images">
31699
- <div class="image-box">
31700
- <div class="image-label">Symbol</div>
31701
- <img src="${symbolImageUrl}" alt="Symbol" onerror="this.src='${noImageDataUri}'">
31702
- </div>
31703
- <div class="image-box">
31704
- <div class="image-label">Footprint</div>
31705
- <img src="${footprintDataUri}" alt="Footprint">
31706
- </div>
31707
- </div>
31708
- <h3>${escapeHtml(r.title)}</h3>
31709
- <div class="meta">
31710
- <div><strong>Package:</strong> ${escapeHtml(r.package || "N/A")}</div>
31711
- <div><strong>Owner:</strong> ${escapeHtml(r.owner.nickname || r.owner.username)}</div>
31712
- ${r.manufacturer ? `<div><strong>Mfr:</strong> ${escapeHtml(r.manufacturer)}</div>` : ""}
31713
- </div>
31714
- <div class="uuid" onclick="navigator.clipboard.writeText('${r.uuid}'); this.classList.add('copied'); setTimeout(() => this.classList.remove('copied'), 1000);">
31715
- ${r.uuid}
31716
- </div>
31717
- </div>`;
31718
- });
31719
- const cards = (await Promise.all(cardsPromises)).join(`
31720
- `);
31721
- const html = `<!DOCTYPE html>
31722
- <html lang="en">
31723
- <head>
31724
- <meta charset="UTF-8">
31725
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
31726
- <title>EasyEDA Search: ${escapeHtml(query)}</title>
31727
- <style>
31728
- * { box-sizing: border-box; }
31729
- body {
31730
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
31731
- max-width: 1400px;
31732
- margin: 0 auto;
31733
- padding: 20px;
31734
- background: #f9f9f9;
31735
- color: #333;
31736
- }
31737
- h1 { margin-bottom: 8px; }
31738
- .subtitle { color: #666; margin-bottom: 20px; }
31739
- .grid {
31740
- display: grid;
31741
- grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
31742
- gap: 16px;
31743
- }
31744
- .card {
31745
- background: white;
31746
- border: 1px solid #ddd;
31747
- border-radius: 8px;
31748
- padding: 16px;
31749
- transition: box-shadow 0.2s;
31750
- }
31751
- .card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
31752
- .card .images {
31753
- display: flex;
31754
- gap: 8px;
31755
- margin-bottom: 12px;
31756
- }
31757
- .card .image-box {
31758
- flex: 1;
31759
- min-width: 0;
31760
- }
31761
- .card .image-label {
31762
- font-size: 10px;
31763
- color: #888;
31764
- text-transform: uppercase;
31765
- text-align: center;
31766
- margin-bottom: 4px;
31767
- }
31768
- .card img {
31769
- width: 100%;
31770
- height: 120px;
31771
- object-fit: contain;
31772
- border-radius: 4px;
31773
- border: 1px solid #eee;
31774
- }
31775
- .card h3 {
31776
- margin: 0 0 8px;
31777
- font-size: 15px;
31778
- line-height: 1.3;
31779
- overflow: hidden;
31780
- text-overflow: ellipsis;
31781
- white-space: nowrap;
31782
- }
31783
- .card .meta {
31784
- color: #666;
31785
- font-size: 12px;
31786
- line-height: 1.6;
31787
- }
31788
- .card .meta div { margin-bottom: 2px; }
31789
- .card .uuid {
31790
- font-family: 'SF Mono', Monaco, 'Courier New', monospace;
31791
- font-size: 10px;
31792
- color: #888;
31793
- background: #f5f5f5;
31794
- padding: 6px 8px;
31795
- border-radius: 4px;
31796
- margin-top: 12px;
31797
- cursor: pointer;
31798
- word-break: break-all;
31799
- transition: background 0.2s;
31800
- }
31801
- .card .uuid:hover { background: #e8e8e8; }
31802
- .card .uuid.copied { background: #d4edda; color: #155724; }
31803
- .instructions {
31804
- background: #e8f4fd;
31805
- border: 1px solid #b8daff;
31806
- border-radius: 8px;
31807
- padding: 16px;
31808
- margin-bottom: 20px;
31809
- font-size: 14px;
31810
- }
31811
- .instructions code {
31812
- background: #fff;
31813
- padding: 2px 6px;
31814
- border-radius: 4px;
31815
- font-family: 'SF Mono', Monaco, monospace;
31816
- }
31817
- </style>
31818
- </head>
31819
- <body>
31820
- <h1>EasyEDA Search: "${escapeHtml(query)}"</h1>
31821
- <p class="subtitle">Found ${results.length} results. Click UUID to copy to clipboard.</p>
31822
-
31823
- <div class="instructions">
31824
- <strong>How to use:</strong><br>
31825
- 1. Click on a UUID to copy it<br>
31826
- 2. Use <code>library_fetch</code> with the UUID to add to global JLC-MCP libraries<br>
31827
- 3. Or use <code>easyeda_fetch</code> for project-local EasyEDA library
31828
- </div>
31829
-
31830
- <div class="grid">
31831
- ${cards}
31832
- </div>
31833
- </body>
31834
- </html>`;
31835
- __require("fs").writeFileSync(filepath, html, "utf-8");
31836
- const browserOpened = openInBrowser(filepath);
31837
- return { filepath, browserOpened };
31838
- }
31839
- function openInBrowser(filepath) {
31840
- const platform4 = process.platform;
31841
- try {
31842
- switch (platform4) {
31843
- case "darwin":
31844
- execSync2(`open "${filepath}"`, { stdio: "ignore" });
31845
- return true;
31846
- case "win32":
31847
- execSync2(`start "" "${filepath}"`, { stdio: "ignore", shell: "cmd.exe" });
31848
- return true;
31849
- case "linux":
31850
- default:
31851
- execSync2(`xdg-open "${filepath}"`, { stdio: "ignore" });
31852
- return true;
31853
- }
31854
- } catch {
31855
- const fallbacks = ["xdg-open", "sensible-browser", "x-www-browser", "gnome-open"];
31856
- for (const cmd of fallbacks) {
31857
- try {
31858
- execSync2(`${cmd} "${filepath}"`, { stdio: "ignore" });
31859
- return true;
31860
- } catch {}
31861
- }
31862
- return false;
31863
- }
31864
- }
31865
- function escapeHtml(str) {
31866
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
31867
- }
31868
- function generateFootprintSvg(dataStr) {
31869
- if (!dataStr.shape || dataStr.shape.length === 0) {
31870
- return "";
31871
- }
31872
- const bbox = dataStr.BBox || { x: 0, y: 0, width: 100, height: 100 };
31873
- const padding = 5;
31874
- const viewBox = `${bbox.x - padding} ${bbox.y - padding} ${bbox.width + padding * 2} ${bbox.height + padding * 2}`;
31875
- const regions = [];
31876
- const tracks = [];
31877
- const pads = [];
31878
- const holes = [];
31879
- const texts = [];
31880
- for (const shape of dataStr.shape) {
31881
- if (typeof shape !== "string")
31882
- continue;
31883
- if (shape.startsWith("SOLIDREGION~")) {
31884
- const svg = renderSolidRegion(shape);
31885
- if (svg)
31886
- regions.push(svg);
31887
- } else if (shape.startsWith("TRACK~")) {
31888
- const svg = renderTrackShape(shape);
31889
- if (svg)
31890
- tracks.push(svg);
31891
- } else if (shape.startsWith("PAD~")) {
31892
- const result = renderPadShape(shape);
31893
- if (result) {
31894
- pads.push(result.pad);
31895
- if (result.hole)
31896
- holes.push(result.hole);
31897
- }
31898
- } else if (shape.startsWith("TEXT~")) {
31899
- const svg = renderTextShape(shape);
31900
- if (svg)
31901
- texts.push(svg);
31902
- }
31903
- }
31904
- const allElements = [...regions, ...tracks, ...pads, ...holes, ...texts];
31905
- if (allElements.length === 0) {
31906
- return "";
31907
- }
31908
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" style="background:#000000">
31909
- <style>
31910
- .pad { fill: #CC0000; stroke: none; }
31911
- .pad-hole { fill: #666666; }
31912
- .track { fill: none; stroke: #FFFF00; stroke-linecap: round; stroke-linejoin: round; }
31913
- .region { fill: #CC0000; opacity: 0.6; }
31914
- .text-path { fill: none; stroke: #FFFF00; stroke-width: 0.4; stroke-linecap: round; stroke-linejoin: round; }
31915
- </style>
31916
- ${allElements.join(`
31917
- `)}
31918
- </svg>`;
31919
- }
31920
- function renderPadShape(padData) {
31921
- const fields = padData.split("~");
31922
- const shapeType = fields[1];
31923
- const cx = parseFloat(fields[2]) || 0;
31924
- const cy = parseFloat(fields[3]) || 0;
31925
- if (shapeType === "POLYGON") {
31926
- const holeDia2 = parseFloat(fields[9]) || 0;
31927
- const pointsStr = fields[10] || "";
31928
- if (!pointsStr)
31929
- return null;
31930
- const coords = pointsStr.split(" ").map(Number);
31931
- if (coords.length < 4)
31932
- return null;
31933
- let pathD = `M ${coords[0]} ${coords[1]}`;
31934
- for (let i = 2;i < coords.length; i += 2) {
31935
- pathD += ` L ${coords[i]} ${coords[i + 1]}`;
31936
- }
31937
- pathD += " Z";
31938
- return {
31939
- pad: `<path class="pad" d="${pathD}"/>`,
31940
- hole: holeDia2 > 0 ? `<circle class="pad-hole" cx="${cx}" cy="${cy}" r="${holeDia2}"/>` : null
31941
- };
31942
- }
31943
- const width = parseFloat(fields[4]) || 0;
31944
- const height = parseFloat(fields[5]) || 0;
31945
- const holeDia = parseFloat(fields[9]) || 0;
31946
- let padSvg = "";
31947
- if (shapeType === "ELLIPSE" || shapeType === "OVAL" || shapeType === "ROUND") {
31948
- const rx = width / 2;
31949
- const ry = height / 2;
31950
- padSvg = `<ellipse class="pad" cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}"/>`;
31951
- } else {
31952
- const rectX = cx - width / 2;
31953
- const rectY = cy - height / 2;
31954
- padSvg = `<rect class="pad" x="${rectX}" y="${rectY}" width="${width}" height="${height}"/>`;
31955
- }
31956
- return {
31957
- pad: padSvg,
31958
- hole: holeDia > 0 ? `<circle class="pad-hole" cx="${cx}" cy="${cy}" r="${holeDia}"/>` : null
31959
- };
31960
- }
31961
- function renderTrackShape(trackData) {
31962
- const fields = trackData.split("~");
31963
- const strokeWidth = parseFloat(fields[1]) || 0.5;
31964
- const pointsStr = fields[4] || "";
31965
- if (!pointsStr)
31966
- return null;
31967
- const coords = pointsStr.split(" ").map(Number);
31968
- if (coords.length < 4)
31969
- return null;
31970
- let pathD = `M ${coords[0]} ${coords[1]}`;
31971
- for (let i = 2;i < coords.length; i += 2) {
31972
- pathD += ` L ${coords[i]} ${coords[i + 1]}`;
31973
- }
31974
- return `<path class="track" d="${pathD}" stroke-width="${strokeWidth}"/>`;
31975
- }
31976
- function renderSolidRegion(regionData) {
31977
- const fields = regionData.split("~");
31978
- const pathD = fields[3] || "";
31979
- if (!pathD || !pathD.startsWith("M"))
31980
- return null;
31981
- return `<path class="region" d="${pathD}"/>`;
31982
- }
31983
- function renderTextShape(textData) {
31984
- const fields = textData.split("~");
31985
- const svgPath = fields[11] || "";
31986
- if (!svgPath || !svgPath.startsWith("M"))
31987
- return null;
31988
- return `<path class="text-path" d="${svgPath}"/>`;
31989
- }
31990
-
31991
32274
  // src/tools/index.ts
31992
32275
  var tools = [
31993
- searchComponentsTool,
31994
- getComponentTool,
31995
- getSymbolKicadTool,
31996
- getFootprintKicadTool,
31997
- fetchLibraryTool,
32276
+ componentSearchTool,
32277
+ libraryInstallTool,
32278
+ libraryBatchInstallTool,
32279
+ libraryGetComponentTool,
31998
32280
  updateLibraryTool,
31999
- fixLibraryTool,
32000
- get3DModelTool,
32001
- easyedaSearchTool,
32002
- easyedaGet3DModelTool
32281
+ fixLibraryTool
32003
32282
  ];
32004
32283
  var toolHandlers = {
32005
- component_search: handleSearchComponents,
32006
- component_get: handleGetComponent,
32007
- library_get_symbol: handleGetSymbolKicad,
32008
- library_get_footprint: handleGetFootprintKicad,
32009
- library_fetch: handleFetchLibrary,
32010
- library_update: handleUpdateLibrary,
32011
- library_fix: handleFixLibrary,
32012
- library_get_3d_model: handleGet3DModel,
32013
- easyeda_search: handleEasyedaSearch,
32014
- easyeda_get_3d_model: handleEasyedaGet3DModel
32284
+ [componentSearchTool.name]: handleComponentSearch,
32285
+ [libraryInstallTool.name]: handleLibraryInstall,
32286
+ [libraryBatchInstallTool.name]: handleLibraryBatchInstall,
32287
+ [libraryGetComponentTool.name]: handleLibraryGetComponent,
32288
+ [updateLibraryTool.name]: handleUpdateLibrary,
32289
+ [fixLibraryTool.name]: handleFixLibrary
32015
32290
  };
32016
32291
 
32017
- // src/http/server.ts
32018
- import { createServer } from "http";
32019
-
32020
- // src/http/routes.ts
32021
- import { readFileSync } from "fs";
32022
- import { join as join9, dirname as dirname2 } from "path";
32023
- import { fileURLToPath } from "url";
32024
- var logger2 = createLogger("http-routes");
32025
- var __filename2 = fileURLToPath(import.meta.url);
32026
- var __dirname2 = dirname2(__filename2);
32027
- var htmlCache = null;
32028
- function getHtmlPage() {
32029
- if (htmlCache)
32030
- return htmlCache;
32031
- try {
32032
- const possiblePaths = [
32033
- join9(__dirname2, "assets/search.html"),
32034
- join9(__dirname2, "../dist/assets/search.html"),
32035
- join9(__dirname2, "../assets/search.html"),
32036
- join9(__dirname2, "../assets/search-built.html"),
32037
- join9(process.cwd(), "dist/assets/search.html"),
32038
- join9(process.cwd(), "packages/jlc-mcp/dist/assets/search.html")
32039
- ];
32040
- for (const path of possiblePaths) {
32041
- try {
32042
- htmlCache = readFileSync(path, "utf-8");
32043
- logger2.debug(`Loaded HTML from: ${path}`);
32044
- return htmlCache;
32045
- } catch {}
32046
- }
32047
- throw new Error("HTML file not found");
32048
- } catch (error2) {
32049
- logger2.error("Failed to load HTML page:", error2);
32050
- return `<!DOCTYPE html>
32051
- <html>
32052
- <head><title>Error</title></head>
32053
- <body>
32054
- <h1>Error: Search page not found</h1>
32055
- <p>The search page has not been built. Run: bun run build</p>
32056
- </body>
32057
- </html>`;
32058
- }
32059
- }
32060
- async function handleRequest(req, res) {
32061
- const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
32062
- const pathname = url.pathname;
32063
- logger2.debug(`${req.method} ${pathname}`);
32064
- if (pathname === "/" || pathname === "/index.html") {
32065
- serveHtml(res);
32066
- } else if (pathname === "/api/search") {
32067
- await handleSearch(url, res);
32068
- } else if (pathname.startsWith("/api/component/")) {
32069
- const uuid2 = pathname.replace("/api/component/", "");
32070
- await handleComponent(uuid2, res);
32071
- } else if (pathname === "/health") {
32072
- res.writeHead(200, { "Content-Type": "application/json" });
32073
- res.end(JSON.stringify({ status: "ok" }));
32074
- } else {
32075
- res.writeHead(404, { "Content-Type": "application/json" });
32076
- res.end(JSON.stringify({ error: "Not found" }));
32077
- }
32078
- }
32079
- function serveHtml(res) {
32080
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
32081
- res.end(getHtmlPage());
32082
- }
32083
- async function handleSearch(url, res) {
32084
- const query = url.searchParams.get("q") || "";
32085
- const source = url.searchParams.get("source") || "user";
32086
- const page = parseInt(url.searchParams.get("page") || "1", 10);
32087
- const limit = parseInt(url.searchParams.get("limit") || "20", 10);
32088
- if (!query) {
32089
- res.writeHead(400, { "Content-Type": "application/json" });
32090
- res.end(JSON.stringify({ error: "Missing query parameter" }));
32091
- return;
32092
- }
32093
- try {
32094
- const allResults = await easyedaCommunityClient.search({
32095
- query,
32096
- source,
32097
- limit: Math.min(limit * page + limit, 100)
32098
- });
32099
- const startIndex = (page - 1) * limit;
32100
- const endIndex = startIndex + limit;
32101
- const results = allResults.slice(startIndex, endIndex);
32102
- const totalPages = Math.ceil(allResults.length / limit);
32103
- res.writeHead(200, { "Content-Type": "application/json" });
32104
- res.end(JSON.stringify({
32105
- results,
32106
- pagination: {
32107
- page,
32108
- limit,
32109
- total: allResults.length,
32110
- totalPages,
32111
- hasNext: page < totalPages,
32112
- hasPrev: page > 1
32113
- }
32114
- }));
32115
- } catch (error2) {
32116
- logger2.error("Search error:", error2);
32117
- res.writeHead(500, { "Content-Type": "application/json" });
32118
- res.end(JSON.stringify({ error: "Search failed" }));
32119
- }
32120
- }
32121
- function communityToComponentData(community) {
32122
- const cPara = community.symbol.head?.c_para ?? {};
32123
- const fpCPara = community.footprint.head?.c_para ?? {};
32124
- return {
32125
- info: {
32126
- name: community.title || cPara.name || "Unknown",
32127
- prefix: cPara.pre || cPara.Prefix || "U",
32128
- package: fpCPara.package || community.footprint.name,
32129
- manufacturer: cPara.Manufacturer || cPara.BOM_Manufacturer,
32130
- description: community.description || cPara.BOM_Manufacturer_Part,
32131
- category: cPara.package
32132
- },
32133
- symbol: community.symbol,
32134
- footprint: community.footprint,
32135
- model3d: community.model3d,
32136
- rawData: community.rawData
32137
- };
32138
- }
32139
- async function handleComponent(uuid2, res) {
32140
- if (!uuid2) {
32141
- res.writeHead(400, { "Content-Type": "application/json" });
32142
- res.end(JSON.stringify({ error: "Missing UUID" }));
32143
- return;
32144
- }
32145
- try {
32146
- const component = await easyedaCommunityClient.getComponent(uuid2);
32147
- if (!component) {
32148
- res.writeHead(404, { "Content-Type": "application/json" });
32149
- res.end(JSON.stringify({ error: "Component not found" }));
32150
- return;
32151
- }
32152
- const componentData = communityToComponentData(component);
32153
- let symbolSexpr = "";
32154
- let footprintSexpr = "";
32155
- try {
32156
- symbolSexpr = symbolConverter.convertToSymbolEntry(componentData);
32157
- } catch (e) {
32158
- logger2.warn("Symbol conversion failed:", e);
32159
- }
32160
- try {
32161
- footprintSexpr = footprintConverter.convert(componentData);
32162
- } catch (e) {
32163
- logger2.warn("Footprint conversion failed:", e);
32164
- }
32165
- res.writeHead(200, { "Content-Type": "application/json" });
32166
- res.end(JSON.stringify({
32167
- uuid: component.uuid,
32168
- title: component.title,
32169
- description: component.description,
32170
- owner: component.owner,
32171
- symbolSexpr,
32172
- footprintSexpr,
32173
- model3d: component.model3d
32174
- }));
32175
- } catch (error2) {
32176
- logger2.error("Component fetch error:", error2);
32177
- res.writeHead(500, { "Content-Type": "application/json" });
32178
- res.end(JSON.stringify({ error: "Failed to fetch component" }));
32179
- }
32180
- }
32181
-
32182
- // src/http/server.ts
32183
- var logger7 = createLogger("http-server");
32184
- var DEFAULT_PORT = 3847;
32185
- var serverInstance = null;
32186
- function startHttpServer() {
32187
- if (serverInstance) {
32188
- logger7.debug("HTTP server already running");
32189
- return DEFAULT_PORT;
32190
- }
32191
- const port = parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
32192
- serverInstance = createServer(async (req, res) => {
32193
- res.setHeader("Access-Control-Allow-Origin", "*");
32194
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
32195
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
32196
- if (req.method === "OPTIONS") {
32197
- res.writeHead(204);
32198
- res.end();
32199
- return;
32200
- }
32201
- try {
32202
- await handleRequest(req, res);
32203
- } catch (error2) {
32204
- logger7.error("Request error:", error2);
32205
- res.writeHead(500, { "Content-Type": "application/json" });
32206
- res.end(JSON.stringify({ error: "Internal server error" }));
32207
- }
32208
- });
32209
- serverInstance.listen(port, () => {
32210
- logger7.info(`HTTP server listening on http://localhost:${port}`);
32211
- });
32212
- serverInstance.on("error", (error2) => {
32213
- if (error2.code === "EADDRINUSE") {
32214
- logger7.warn(`Port ${port} already in use, HTTP server not started`);
32215
- } else {
32216
- logger7.error("HTTP server error:", error2);
32217
- }
32218
- });
32219
- return port;
32220
- }
32221
-
32222
32292
  // src/index.ts
32223
- var logger9 = createLogger("jlc-mcp");
32293
+ var require2 = createRequire(import.meta.url);
32294
+ var { version: version2 } = require2("../package.json");
32295
+ var logger2 = createLogger("jlc-mcp");
32224
32296
  var server = new Server({
32225
32297
  name: "jlc-mcp",
32226
- version: "0.1.0"
32298
+ version: version2
32227
32299
  }, {
32228
32300
  capabilities: {
32229
32301
  tools: {}
32230
32302
  }
32231
32303
  });
32232
32304
  server.setRequestHandler(ListToolsRequestSchema, async () => {
32233
- logger9.debug("Listing tools");
32305
+ logger2.debug("Listing tools");
32234
32306
  return { tools };
32235
32307
  });
32236
32308
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
32237
32309
  const { name, arguments: args } = request.params;
32238
- logger9.debug(`Tool call: ${name}`, args);
32310
+ logger2.debug(`Tool call: ${name}`, args);
32239
32311
  const handler = toolHandlers[name];
32240
32312
  if (!handler) {
32241
32313
  return {
@@ -32246,7 +32318,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
32246
32318
  try {
32247
32319
  return await handler(args);
32248
32320
  } catch (error2) {
32249
- logger9.error(`Tool error: ${name}`, error2);
32321
+ logger2.error(`Tool error: ${name}`, error2);
32250
32322
  return {
32251
32323
  content: [{
32252
32324
  type: "text",
@@ -32259,26 +32331,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
32259
32331
  async function main() {
32260
32332
  const registration = await ensureGlobalLibraryTables();
32261
32333
  if (!registration.success) {
32262
- logger9.error("Failed to register JLC libraries in KiCad global tables", {
32334
+ logger2.error("Failed to register JLC libraries in KiCad global tables", {
32263
32335
  errors: registration.errors
32264
32336
  });
32265
32337
  process.exit(1);
32266
32338
  }
32267
- const { symLibTable, fpLibTable, libraryStubs, version: version2 } = registration;
32339
+ const { symLibTable, fpLibTable, libraryStubs, version: version3 } = registration;
32268
32340
  if (symLibTable.created || symLibTable.modified || fpLibTable.created || fpLibTable.modified) {
32269
- logger9.info(`JLC libraries registered in KiCad ${version2}`, {
32341
+ logger2.info(`JLC libraries registered in KiCad ${version3}`, {
32270
32342
  symLibTable: symLibTable.created ? `created with ${symLibTable.entriesAdded} entries` : symLibTable.modified ? `added ${symLibTable.entriesAdded} entries` : "already configured",
32271
32343
  fpLibTable: fpLibTable.created ? "created" : fpLibTable.modified ? "updated" : "already configured",
32272
32344
  stubsCreated: libraryStubs.symbolsCreated.length + libraryStubs.directoriesCreated.length
32273
32345
  });
32274
32346
  }
32275
32347
  const httpPort = startHttpServer();
32276
- logger9.info(`Component browser available at http://localhost:${httpPort}`);
32348
+ logger2.info(`Component browser available at http://localhost:${httpPort}`);
32277
32349
  const transport = new StdioServerTransport;
32278
32350
  await server.connect(transport);
32279
- logger9.info("JLC MCP server running on stdio");
32351
+ logger2.info("JLC MCP server running on stdio");
32280
32352
  }
32281
32353
  main().catch((error2) => {
32282
- logger9.error("Server error", error2);
32354
+ logger2.error("Server error", error2);
32283
32355
  process.exit(1);
32284
32356
  });