UnoPim MCP Server
by oledmansfeld
README.md
# UnoPim MCP Server
Version 3.0.0 -- A Model Context Protocol server that connects Claude Desktop (and other MCP clients) to a UnoPim PIM instance (UnoPim v3 / 3.0.x, self-hosted or Cloud Hosting). Provides 15 consolidated, action-based tools for managing attributes, families, categories, products, media, association types, variant structures, measurements, and settings.
## 3.0.0 — UnoPim v3
This release targets **UnoPim 3.0.x** and is a breaking change from 2.x. It will not work against a 2.x UnoPim instance, and a 2.x version of this server will not work against a v3 instance.
### Requirements
- UnoPim **3.0.x** (self-hosted or UnoPim Cloud Hosting)
- Node.js 20+
### Breaking changes
- **Tool set consolidated**: 36 old per-verb tools → 15 action-based tools (one tool per resource, `action` parameter selects the operation). Full mapping:
<!-- KEEP IN SYNC: this table exists in CLAUDE.md and README.md — edit both. -->
| Old tool (2.x) | New call (3.0) |
|---|---|
| `unopim_get_guide` | unchanged |
| `unopim_get_schema` | unchanged |
| `unopim_get_attributes` | `unopim_attributes` action=`list` |
| `unopim_create_attribute` | `unopim_attributes` action=`create` |
| `unopim_get_attribute_options` | `unopim_attributes` action=`list_options` |
| `unopim_create_attribute_options` | `unopim_attributes` action=`create_options` |
| `unopim_delete_attribute` | `unopim_attributes` action=`delete` |
| `unopim_get_attribute_groups` | `unopim_attribute_groups` action=`list` |
| `unopim_create_attribute_group` | `unopim_attribute_groups` action=`create` |
| `unopim_delete_attribute_group` | `unopim_attribute_groups` action=`delete` |
| `unopim_get_families` | `unopim_families` action=`list` |
| `unopim_create_family` | `unopim_families` action=`create` |
| `unopim_update_family` | `unopim_families` action=`add_attributes` (or `update` = full PUT) |
| `unopim_delete_family` | `unopim_families` action=`delete` |
| `unopim_get_family_schema` | removed — `unopim_families` action=`get` shows the attribute groups |
| `unopim_get_categories` | `unopim_categories` action=`list` |
| `unopim_create_category` | `unopim_categories` action=`create` |
| `unopim_delete_category` | `unopim_categories` action=`delete` |
| `unopim_get_category_fields` | `unopim_category_fields` action=`list` |
| `unopim_create_category_field` | `unopim_category_fields` action=`create` |
| `unopim_get_products` | `unopim_products` action=`list` |
| `unopim_get_product` | `unopim_products` action=`get` |
| `unopim_create_product` | `unopim_products` action=`create` |
| `unopim_update_product` | `unopim_products` action=`update` |
| `unopim_upsert_product` | `unopim_products` action=`upsert` |
| `unopim_bulk_create_products` | `unopim_products` action=`bulk_create` (NB: `validate_only` removed — it never actually validated) |
| `unopim_delete_product` | `unopim_products` action=`delete` (configurable parent: `unopim_configurable_products` action=`delete` — deletes ALL variants in v3!) |
| `unopim_smart_create_product` | removed — `unopim_products` action=`create` + `unopim_get_guide` |
| `unopim_create_configurable_product` | `unopim_configurable_products` action=`create` |
| `unopim_add_variant` | `unopim_configurable_products` action=`add_variant` |
| `unopim_update_configurable_product` | `unopim_configurable_products` action=`update` |
| `unopim_upload_product_media` | `unopim_media` action=`upload` target=`product` |
| `unopim_upload_category_media` | `unopim_media` action=`upload` target=`category` |
| `unopim_get_channels` | `unopim_settings` action=`list` resource=`channel` |
| `unopim_get_locales` | `unopim_settings` action=`list` resource=`locale` |
| `unopim_get_currencies` | `unopim_settings` action=`list` resource=`currency` |
- **`unopim_smart_create_product` and `unopim_get_family_schema` are gone.** Use `unopim_products` action=`create` (family validation happens against the real family) and `unopim_families` action=`get` respectively.
- **Authentication changed**: OAuth2 password grant now sends client credentials via an `Authorization: Basic` header instead of the JSON body. No config change needed — the server builds the header internally from `UNOPIM_CLIENT_ID`/`UNOPIM_CLIENT_SECRET` — but **tokens and cached OAuth sessions issued against a 2.x instance are invalid after the instance is upgraded to v3.** Re-authenticate (HTTP mode) or regenerate API credentials (stdio mode) after a UnoPim upgrade.
- **Pagination defaults to 10 and clamps to 100** per request — tools now follow `links.next` internally where relevant; if you built anything against the raw API, don't assume `limit` above 100 is honored.
- **Product `associations` moved to the top level** of the product payload (a sibling of `values`, not nested inside it).
### New capabilities
- `unopim_measurements` — measurement families, units (with conversion factors), and attribute↔unit bindings.
- `unopim_association_types` — custom product-relationship types (e.g. spare parts, accessories) with per-link fields.
- `unopim_variant_structures` — 1-/2-level variant axes per family with per-level attribute placement.
- `unopim_settings` — full CRUD (not just read) on channels, locales and currencies, enabling instance bootstrap from scratch.
- `unopim_media` — `get`/`delete` in addition to `upload`, plus a `swatch` target for attribute-option swatches. Product uploads are auto-linked server-side by UnoPim v3.
- Delta sync on `unopim_products`: `updated_since` filter shortcut plus `pagination_type=search_after` cursor pagination (simple products only).
- `unopim_get_capabilities`: deterministic, local self-description of every resource, filterable field, operator, pagination rule and rate limit — no trial-and-error required.
## UnoPim Cloud Hosting ("SaaS")
UnoPim also ships as a managed offering — a dedicated, standard UnoPim instance per customer, same API, no code differences. Point `UNOPIM_BASE_URL` (or the HTTP-mode login form's base-URL field) at the tenant's assigned URL. Hourly API-call quotas apply per tier: Starter 5,000/hour, Pro 10,000/hour, Enterprise 25,000/hour (independent of the general 120 req/min rate limit).
## Transports
**Stdio** -- Runs as a local subprocess of Claude Desktop. Requires UnoPim credentials in env vars.
**HTTP** -- Exposes SSE and Streamable HTTP endpoints. Intended for remote access via ngrok or cloud deployment.
## Authentication
### HTTP transport: OAuth browser-based (per-user)
Each user authenticates through a browser login form that Claude Desktop opens automatically (MCP OAuth spec). Uses PKCE with S256 challenge. Tokens expire after 24 hours by default (configurable via `MCP_TOKEN_EXPIRY_SECONDS`). Credentials are stored in localStorage for seamless re-auth.
When `UNOPIM_*` env vars are absent, the server runs in OAuth-only mode.
### HTTP transport: API key fallback
Uses shared credentials from env vars. Set `MCP_API_KEY` or it defaults to `UNOPIM_CLIENT_ID`.
### Stdio transport
Uses `UNOPIM_*` env vars directly. No browser-based auth.
UnoPim v3 itself authenticates via OAuth2 password grant with client credentials sent as an `Authorization: Basic` header (handled internally — no config change needed).
## Claude Desktop Configuration
### HTTP mode with OAuth (recommended)
```json
{
"mcpServers": {
"unopim": {
"url": "https://your-ngrok-url.ngrok-free.app/mcp"
}
}
}
```
### Stdio mode (local)
```json
{
"mcpServers": {
"unopim": {
"command": "node",
"args": ["/path/to/unopim-mcp/dist/index.js"],
"env": {
"UNOPIM_BASE_URL": "http://your-unopim:8000",
"UNOPIM_CLIENT_ID": "your-client-id",
"UNOPIM_CLIENT_SECRET": "your-client-secret",
"UNOPIM_USERNAME": "user@example.com",
"UNOPIM_PASSWORD": "password"
}
}
}
}
```
## Tools (15)
### Meta / Discovery (start here)
- `unopim_get_schema` -- Fetch the complete data model (fetches ALL pages)
- `unopim_get_capabilities` -- Deterministic self-description: resources, filters, operators, pagination, rate limits
- `unopim_get_guide` -- Step-by-step guides: product-import, configurable-products, troubleshooting
### Data Model (action-based CRUD)
- `unopim_attributes` -- actions: list, get, create, update, delete, list_options, create_options, update_options, delete_option
- `unopim_attribute_groups` -- actions: list, get, create, update, delete
- `unopim_families` -- actions: list, get, create, update, delete, add_attributes
- `unopim_variant_structures` -- actions: list, get, create, update, delete (NEW in v3)
- `unopim_categories` -- actions: list, get, create, update, delete
- `unopim_category_fields` -- actions: list, get, create, update, delete, list_options, create_options, update_options, delete_option
### Products
- `unopim_products` -- actions: list, get, create, update, patch, delete, upsert, bulk_create
- `unopim_configurable_products` -- actions: list, get, create, update, add_variant, delete (deleting a parent deletes ALL its variants)
- `unopim_media` -- actions: upload, get, delete × target: product, category, swatch (product uploads auto-link server-side)
### v3 Features
- `unopim_settings` -- actions: list, get, create, update, delete × resource: channel, locale, currency
- `unopim_measurements` -- measurement families, units, attribute↔unit bindings (NEW in v3)
- `unopim_association_types` -- custom product-relationship types + per-link fields (NEW in v3)
Every tool's `delete` action is **IRREVERSIBLE** -- always confirm with the user first.
## Environment Variables
### Required for stdio mode / Optional for HTTP mode (enables API key auth)
| Variable | Description |
|----------|-------------|
| `UNOPIM_BASE_URL` | UnoPim API URL (e.g., `http://localhost:8000`) |
| `UNOPIM_CLIENT_ID` | OAuth2 Client ID |
| `UNOPIM_CLIENT_SECRET` | OAuth2 Client Secret |
| `UNOPIM_USERNAME` | API username |
| `UNOPIM_PASSWORD` | API password |
### Optional
| Variable | Default | Description |
|----------|---------|-------------|
| `UNOPIM_DEFAULT_LOCALE` | `en_US` | Default locale |
| `UNOPIM_DEFAULT_CHANNEL` | `default` | Default channel |
| `UNOPIM_DEFAULT_CURRENCY` | `USD` | Default currency |
| `PORT` | `3000` | HTTP server port |
| `MCP_API_KEY` | Value of `UNOPIM_CLIENT_ID` | API key for shared-credential auth |
| `MCP_TOKEN_EXPIRY_SECONDS` | `86400` (24h) | OAuth token expiry |
| `MCP_LOCK_BASE_URL` | unset | Set to `true` to lock URL field in login form |
## Docker
The server is packaged as a Docker image (node:20-alpine). See the `unopim-deploy` repo for the Docker Compose setup.
```bash
docker compose build mcp
docker compose up -d
```
## Development
```bash
npm install
npm run build # Build TypeScript (plain tsc)
npx tsc --noEmit # Type check only
npm test # Run the vitest suite -- run before considering any change done
```
## Project Structure
```
src/
index.ts # Stdio transport entry point
index-http.ts # HTTP transport with OAuth + per-session servers
config.ts # Environment configuration
version.ts # Package version constant
auth/
oauth.ts # UnoPim OAuth2 token management (v3: Basic-header client creds)
mcp-oauth.ts # MCP OAuth server (PKCE, stores, token exchange)
login-form.ts # HTML login form with localStorage
client-registry.ts # Per-user UnoPimClient cache
client/
endpoints.ts # Typed endpoint catalog (paths/filters/pagination)
unopim-client.ts # HTTP client: envelope handling, ETag cache, retry/backoff, pagination
resources.ts # Generic ResourceClient (list/get/create/update/patch/delete + sub-resources)
tools/
registry.ts # Shared tool registry
register-all.ts # Registers all 15 consolidated tools
crud-factory.ts # Factory for action-based CRUD tools
product-helpers.ts # Shared product payload/error/orphan helpers
schema.ts # unopim_get_schema
capabilities.ts # unopim_get_capabilities
guides.ts # unopim_get_guide
attributes.ts # unopim_attributes
groups.ts # unopim_attribute_groups
families.ts # unopim_families
variant-structures.ts # unopim_variant_structures
categories.ts # unopim_categories
category-fields.ts # unopim_category_fields
products.ts # unopim_products
configurable-products.ts # unopim_configurable_products
media.ts # unopim_media
settings.ts # unopim_settings
measurements.ts # unopim_measurements
association-types.ts # unopim_association_types
prompts/
index.ts # MCP prompt handlers
types/
errors.ts # Error types (v3 error envelope)
oauth.ts # OAuth + MCP auth types
unopim.ts # UnoPim API types
skills/ # Content served by unopim_get_guide
```
## Tech Stack
- Node.js 20, TypeScript
- @modelcontextprotocol/sdk, zod
- vitest for tests
- No other runtime dependencies -- OAuth uses Node.js built-in crypto
## License
ISC
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing