Skip to main content
Glama
README.md
# sticker-mcp

MCP server that publishes sticker packs: PNG → visually-lossless HEIC + small HEIC
thumbnail, upload to Firebase Storage, and merge into the `sticker_categories`
Remote Config parameter.

## Requirements

- Node 18+ (uses global `fetch`)
- `brew install libheif imagemagick` (provides `heif-enc`, `magick`)
- `gcloud` authenticated with an account that has access to the target Firebase
  project (`crsel-dev`) for both Storage and Remote Config:
  ```
  gcloud auth login
  ```
  The bearer token comes from `gcloud auth print-access-token`. The active
  account must have Storage write + `firebaseremoteconfig.*` permissions on the
  project. The Remote Config REST API always requires a quota project — the
  tools send `x-goog-user-project` (defaults to `project`; override with
  `quotaProject` if your billing/quota project differs).

## Install

```bash
git clone git@github.com:crsel/stickers-mcp.git ~/stickers-mcp && bash ~/stickers-mcp/install.sh
```

`install.sh` (safe to re-run) will:
- verify Node 18+,
- install `libheif` + `imagemagick` via Homebrew if missing,
- `npm install`,
- register the server with Claude Code at **user scope** (available in all your projects).

Afterwards: run `gcloud auth login` if prompted, then restart Claude Code.

Manual registration (if you skip the script):

```bash
claude mcp add stickers-mcp -s user -- node /absolute/path/to/stickers-mcp/src/index.js
```

## Tools

| Tool | What it does |
|------|--------------|
| `convert_stickers` | A single `.png` file **or** every `.png` in a folder → `<out>/full/*.heic` (q90, alpha preserved) + `<out>/thumb/*.heic` sized into 10–25 KB. One file = one sticker, converted as-is (no content judgement). |
| `inspect_bucket` | `gcloud storage ls gs://<bucket>/<path>` — confirm existing layout before uploading. |
| `upload_stickers` | Upload full → `<folder>/` (bucket root), thumb → `thumbnails/<folder>/`. Returns config urls. `dryRun` to preview paths. |
| `get_sticker_categories` | Fetch live Remote Config: category_order, category sizes, ETag. |
| `update_sticker_categories` | `new`/`merge` a category into `sticker_categories` and publish (If-Match/ETag). `dryRun=true` by default — returns the diff without publishing. |
| `migrate_png_to_heic` | Scan `sticker_categories` for `.png` urls (optionally scoped via `categoryKey`, capped via `limit`), fetch each PNG, convert to HEIC + thumbnail, upload to root + `thumbnails/`, rewrite the url to `.heic`. Publishes incrementally (every 10, fresh ETag), emits per-item progress, skips a failed sticker instead of aborting, and is safe to re-run. `dryRun=true` by default. |
| `reorder` | Reorder the category list (`scope=categories`) or stickers within a category (`scope=stickers`). Use `moves` for "move X before/after B" / to start/end/index, or `order` for a full desired list. `dryRun=true` by default; publishes with If-Match/ETag. |

## Conventions

- Verified bucket layout: full sticker = `gs://<bucket>/<folder>/<file>.heic`
  (bucket root, **no** `stickers/` prefix); thumbnail =
  `gs://<bucket>/thumbnails/<folder>/<file>.heic`. Config `url` = `<folder>/<file>.heic`.
- `folder` (storage subfolder) can differ from the category key — e.g. category
  key `Cut Out`, folder `Cut Outs`.
- Referencing a **folder** of PNGs mirrors that folder's name into the bucket
  root: `convert_stickers` returns `suggestedFolder`, which you pass to
  `upload_stickers` as `folder` → `gs://<bucket>/<folder>/` + `thumbnails/<folder>/`.

## Typical flow (driven by Claude)

1. `convert_stickers` on the PNG file or folder.
2. `inspect_bucket stickers/<existing folder>/` to confirm the layout.
3. `upload_stickers` (try `dryRun` first) → collect returned `urls`.
4. Decide category placement with the user (new vs merge, order).
5. `update_sticker_categories dryRun=true` → review the diff.
6. `update_sticker_categories dryRun=false` to publish.