UNPKG

jlcpcb-mcp-parts-finder

Version:

MCP server for searching JLCPCB electronic components database

129 lines 3.85 kB
#!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import Database from "better-sqlite3"; import { z } from "zod"; import { homedir } from "os"; import { join } from "path"; import { existsSync } from "fs"; // Create server instance const server = new McpServer({ name: "jlcpcb-mcp-parts-finder", version: "1.0.0", }); // Database path - check multiple locations const dbLocations = [ process.env.JLCPCB_DB_PATH, join(homedir(), ".jlcpcb-mcp", "cache.sqlite3"), join(process.cwd(), "vendor", "jlcpcb-parts-mcp", "cache.sqlite3"), "/opt/jlcpcb-mcp/cache.sqlite3", ].filter(Boolean); let dbPath; for (const path of dbLocations) { if (path && existsSync(path)) { dbPath = path; break; } } if (!dbPath) { // Database not found - exit silently for MCP compatibility process.exit(1); } // Connect to database let db; try { db = new Database(dbPath, { readonly: true }); // Connected to database successfully } catch (error) { // Failed to open database process.exit(1); } // Define the search_parts tool server.tool("search_parts", "Search JLCPCB parts by category and keyword", { category_id: z .number() .describe("Category ID (e.g., 208 for Audio Connectors)"), keyword: z .string() .optional() .describe("Search keyword"), limit: z .number() .optional() .default(20) .describe("Max results (default: 20)"), }, async ({ category_id, keyword = "", limit = 20 }) => { try { const query = keyword ? `SELECT lcsc, mfr, description, package, stock FROM components WHERE category_id = ? AND (mfr LIKE ? OR description LIKE ?) ORDER BY stock DESC LIMIT ?` : `SELECT lcsc, mfr, description, package, stock FROM components WHERE category_id = ? ORDER BY stock DESC LIMIT ?`; const params = keyword ? [category_id, `%${keyword}%`, `%${keyword}%`, limit] : [category_id, limit]; const stmt = db.prepare(query); const results = stmt.all(...params); const text = results .map((r) => `C${r.lcsc}: ${r.mfr} - ${r.description || "No description"} (${r.package}, Stock: ${r.stock})`) .join("\n"); return { content: [{ type: "text", text: text || "No results found" }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error.message}` }], }; } }); // Define the list_categories tool server.tool("list_categories", "List all JLCPCB component categories", {}, async () => { try { const categories = db .prepare("SELECT id, category, subcategory FROM categories ORDER BY category, subcategory") .all(); const text = categories .map((c) => `${c.id}: ${c.category} > ${c.subcategory}`) .join("\n"); return { content: [{ type: "text", text }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error.message}` }], }; } }); // Handle graceful shutdown process.on("SIGINT", () => { // Shutting down if (db) { db.close(); } process.exit(0); }); process.on("SIGTERM", () => { // Shutting down if (db) { db.close(); } process.exit(0); }); // Start the server async function run() { const transport = new StdioServerTransport(); await server.connect(transport); // Server started successfully } run().catch((error) => { // Failed to start server process.exit(1); }); //# sourceMappingURL=index.js.map