Skip to main content
Glama
luke-nielsen

walmart-mcp

by luke-nielsen
README.md
# walmart-mcp

A TypeScript Walmart API client, delivered as an **MCP server** (stdio) with a reusable SDK
underneath. It wraps **two Walmart APIs** behind one client:

- **Marketplace** (seller) — manage your Walmart Marketplace catalog: items, inventory, prices,
  and orders. OAuth 2.0.
- **Affiliate / Catalog** (consumer) — search the public Walmart catalog: product search,
  lookup, taxonomy, trending, reviews, and store locator. Read-only, RSA-signature auth.

Configure **either or both** surfaces. Tools register only for the surfaces you've configured.

## Why this one

- **Importable SDK, not just a server.** `client/` + `api/` are a standalone, fully unit-tested
  Walmart SDK with **zero MCP knowledge** — `import { WalmartSdk }` and use it in any Node
  project. The MCP server is a thin wrapper.
- **Minimal footprint.** Two runtime deps total (`@modelcontextprotocol/sdk` + `zod`). RSA
  signing and correlation UUIDs use Node's built-in `crypto`; `.env` is loaded via `--env-file`,
  no `dotenv`/`node-rsa`/`uuid`.
- **Two auth schemes done right.** Marketplace's OAuth token is cached and auto-refreshed on
  `401`; the Affiliate signature is regenerated (with a fresh timestamp) on every request.
- **Curated outputs.** Search/list tools return compact, schema-validated `structuredContent`
  instead of raw, deeply-nested Walmart payloads, so agents don't shell out to parse JSON.

## Which surface do I want?

