@minipim/mcp
v0.2.1
Published
MCP server for MiniPim — exposes a headless PIM catalog to AI agents.
Downloads
241
Maintainers
Readme
@minipim/mcp
MCP server for MiniPim — exposes a headless PIM catalog to AI agents.
Status: Phase 1 complete — ten tools and three prompts. Diagnose with
find_incomplete_products, fix one product withset_product_attributes, many withbulk_update_products, or variant barcodes withset_variant_identifiers, then verify withget_product_completeness. Remote hosted endpoint and OAuth are Phase 2/3; seedocs/MCP_DISTRIBUTION_PLAN.md.
Tools
| Tool | What it's for |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| get_catalog_overview | Catalog shape in one call: counts by status, families, channels with the attributes each requires, locales, top categories, brands, tags. Start here. |
| get_family_schema | Every attribute a family accepts, with the required wire shape of each value. Call before writing attributes. |
| search_products | Find products by text and filters. Returns lean rows, not full payloads. |
| get_product | One product fully hydrated — attributes, resolved category names, media with alt text, variants, identifiers, relationships. |
| find_incomplete_products | What needs work. Scores products against their channels' required attributes, worst-first, plus which attributes are missing across how many products. |
| get_product_completeness | Per-channel score and gaps for one product. Use it to verify after a write. |
| set_product_attributes | Write. Set or clear attributes on one product. Refuses undeclared codes; teaches the expected shape on failure. |
| bulk_update_products | Write, many. One change across an explicit id list: attribute, status, tags, categories, family. Dry run by default. |
| set_variant_identifiers | Write. UPC/GTIN/MPN/SKU on a variant — the only way to close identifier gaps. Check-digit validated; add by default. |
| check_connection | Diagnostic: which catalog am I pointed at, and does the key work? |
Attribute codes are per-catalog data, not a fixed schema, so they can't be baked into a
tool signature — get_family_schema is how an agent learns them, and it returns the value
shape for each type (notably: money is integer cents, never a decimal).
Two different meanings of "required"
Worth knowing before you trust either one:
get_family_schema'srequiredflag is the family's rule, enforced when a product is activated. It is common for a catalog to have thisfalseon everything.- Completeness is scored against each channel's own list, reported as
requiredAttributesper channel byget_catalog_overview.
So an attribute can be required: false in the schema and still be counted missing by
find_incomplete_products. The channel list is the one that decides whether a product is
publishable.
Localizable attributes need a locale
A write to a localizable attribute must pass locale; there is no default scope to fall
back to. get_catalog_overview reports defaultLocale for exactly this — including when a
single-locale catalog doesn't flag its one locale as the default.
find_incomplete_products is the intended entry point for "improve this catalog". Read its
byMissingAttribute breakdown before starting: fixing one attribute across many products is
usually the efficient order, and a per-product list invites walking the catalog one item at a
time until you run out of context. It also separates gaps you can fix by writing attributes
from variant identifier gaps (UPC/GTIN/MPN), which need a different endpoint entirely —
worth knowing before you try, because on a freshly imported catalog most gaps are the latter.
Prompts
Tools describe capability; these describe a good order to use it in. Hosts surface them as
slash commands (/enrich-catalog in Claude Code, a "Prompts" menu elsewhere).
| Prompt | Arguments | What it does |
| ------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| audit-catalog | channel? | Read only. Reports catalog health worst-first, separating structural problems from bulk data gaps. Changes nothing. |
| enrich-catalog | channel? limit? | Closes the largest gaps, batching by attribute across many products rather than walking product by product. |
| prep-for-channel | channel tag? | Gets products publishable on one channel, writing at that channel's scope — which a default-scope write does not satisfy. |
Two rules are baked into all three, because no individual tool description can carry them:
- Batch by attribute, not by product. One
get_family_schemaread serves a whole group of products missing the same code. The per-product walk re-reads the same schema every time. - Never invent a catalog fact. Drafting prose from what's already on a record is fine. Originating a weight, dimension, material, certification, or barcode is not — a fabricated GTIN passes check-digit validation and then fails silently in a channel feed, and nothing downstream flags it. Gaps needing supplier data get reported, not filled.
In read-only mode only audit-catalog is offered, since the other two would send an agent
down a path it has no write tools to finish.
What you need
An API key from the MiniPim admin under Settings → API keys. Create it with the
read scope only to start — the server can't write what the key can't write, which is a
stronger guarantee than any client-side flag. A read-scoped key still gives you the whole
diagnosis half of the loop — the six read tools and audit-catalog. The three writing tools
(set_product_attributes, set_variant_identifiers, bulk_update_products) need write.
The key carries its own tenant, so you don't configure an organization id.
Setup
Claude Code
claude mcp add minipim --env MINIPIM_API_KEY=pim_… -- npx -y @minipim/mcpClaude Desktop / Cursor / any stdio client
{
"mcpServers": {
"minipim": {
"command": "npx",
"args": ["-y", "@minipim/mcp"],
"env": { "MINIPIM_API_KEY": "pim_…" }
}
}
}n8n, Zapier MCP Client, Make MCP Client, claude.ai connectors
These connect over HTTP rather than launching a process. Run the server somewhere reachable:
MINIPIM_API_KEY=pim_… npx -y -p @minipim/mcp minipim-mcp-http # serves /mcp on :4200then point the client at https://your-host/mcp.
Phase 1 authenticates from the server's own environment, so an HTTP deployment acts as a single tenant — one key, one catalog. Don't expose it publicly. Per-request credentials and OAuth are Phase 2/3 of the distribution plan.
Configuration
| Env var | Default | Notes |
| ------------------- | ------------------------- | ------------------------------------------------------------------------------ |
| MINIPIM_API_KEY | — | Required. pim_… from Settings → API keys. |
| MINIPIM_BASE_URL | https://api.minipim.com | Point at your own instance if self-hosting. |
| MINIPIM_READ_ONLY | false | 1/true withholds mutating tools. |
| PORT | 4200 | HTTP transport only. |
| MINIPIM_USER_ID | — | Local dev only, for an instance running PIM_AUTH=header. |
| MINIPIM_ORG_ID | — | Local dev only, paired with MINIPIM_USER_ID. Ignored when an API key is set. |
Verifying it works
Ask the agent to run check_connection. It reports the API version, the organization the
key resolves to, and the product count. Check the organization name is the catalog you
meant — a key from the wrong org will fail nothing and quietly work against the wrong
data.
To exercise every read tool against a real instance from the command line:
pnpm --filter @minipim/mcp build
SMOKE_ORG_ID=<org-uuid> pnpm --filter @minipim/mcp smokescripts/smoke.mjs drives the built binary over real stdio JSON-RPC. The unit tests stub
HTTP, so this is what catches wrong assumptions about live response shapes; the header at
the top of the script documents the env it needs.
When it fails, the message says which knob to turn (v0.2.1+)
Every failure below is a different problem, and each one names itself rather than defaulting to "check your key":
| What you see | What it means |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| Could not reach the MiniPim API at <url> — nothing is listening… | Wrong MINIPIM_BASE_URL, or the instance is stopped. The message also covers DNS, timeouts and TLS failures by name. |
| <url> answered, but not with a MiniPim health check | The URL resolves to something that isn't a MiniPim API — commonly the admin UI instead of the API origin. |
| The credentials were rejected… | A 401. The key is wrong, revoked, or truncated. |
| The credentials are valid but not permitted… | A 403. A read-only key attempting a write, or a user who isn't a member of MINIPIM_ORG_ID. Issuing a new key will not help. |
| This is a fault in the MiniPim instance… | A 5xx. Nothing about your request or credentials will fix it — check the instance's own health and logs. |
Embedding
import { buildServer, handleMcpRequest } from '@minipim/mcp';
const { server } = buildServer({ apiKey: process.env.MINIPIM_API_KEY });
// …attach your own transport, or mount handleMcpRequest on an existing HTTP app.Identifiers are not attributes
upc, gtin* and mpn completeness gaps are satisfied by rows on a variant, not by the
product's attributes payload — set_product_attributes cannot close them. That distinction
matters more than it sounds: on a freshly imported catalog, identifier gaps are typically most
of the outstanding work, so an agent that only knows about attributes will report itself stuck.
set_variant_identifiers defaults to mode: "add". The API also offers a replace that wipes
the variant's whole identifier set, so mode: "replace" additionally requires apply: true
and names exactly which identifiers it would destroy first.
GTIN-family values are check-digit validated locally before any request, which means the error can tell you the digit that was expected rather than just "invalid". It will not tell you to blindly substitute it — a wrong check digit usually means a digit was mistyped earlier in the number.
Supply identifiers from supplier or source data. Never let an agent generate a barcode, even one that passes validation: it will pass every downstream check too, and then be wrong.
Bulk writes
bulk_update_products dry-runs by default. It reports what would change and writes
nothing until you pass apply: true. The dry run is computed here rather than by the API
(which has no dry-run mode), so it reads the current value of every id and diffs it — which
is why the id cap is 100 rather than the API's 500.
Read alreadyMatching in the dry-run output: it's usually the most informative number,
because a batch where most ids already hold the target value is a sign the filter was wrong.
It compares by value rather than by key order, so { amount, unit } and { unit, amount }
count as the same value — they are.
The dry run validates the value against the attribute's declared shape before reporting anything, so it won't promise a change that the API would reject for every id. Send structured values as real JSON objects, not as JSON strings.
There is no filter parameter, on purpose. "Apply to everything matching X" is how a
catalog gets wrecked by a slightly-wrong understanding of the filter. Get the ids from
find_incomplete_products or search_products, look at them, then pass them.
Two things to know before using move_family or replace modes:
familyMode: "drop_unsupported"deletes attribute values the destination family doesn't declare. The default,preserve, skips those products instead.mode: "replace"on tags or categories discards what's there.add/removedon't.
What this server will not do
Deliberately absent, and staying absent: catalog wipe, organization deletion, member and
API-key management, product deletion, and triggering connector syncs. An agent pushing a
half-enriched catalog to a live storefront is the worst failure mode available here, so
that stays a human action. Reasoning in docs/MCP_DISTRIBUTION_PLAN.md.
License
Apache-2.0
