Skip to main content
Glama
stevegustafson32

Amazon Seller MCP

README.md
# Amazon Seller MCP

A self-hosted analytics brain for one Amazon seller account. It pulls your
Seller Central + Advertising data on a schedule into your own Postgres, then
serves it to Claude (or any MCP client) over a bearer-token-guarded HTTP endpoint
— so you can ask *"where am I wasting ad spend?"* and get an answer grounded in
your real numbers.

**Single-tenant by design.** One deployment = one Amazon account = your data,
your Railway project, your secrets. Nobody else authorizes your apps and no data
leaves your infrastructure.

## Quickstart

```bash
git clone https://github.com/stevegustafson32/amazon-seller-mcp && cd amazon-seller-mcp
cp .env.example .env                              # fill in your own credentials
docker compose up -d db                            # local Postgres (Railway provides its own)
export DATABASE_URL=postgresql://postgres:postgres@localhost:5432/amazon_seller_mcp
python -m pytest                                   # seeds fixtures, verifies all 7 tools
```

Deploying for real (non-technical)? Follow **[DEPLOY_GUIDE.md](DEPLOY_GUIDE.md)** —
it walks you click-by-click from zero to a live MCP your Claude can connect to.

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/amazon-seller-mcp)

> One click provisions all three components — Postgres + worker + mcp — and
> prompts you for every credential at deploy time (nothing is baked into the
> template). Prefer a guided walkthrough? Follow the click-by-click
> **[DEPLOY_GUIDE.md](DEPLOY_GUIDE.md)**.

## How it works

```
Your Amazon account            Your Railway project                  Your Claude / Cowork
-------------------            --------------------                  --------------------
SP-API app  (self-authorized) ─┐   ┌─ worker (cron ingestion) ─┐
Ads API app (refresh token)   ─┼─► │  Postgres (your data)     │ ─► MCP web service ─► custom
Data Kiosk / Pricing          ─┘   └─ writes normalized tables ┘     (HTTP + bearer)     connector
```

Claude never calls Amazon live. The **worker** owns every Amazon call — auth,
pagination, throttling, retries — and writes normalized tables. The **MCP web
service** only *reads* Postgres, except the single approval-gated write path
(propose → confirm) that changes a bid.

## Tools your Claude gets

**Read**
- `query_ads_performance` — ACOS / ROAS / CTR / CVR by campaign, target, or search term
- `find_search_term_waste` — spend with zero conversions, ranked
- `find_keyword_harvest` — converting search terms to promote to exact keywords
- `find_cannibalization` — terms you win organically yet still pay to advertise *(Brand Registry)*
- `get_sqp_share` — Search Query Performance share *(Brand Registry)*
- `get_buybox_status` — who owns the Buy Box, and whether it's you
- `get_pnl_snapshot` — sales, ad spend, ACOS, TACOS vs. break-even per ASIN

**Write (approval-gated)**
- `propose_bid_change` → returns a diff and a one-time token. Writes nothing.
- `confirm_bid_change` → applies the change via Ads API, only with that exact token.

**Bookmarkable dashboard** — `GET /dashboard?token=<MCP_BEARER_TOKEN>` server-renders a
single self-contained page (KPI row, benchmark scorecard, 8-week trends, top books,
negative/harvest lists, Buy Box, inventory health) from your Postgres. Thresholds,
benchmarks, and which panels show all come from [`config/profile.yaml`](config/profile.yaml).
Add `?range=<days>` to change the KPI window. Bookmark `{APP_URL}/dashboard?token=…`.

## Layout

```
amazon_seller_mcp/
  shared/      config.py (env validation), db.py (pool + migrations), lwa.py (token exchange)
  ingestion/   sp_reports, sp_datakiosk, sp_pricing, ads_reports, ads_entities, scheduler
  mcp/         server.py (remote MCP + bearer auth), tools.py, metrics.py, ads_auth.py (hosted OAuth)
db/            migrations/ (idempotent DDL), views.sql (metric layer), fixtures/ (seed data)
config/        profile.yaml (business defaults + Good/Watch/Fix thresholds)
scripts/       check_setup.py, get_ads_token.py, backfill.py
tests/         all 7 read tools + write path + auth + profile, against fixtures
```

> **Note on the package name.** The build spec named the server folder `mcp/`,
> which would shadow the official `mcp` SDK package on import. Everything is
> nested under the `amazon_seller_mcp` package so every spec folder name is
> preserved while the SDK stays importable. Commands are therefore
> `python -m amazon_seller_mcp.mcp.server` and
> `python -m amazon_seller_mcp.ingestion.scheduler`.

## Configure your business — `config/profile.yaml`

The analytics layer reads defaults and thresholds from
[`config/profile.yaml`](config/profile.yaml) instead of hard-coding them. It
ships with sensible Books/seller-publisher defaults so everything runs before you
touch it. Edit it to set your `business_model`, unit economics (which compute your
default **break-even ACOS** and **target ACOS**), the benchmark set, per-metric
**Good / Watch / Fix** thresholds, and which panels are enabled (turn off the
Brand-Registry panels if you're not enrolled). Per-SKU rows in the `sku_margins`
table override the profile's break-even for that SKU.

## Security

- The MCP service rejects any request without the correct `MCP_BEARER_TOKEN`. Tokens are never logged.
- Every account-mutating action goes through propose → human approval → confirm. No silent writes.
- Official Amazon APIs only. No browser automation against Seller Central.
- `check_setup.py` and `get_ads_token.py` print secrets to your own terminal only.

### Getting the Ads refresh token — two ways

The Ads API needs a browser OAuth round-trip (SP-API doesn't). Either works:

- **Hosted (no terminal):** once deployed, visit `https://YOUR-URL/setup/ads-auth?token=YOUR_MCP_BEARER_TOKEN`. It walks you through Amazon consent and renders your `ADS_REFRESH_TOKEN` + `ADS_PROFILE_ID` to paste into Railway. The route is bearer-guarded to start; the Amazon callback is authorized by a single-use `state` it issued.
- **Local fallback:** `python scripts/get_ads_token.py` does the same via a localhost callback before you deploy.

## Development

`python -m pytest` needs a Postgres (`docker compose up -d db`) and
`TEST_DATABASE_URL`/`DATABASE_URL` pointing at it. Tests seed `db/fixtures/` and
assert exact results — so the analytics are verifiable before any real Amazon
credentials exist. CI (`.github/workflows/ci.yml`) runs the same on every push.

MIT licensed. See [LICENSE](LICENSE).