| You are… | Use | Credentials from | Auth |
|---|---|---|---|
| A **Walmart seller** managing your listings/orders | Marketplace | [developer.walmart.com](https://developer.walmart.com/) → API Key Management | OAuth 2.0 (Client ID + Secret) |
| An **affiliate / app** reading the public catalog | Affiliate | [walmart.io](https://walmart.io/) (program approval required) | RSA signature (Consumer ID + private key) |

## Setup

### 1. Install & build

```bash
npm install
npm run build
```

### 2. Configure credentials

```bash
cp .env.example .env   # then fill in whichever surface(s) you use
```

```
# Marketplace (seller)
WALMART_CLIENT_ID=...
WALMART_CLIENT_SECRET=...

# Affiliate / Catalog (consumer)
WALMART_CONSUMER_ID=...
WALMART_PRIVATE_KEY=...        # single-line Base64 PKCS#8 from the portal (or a full PEM)
WALMART_KEY_VERSION=1
```

> **The server reads credentials from its environment; it does not auto-load `.env`.** Your MCP
> client supplies them via the config `env` block (step 4). For the CLI and standalone runs,
> Node's `--env-file=.env` loads them (the `npm run check` / `npm start` scripts do this).

Notes:
- **Private key handling.** `WALMART_PRIVATE_KEY` accepts the single-line Base64 key the
  Walmart.io portal issues, or a full `-----BEGIN PRIVATE KEY-----` PEM. It's never logged. The
  matching public key is uploaded to the portal; `WALMART_KEY_VERSION` is shown next to it.
- **Marketplace tokens** are short-lived (~15 min) and cached in your OS data dir
  (`$XDG_DATA_HOME/walmart-mcp` on Unix, `%APPDATA%\walmart-mcp` on Windows; files chmod `0600`),
  then auto-refreshed on use.

### 3. Validate (optional but recommended)

```bash
npm run check
```

Mints a Marketplace token (proves CLIENT_ID/SECRET) and signs a live Affiliate request (proves
CONSUMER_ID + private key), skipping whichever surface you didn't configure. Expected output:

```
Marketplace  validating OAuth client credentials… OK
Affiliate    signing a taxonomy request… OK
✅ Configured surfaces validated. The MCP server reuses these same credentials.
```

### 4. Register with your MCP client

```json
{
  "mcpServers": {
    "walmart": {
      "command": "node",
      "args": ["/absolute/path/to/walmart-mcp/dist/mcp/server.js"],
      "env": {
        "WALMART_CLIENT_ID": "...",
        "WALMART_CLIENT_SECRET": "...",
        "WALMART_CONSUMER_ID": "...",
        "WALMART_PRIVATE_KEY": "...",
        "WALMART_KEY_VERSION": "1"
      }
    }
  }
}
```

### 5. Run

Your MCP client launches `node dist/mcp/server.js`, injects the env vars, and the server
registers the tools for each configured surface. (To run standalone for testing: `npm start`.)

## Tools

### Affiliate / Catalog (read-only)

| Tool | Purpose |
|------|---------|
| `affiliate_search` | Full-text catalog search (compact results) |
| `affiliate_product_lookup` | Look up products by item id(s), UPC, or GTIN |
| `affiliate_taxonomy` | The catalog category tree |
| `affiliate_trending` | Currently trending products |
| `affiliate_reviews` | Customer reviews for a product |
| `affiliate_stores` | Walmart stores near a lat/long, city, or ZIP |

### Marketplace (seller)

| Tool | Auth | Purpose |
|------|------|---------|
| `mp_get_items` | OAuth | List your catalog (compact, cursor-paginated) |
| `mp_get_item` | OAuth | One catalog item by SKU |
| `mp_get_inventory` | OAuth | A SKU's on-hand inventory |
| `mp_update_inventory` | OAuth | **Write:** set a SKU's available quantity |
| `mp_update_price` | OAuth | **Write:** update a SKU's price |
| `mp_get_orders` | OAuth | List orders by date/status (compact, cursor-paginated) |
| `mp_get_order` | OAuth | One order by purchaseOrderId |
| `mp_acknowledge_order` | OAuth | **Write:** acknowledge an order (required before shipping) |

Search/list tools return **compact, structured results** (`structuredContent` validated by an
output schema) — just the fields the workflow needs — rather than the raw, deeply-nested Walmart
payload.

## Troubleshooting

| Symptom | Cause / fix |
|---------|-------------|
| Server logs **"No Walmart credentials found"** | Neither surface configured. Fill the config `env` block (MCP) or `.env` (standalone). |
| A tool returns **"… credentials are not configured"** | That surface's env vars are missing; the other surface still works. |
| **`401` / token errors** on Marketplace | Client ID/Secret wrong or revoked. Re-check with `npm run check`. The client already refreshes a normal expiry automatically. |
| **`401` / signature errors** on Affiliate | Wrong Consumer ID, key version, or private key; or large clock skew (timestamp TTL ≈ 180s). Verify with `npm run check`. |
| **"Could not parse the affiliate private key"** | `WALMART_PRIVATE_KEY` isn't the Base64 PKCS#8 portal key or a valid PEM. |
| MCP server **won't start / behaves like old code** | `dist/` is missing or stale — the MCP launch runs the **built** file and does not auto-build. Run `npm run build`. |
| `--env-file=.env` errors with **ENOENT** | `.env` doesn't exist. `cp .env.example .env` and fill it in. |

## Development

```bash
npm test         # vitest (fetch mocked, throwaway RSA keypair) — no credentials needed
npm run typecheck
npm run build    # also runs on install (prepare) and before start/check
```

## Architecture

```
src/
  client/   WalmartClient, marketplaceAuth (OAuth), affiliateAuth + signature (RSA),
            tokenStore, correlation, errors
  api/      marketplace/{items,inventory,prices,orders}, affiliate/catalog   (zero MCP knowledge)
  types/    request/response shapes
  mcp/      server.ts (stdio) + config.ts + tools.ts + format.ts
  sdk.ts    WalmartSdk facade
```

The `client/` + `api/` layers are a standalone SDK, fully unit-tested with `fetch` mocked;
`mcp/` is a thin wrapper. Import the SDK directly via the package root (`WalmartSdk`).