Skip to main content
Glama
README.md
# bow-products-mcp

MCP server + CLI for Bow Beauty admins (and Claude) to populate, enrich and validate the products catalog without leaving the editor.

The MCP exposes the four admin-only endpoints introduced in the products module overhaul (`/enrich`, `/bulk`, `/import-from-url`, `/test-recommendations`) plus the regular product CRUD endpoints. Authentication is the same Bow Beauty JWT used by the `bow-admin` dashboard — your account must have role `Admin`.

## Requirements

- Node.js ≥ 20.
- An Admin account on a Bow Beauty API.

## Install

```bash
git clone https://github.com/<org>/bow-products-mcp.git
cd bow-products-mcp
npm install
npm run build
npm link        # makes the `bow-products-mcp` bin available globally
```

The bin can also be invoked directly via `node /path/to/bow-products-mcp/dist/cli.js …`.

## Commands

| Command | Purpose |
|---|---|
| `bow-products-mcp login` | Interactive login (email + masked password). Persists JWT to `~/.bow-mcp/config.json` chmod 600. Rejects non-Admin accounts. |
| `bow-products-mcp whoami` | Prints the current session (email + API base URL + login timestamp). |
| `bow-products-mcp logout` | Removes the persisted config. |
| `bow-products-mcp run` | Starts the MCP server over stdio. This is what Claude Code invokes. |
| `bow-products-mcp help` | Usage summary. |

### Pointing at a different backend

The default API URL is the Bow Beauty dev backend. Override with the `BOW_API_BASE_URL` env var or `--api-base-url=<url>` flag when logging in:

```bash
BOW_API_BASE_URL=https://my-api.example.com bow-products-mcp login
```

## Wiring into Claude Code

After running `bow-products-mcp login` once, add the server to Claude Code:

```bash
claude mcp add bow-products bow-products-mcp run
```

Then in any Claude Code session you can ask Claude to:

- "Search the catalog for niacinamide serums under $30."
- "Enrich this draft product with brand X."
- "Import this product URL and test how it would rank for an oily-skin profile."
- "Bulk ingest these 12 products."

## MCP tools exposed

### Catalog read

- `search_products({ searchTerm?, category?, brand?, minPrice?, maxPrice?, inStock?, isFeatured?, page?, pageSize? })`
- `get_product({ id })`

### Catalog write (Admin)

- `create_product({ reference, name, description, price, brand, category, ... })`
- `update_product({ id, patch })` — partial update; only the fields in `patch` change.
- `delete_product({ id })`

### AI ingestion (Admin)

- `enrich_product_draft({ name, brand, category, ...hints })` — Gemini-enriched draft, **not persisted**.
- `bulk_ingest({ mode: 'Create' | 'Upsert' | 'EnrichAndCreate', products: [...] })` — max 50 items per call.
- `import_from_url({ url, dryRun })` — fetch + Gemini extract + enrich; persists as `IsActive=false` when `dryRun=false`.

### Test recommendations

- `test_recommendations({ profile, limit?, includeScoreBreakdown? })` — runs the engine against a synthetic user. `profile` accepts `skinType`, `concerns`, `hairTypes`, `colorSeason`, `undertone`, `preferredRoutineStep`, `categories`, `priceRange`, `ethical`. The breakdown surfaces every signal that fired and the points it added.

### Enum surfacing

The `list_*` tools return the valid string values for each enum, so Claude can construct correct inputs without a round-trip:

`list_categories`, `list_concerns`, `list_skin_types`, `list_hair_types`, `list_routine_steps`, `list_formulations`, `list_finishes`, `list_color_seasons`, `list_undertones`, `list_bulk_modes`.

## Auth and refresh

Login decodes the JWT locally just to short-circuit non-admin accounts (rejected with a clear message). The backend remains the authoritative role check on every request.

The HTTP client transparently refreshes the JWT on any `401` (using the persisted refresh token) and retries the request once. If the refresh itself fails, all subsequent tool calls return an error asking you to run `bow-products-mcp login` again.

## Security notes

- The config file is written with `chmod 600` so it is only readable by your user.
- Tokens are never sent to anywhere other than the configured API base URL.
- This is v1: the JWT is stored on disk in plain JSON. If you'd rather use the OS keychain, open an issue.
- Never commit `~/.bow-mcp/config.json` — it's a per-user secret.

## Development

```bash
npm run watch   # tsc --watch
npm start       # node dist/cli.js help
```

Lint and tests are pending in v1; this package targets a small operational footprint.

## License

MIT