npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@minipim/mcp

v0.2.1

Published

MCP server for MiniPim — exposes a headless PIM catalog to AI agents.

Downloads

241

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 with set_product_attributes, many with bulk_update_products, or variant barcodes with set_variant_identifiers, then verify with get_product_completeness. Remote hosted endpoint and OAuth are Phase 2/3; see docs/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's required flag is the family's rule, enforced when a product is activated. It is common for a catalog to have this false on everything.
  • Completeness is scored against each channel's own list, reported as requiredAttributes per channel by get_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_schema read 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/mcp

Claude 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 :4200

then 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 smoke

scripts/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/remove don'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