fabplane
OfficialSupports AliExpress as a built-in purchase destination for cart items, allowing BOMs and shopping lists to route parts procurement to AliExpress.
Supports Amazon as a built-in purchase destination for cart items, allowing BOMs and shopping lists to route parts procurement to Amazon.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fabplaneadd 10 10k 0603 resistors to my JLCPCB cart"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
fabplane-cli
Command line, MCP server and typed TypeScript client for fabplane.com: orgs, personal API tokens, carts (shopping lists / BOMs with per-item purchase destinations) and the parts inventory. It also talks to the local fabPlane desktop app (the fabdesk daemon).
fabplane: a CLI with human output by default and--jsonfor scriptsfabplane mcp: a stdio MCP server for Claude Code, Codex, Cursor and other agentsimport { FabplaneClient } from "fabplane-cli": the same API as a library
Docs: https://fabplane.com/docs/cli · API reference: https://fabplane.com/docs/api · MIT licensed.
Install
npm i -g fabplane-cli # installs the `fabplane` command
npx fabplane-cli help # or run it without installingUntil the first npm release, install from GitHub:
npm i -g github:fabPlane/fabplane-cliNode.js 20 or newer is required.
Related MCP server: a2a-mcp-bridge
Sign in
fabplane login # browser device flow: prints a URL and code, waits for approval
fabplane login --token fpk_… # store a personal API token instead
fabplane whoami
fabplane logoutCredentials are resolved in this order:
What | Source |
token |
|
API origin |
|
org |
|
The credentials file is $XDG_CONFIG_HOME/fabplane/credentials.json (default
~/.config/fabplane/credentials.json; %APPDATA%\fabplane\credentials.json on Windows). It is
written atomically with mode 0600 and keeps one profile per API origin, so you can be signed in to
production and to a self-hosted or development API at the same time. FABPLANE_CREDENTIALS_FILE
overrides the path.
Personal API tokens (fpk_…) suit CI and agents: create one with fabplane tokens create <name>,
then set FABPLANE_TOKEN. logout does not revoke fpk_ tokens; use fabplane tokens revoke <id>.
To use an API other than production, set FABPLANE_API_ORIGIN (for example
http://localhost:4000 for a local fabplane API) or pass --origin. Password login
(fabplane login --email … --password …) exists only for local/development accounts and is refused
against production.
Commands
Every command accepts --json (machine output), --org <id|slug> and --origin <url>.
fabplane help <command> prints the details.
Command | What it does |
| Sign in (device flow) or store a token |
| Forget stored credentials; revokes a device session |
| User, API origin, token source and default org |
| Print (or open) the web dashboard for the current API |
| Your orgs; |
| Create an org (you are owner) |
| Set the default org for this API origin |
| Members of the org |
| Create an invite link (admin) |
| Pending invites (admin) |
| Change a member's role |
| Accept an invite |
| Orgs your verified email domain may join, and joining one |
| Personal API tokens (the secret is shown once) |
| Built-in fab houses and distributors plus the org's custom ones |
| Add a custom destination, e.g. a regional DigiKey site |
| Carts, filtered by git repo or client project id |
| A cart, its items and per-destination totals |
| Create a cart |
| Add one item |
| Add items from JSON, JSONL or a BOM CSV (500 per request); |
| CSV export, optionally for one destination |
| Delete a cart |
| Search inventory |
| One item with attributes and images |
| Add an item, optionally with photos |
| Bulk upsert, 500 items per request, keyed by |
| Atomic stock change, e.g. |
| Delete an item and its images |
| Run the stdio MCP server |
| The local fabPlane desktop app |
| Call a desktop tool |
| Raw request to any endpoint (escape hatch) |
|
Exit codes: 0 success, 1 API or runtime error, 2 usage error.
Carts and destinations
Each cart item has a destination: a built-in (builtin:jlcpcb, builtin:pcbway,
builtin:oshpark, builtin:digikey, builtin:mouser, builtin:lcsc, builtin:arrow,
builtin:farnell, builtin:aliexpress, builtin:amazon, builtin:manual) or one of the org's
custom destinations. Items without one go to the cart's fab house (--fab, JLCPCB by default).
carts import reads CSV headers case-insensitively and understands common BOM names: qty,
designator, mfr, manufacturer part number, LCSC Part (as sku), value, footprint,
unit price, destination, status, notes. A row with no quantity column counts its designators.
fabplane carts create "Rev B" --repo https://github.com/acme/sensor-board
fabplane carts import <cartId> bom.csv
fabplane carts add <cartId> --mpn STM32G031K8T6 --qty 10 --dest builtin:lcsc
fabplane carts export <cartId> --dest builtin:lcsc -o lcsc.csvInventory from photos: extract locally, send fields
The API stores what you send; it does not run AI on uploads. Setting serverAiProcessing: true
(--server-ai on the CLI) is answered with HTTP 501 server_ai_unavailable and nothing is
stored. Clients are expected to run a model locally (a vision model on the photo of a reel, bag
label or datasheet), extract the fields, and send them as structured data with the photo attached:
# e.g. after a local model read the label of a reel:
fabplane inventory add --name "10k 0603 resistor" --mpn RC0603FR-0710KL --manufacturer Yageo \
--qty 5000 --unit pcs --location "Lab / drawer A3" --tag passive \
--attr resistance=10k --attr tolerance=1% --attr package=0603 \
--source my-scanner --external-id reel-0042 --image reel.jpgFor batches, write one JSON object per line and import them. Rows are upserted by
(source, externalId), so re-running an import updates instead of duplicating:
{"name":"NE555 timer","mpn":"NE555P","manufacturer":"TI","quantity":25,"location":"Bin 4","externalId":"bin4-ne555","attributes":{"package":"DIP-8"}}
{"name":"100nF 0402 capacitor","mpn":"GRM155R71C104KA88D","quantity":10000,"externalId":"reel-0107"}fabplane inventory import parts.jsonl --source my-scannerRows without an externalId get one derived from name, MPN, manufacturer, SKU and location.
MCP server
fabplane mcp serves these tools over stdio. They act on your default org unless the agent passes
orgId:
Tool | |
| Your orgs (ids, slugs, roles) |
| Purchase destinations for cart items |
| Find, read and create carts (by repo or project id) |
| Edit cart items |
| Search, add and count stock |
fabplane mcp --desktop also exposes desktop_status, desktop_projects and desktop_call_tool,
which reach the fabPlane desktop app running on the same machine.
Sign in first (fabplane login) or pass FABPLANE_TOKEN in the server's environment.
Claude Code
claude mcp add fabplane -- npx -y fabplane-cli mcp
# with a token and the desktop tools:
claude mcp add fabplane -e FABPLANE_TOKEN=fpk_… -- npx -y fabplane-cli mcp --desktopCodex (~/.codex/config.toml)
[mcp_servers.fabplane]
command = "npx"
args = ["-y", "fabplane-cli", "mcp"]
# env = { FABPLANE_TOKEN = "fpk_…", FABPLANE_ORG = "my-team" }Cursor (.cursor/mcp.json or ~/.cursor/mcp.json)
{
"mcpServers": {
"fabplane": { "command": "npx", "args": ["-y", "fabplane-cli", "mcp"] }
}
}With a global install, use "command": "fabplane", "args": ["mcp"] instead of npx.
Library
import { FabplaneClient, FabplaneApiError, resolveAuth, resolveDefaultOrg, dashboardUrlFor } from "fabplane-cli";
const client = new FabplaneClient({ token: process.env.FABPLANE_TOKEN }); // origin defaults to https://api.fabplane.com
const orgId = await resolveDefaultOrg(client); // FABPLANE_ORG, else your personal org
const { cart } = await client.createCart(orgId, { name: "Rev B", repos: ["https://github.com/acme/board"] });
await client.addCartItems(orgId, cart.id, [{ mpn: "NE555P", quantity: 4, refs: ["U1", "U2", "U3", "U4"] }]);
try {
await client.adjustInventory(orgId, "item-id", -10, "built 10 boards");
} catch (err) {
if (err instanceof FabplaneApiError && err.code === "conflict") console.log("not enough stock");
else throw err;
}
console.log(dashboardUrlFor(client.origin)); // https://app.fabplane.com/dashboardEvery API operation is a method named after its OpenAPI
operationId(listOrgs,createCart,replaceCartItems,listInventory,bulkUpsertInventory,uploadInventoryImage, …) and resolves to the JSON body the API documents, e.g.{ orgs }or{ items, nextCursor }.204answers resolve toundefined;exportCartCsvresolves to CSV text.Also available:
startDeviceLogin,pollDeviceToken,waitForDeviceToken,me,logout,getSettings,putSettings,publicCatalog,config,sendPush,getPushJob, andraw/requestfor anything else.Failures throw
FabplaneApiErrorwithstatus,code(the API'serrorfield),message,body.Images:
createInventoryItem(orgId, input, [{ data, contentType, filename }])sends multipart;datamay be aUint8Array,ArrayBufferorBlob.Pass
fetchto the constructor to inject your own implementation (tests, proxies).fabplaneToolsis the MCP tool list as plain objects (name,title,description, zodinputSchemashape,annotations,handler(client, args, { orgId })) for embedding in another MCP server;createFabplaneMcpServer()builds a readyMcpServer.CredentialStoreandresolveAuth()read and write the same credentials file as the CLI.
The desktop app
import { FabdeskClient } from "fabplane-cli";
const desk = new FabdeskClient(); // finds daemon.json; or { baseUrl, token }, or FABDESK_URL + FABDESK_TOKEN
console.log(await desk.health(), await desk.projects());
const tools = await desk.toolManifest();
const result = await desk.callTool("board_stats", {}, { projectId: "…", sync: true });FabdeskClient looks for daemon.json in FABDESK_HOME, then in the desktop app's data folders
(~/Library/Application Support/{fabPlane,fabdesk,fabPlane Dev,fabdesk-dev} on macOS,
%APPDATA%\… on Windows, $XDG_CONFIG_HOME/… on Linux), preferring a daemon whose process is alive.
It covers health, projects, project, readFile, threads, createThread, messages,
sendMessage, liveRuns, toolManifest, callTool, jobs, job, waitJob, settings and
authState.
API spec sync
spec/openapi.json is a snapshot of the API's OpenAPI 3.1 document and
src/generated/operations.ts lists its operations. The Sync API spec workflow runs every Monday
at 03:00 UTC (and on demand): it fetches /v1/public/openapi.json, regenerates both files and opens
or updates a pull request when something changed, listing operations that have no FabplaneClient
method yet. A test fails while any operationId lacks a method. If the API does not serve the
document yet (404), the job ends without changes.
npm run sync-spec # fetch from FABPLANE_API_ORIGIN (default https://api.fabplane.com)
npm run sync-spec -- --offline # regenerate from the committed snapshot onlyspec/fabdesk-tools.json is a snapshot of the desktop app's tool manifest (names, toolsets,
descriptions and JSON-schema inputs, as served by GET /tools/manifest). The fabdesk repository
refreshes it with a pull request here when its tools change.
Contributing
npm ci
npm run build # tsc → dist/
npm test # compiles src + test and runs node --test
bun test # the same tests straight from TypeScriptTests run against a small in-memory fake of the API (test/fake-api.ts) and of the desktop daemon
(test/fake-daemon.ts); no network access is needed. Keep runtime dependencies to
@modelcontextprotocol/sdk and zod (imported as zod/v4), and keep src/ free of Bun-only APIs:
the package must run under plain Node. Bun workspaces that vendor this repo get src/index.ts
through the bun export condition.
Publishing
Releases go to npm from GitHub Actions (.github/workflows/publish.yml), which needs an npm token
that has not been added yet:
On npmjs.com, create an automation (or granular publish) token that can publish the
fabplane-clipackage.Add it as the
NPM_TOKENrepository secret: Settings → Secrets and variables → Actions.Bump
versioninpackage.jsonandsrc/version.ts, then publish a GitHub release or push av*tag (e.g.v0.1.1). The workflow builds, tests and runsnpm publish --provenance.
Without the secret the workflow fails on its first step with an error asking for it, and publishes
nothing. Until then, install from GitHub: npm i -g github:fabPlane/fabplane-cli.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for Flex on the Job: inventory, jobs, customers, invoices, purchasing, sales orders.
MCP server for Product Management
MCP Server for an Agent Task Marketplace
Search multi-merchant supply, checkout, and track orders via MCP.
Related MCP Servers
- AlicenseCqualityBmaintenancePrototype MCP server enabling coding agents to manage Amigo Agent Forge operations, including org credentials, entity configurations, conversation simulations, and version sets.3719 npmISC
- FlicenseNot gradedqualityDmaintenanceA stdio MCP server that allows MCP-capable agents to call an A2A endpoint, enabling agent-to-agent messaging through the A2A protocol.-
- AlicenseNot gradedqualityBmaintenanceMCP server that provides pre-checkout basket tools and a local API/viewer, enabling agents to research products and manage a shopping cart through natural language.1MIT
- FlicenseAqualityBmaintenanceMCP server that exposes read-only NetSuite tools for querying warehouse stock levels and order status, using mock data locally and a pluggable client interface.2-