npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

intermarche-drive-mcp

v0.1.0

Published

MCP server that drives Intermarché Drive: search, cart, slots, prepare checkout (one click short of placing the order).

Readme

Intermarché Drive MCP Server

An MCP (Model Context Protocol) server that lets an LLM agent do your grocery shopping on Intermarché Drive end-to-end: search products, build a cart, pick a collection slot, and place the order.

The goal is: say "order this week's groceries: milk, eggs, chicken, the usual veggies, and pick it up Saturday morning" to Claude/Cursor/your agent of choice, and have it happen.


Installation

There are two ways to run the server: from source (recommended for development and the first install) or via Docker Compose (recommended for keeping it running 24/7). Either way you need a Chromium-based browser logged in to intermarche.com — the server never sees your password; cookies are pushed by a tiny browser extension.

Prerequisites

| | Source install | Docker install | |---|---|---| | Node 22+ | yes | not needed | | Docker + Compose | not needed | yes | | Chrome / Edge / Brave (logged in to intermarche.com) | yes | yes | | Your 5-digit Intermarché store code | yes | yes |

To find your store code: log in to intermarche.com, set your active store from the header, then look at the URL on any product page — pdvRef=NNNNN or the last segment of a product URL is the code. (Example: 09571 for Hyper Toulouse Lardenne, 11131 for Super Ramonville Saint-Agne.)

Option A — Run from npm (no clone, no build)

Once published, the whole server runs from npx — no need to clone or check out the repo. Skip straight to Step 3 with your MCP client:

# Claude Code
claude mcp add intermarche-drive \
  --env STORE_ID=09571 \
  -- npx -y intermarche-drive-mcp --stdio

# direct invocation (smoke test, --http, MCP Inspector, etc.)
STORE_ID=09571 npx -y intermarche-drive-mcp --http

You'll still need the cookie-sync extension (Step 2) — it lives outside npm because Chromium requires a loaded-unpacked folder. Grab it from the GitHub repo's extension/ folder or from your package install at $(npm root -g)/intermarche-drive-mcp/extension/.

State (cookies + picked slot) is persisted to $HOME/.intermarche-drive-mcp/state.json so it survives npx cache resets. Override with the STATE_FILE env var.

For maintainers: until npm publish has been run once, this path doesn't work yet — use Option B or C below. The package is wired (bin, files allowlist, prepublishOnly guard) and ready to ship.

Option B — Install from source

git clone https://github.com/TsRun/intermarche-drive-mcp.git
cd intermarche-drive-mcp
npm install
npm run build

Set the required env var and (optionally) override the defaults:

# required
export STORE_ID=09571            # your 5-digit store code

# optional — these are the defaults
export PORT=7801                 # streamable-http MCP port
export EXTENSION_PORT=7800       # cookie-sync sidecar (loopback only)
export REQUEST_DELAY_MS=500      # rate limit per request — do not lower
export LOG_LEVEL=info
export CHECKOUT_URL=https://www.intermarche.com/courses-en-ligne

Run the server:

# stdio transport (for Claude Desktop, Cursor, etc.)
node dist/server.js --stdio

# OR streamable-http transport (for MCP Inspector, smoke.sh, remote clients)
node dist/server.js --http

Skip to Step 2 — Install the cookie-sync extension.

Option C — Install with Docker

git clone https://github.com/TsRun/intermarche-drive-mcp.git
cd intermarche-drive-mcp
STORE_ID=09571 docker compose up -d --build

The compose file exposes the MCP HTTP transport on localhost:7801 and the cookie-sync sidecar on 127.0.0.1:7800 (loopback only — the extension talks to this from your browser). Cookies and lists are persisted to ./data/.

Check it's healthy:

docker compose logs -f
# Expect: {"level":"info","msg":"server_ready","transport":"http","port":7801}

Step 2 — Install the cookie-sync extension

The server doesn't accept passwords. Instead, a small unpacked Chromium extension reads your *.intermarche.com cookies and posts them to the sidecar on 127.0.0.1:7800 whenever you click Sync now.

  1. Open chrome://extensions (or edge://extensions, brave://extensions).
  2. Toggle Developer mode on.
  3. Click Load unpacked and pick the extension/ folder of this repo.
  4. Log in to intermarche.com in any tab and set your store.
  5. Click the extension's toolbar icon → Sync now.
  6. The popup should report ✓ Logged in to <store name> (N cookies).

If you changed EXTENSION_PORT, see extension/README.md for the two-line patch needed.

Step 3 — Register it with your MCP client

Claude Code (one command)

If you use the Claude Code CLI, register the server in one line — no JSON editing:

# stdio (recommended — local launch, one process per Claude Code session)
claude mcp add intermarche-drive \
  --env STORE_ID=09571 \
  -- node /absolute/path/to/intermarche-drive-mcp/dist/server.js --stdio

# OR HTTP, against a long-running server (e.g. docker compose up)
claude mcp add --transport http intermarche-drive http://localhost:7801/mcp

Then verify in any Claude Code session:

/mcp

You should see intermarche-drive listed with its tools (whoami, set_store, search_products, ...). To remove it later: claude mcp remove intermarche-drive.

If you took Option A (npm), the stdio command is: claude mcp add intermarche-drive --env STORE_ID=09571 -- npx -y intermarche-drive-mcp --stdio

Claude Desktop

Edit claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "intermarche-drive": {
      "command": "node",
      "args": ["/absolute/path/to/intermarche-drive-mcp/dist/server.js", "--stdio"],
      "env": {
        "STORE_ID": "09571"
      }
    }
  }
}

Restart Claude Desktop. The Intermarché tools appear in the tool list (look for whoami, set_store, search_products, etc.).

If you're running via Docker (HTTP transport), point Claude at the URL instead:

{
  "mcpServers": {
    "intermarche-drive": {
      "url": "http://localhost:7801/mcp"
    }
  }
}

OpenAI Codex CLI

Codex reads MCP server definitions from ~/.codex/config.toml. Append:

[mcp_servers.intermarche-drive]
command = "node"
args = ["/absolute/path/to/intermarche-drive-mcp/dist/server.js", "--stdio"]
env = { STORE_ID = "09571" }

For an HTTP server (e.g. docker compose up), use:

[mcp_servers.intermarche-drive]
url = "http://localhost:7801/mcp"

Start a Codex session (codex) and check the tool list with /mcp — same verification flow as Claude Code. (Newer Codex builds also expose codex mcp add ... as a one-liner; the TOML snippet above always works.)

Cursor

In ~/.cursor/mcp.json (or your workspace's .cursor/mcp.json):

{
  "mcpServers": {
    "intermarche-drive": {
      "command": "node",
      "args": ["/absolute/path/to/intermarche-drive-mcp/dist/server.js", "--stdio"],
      "env": { "STORE_ID": "09571" }
    }
  }
}

Open Cursor's command palette → Cursor Settings → MCP to reload.

Step 4 — Verify

In your MCP client, ask the agent "call whoami". You should get back { logged_in: true, cookie_count: <N>, store_id: "09571" }. If logged_in: false, click Sync now in the browser extension again.

For an end-to-end check (live account, never spends money):

LIVE=1 STORE_ID=09571 SMOKE_STORE_ID=09571 ./smoke.sh

The script ends by printing a checkout_url. Open it in your browser to click Payer; this server has no place_order tool by design.

Publishing to npm (maintainer only)

Anyone with publish rights on intermarche-drive-mcp can ship a new version:

npm login                           # one time
npm version patch                   # or minor / major
npm publish                         # runs typecheck + lint + test + build first
git push --follow-tags

The prepublishOnly script blocks publish on a red build, lint failure, or test failure. files in package.json is an allowlist (dist/, extension/, README.md, LICENSE) so source, tests, fixtures, and .claude/ never ship.

Troubleshooting

  • logged_in: false after sync — verify the extension popup reports the same store as your STORE_ID. The sidecar rejects sync attempts where /api/pdv-info returns a different ref.
  • add_to_cart returns an empty cart — the active store doesn't sell that product in its Drive catalog. Call set_store against a Drive-capable store (supportsDrive: true in the response) and search again.
  • 429 errors — you have another tool or script hitting intermarche.com. This server's rate limit (1 req / 500ms) is enforced per process; concurrent processes share the upstream's budget. Stop the other client.
  • State file conflicts — delete data/state.json to start fresh; the extension will repopulate it on the next sync.

Task

Build a Model Context Protocol server (spec: https://modelcontextprotocol.io) that exposes a set of tools for driving Intermarché's click-and-collect service. The server runs locally, holds a persistent authenticated session to intermarche.com, and performs actions on the user's behalf.

Clients (Claude Desktop, Cursor, any MCP-capable agent) connect over stdio or streamable-http and call the tools the server exposes.

Tech stack

  • TypeScript + Node 22 + @modelcontextprotocol/sdk (official MCP SDK).
  • Playwright (chromium, headless by default) for browser automation.
    • If a clean internal JSON API can be reverse-engineered, prefer undici fetch calls and keep Playwright as fallback for login / captcha / pages without a clean API.
  • Zod for tool-input schemas (MCP SDK integrates with it natively).
  • Persistent storage: single data/state.json (session cookies, last store, shopping lists).
  • Dockerfile + docker-compose.yml for headless deployment.

Transports

Support both:

  • stdio — for Claude Desktop and most local MCP clients.
  • streamable-http on PORT (default 7801) — for remote use and debugging with MCP Inspector.

The transport is chosen via CLI flag: intermarche-drive-mcp --stdio or --http.

Tools exposed

All tool names use snake_case. Every input is validated with Zod. Every response is JSON, never a giant string blob.

This is intentionally minimal: search, fill cart, see/clear cart. Checkout (placing the order) is left to the human in the browser — the agent prepares the basket, you click "pay".

Session

| Tool | Inputs | Returns | |---|---|---| | whoami | — | { logged_in, cookie_count, store_id } | | set_store | { storeId } (5-digit) | { ok, storeId, storeName, driveType, supportsDrive } |

Catalog

| Tool | Inputs | Returns | |---|---|---| | search_products | { query, limit?, page? } | { total, page, totalPages, products: [...] } |

Cart

| Tool | Inputs | Returns | |---|---|---| | get_cart | — | { storeCode, lines: [...], subtotal, fees, total } | | add_to_cart | { product_id, qty } (1–99) | updated cart | | update_cart_item | { product_id, qty } (0–99, 0 = remove) | updated cart | | clear_cart | — | { ok: true } | | list_slots | — | { storeId, slots: [{ slotId, deliveryType, day, start, end, available, ... }] } | | pick_slot | { slotId } | { ok, slot: ConfirmedSlot } — persisted to state.json, no server reservation | | prepare_checkout | — | { ready, summary, blockers, checkout_url } — open URL to click Pay |

Orders

| Tool | Inputs | Returns | |---|---|---| | list_orders | { limit?, months? } | array of { orderId, total, itemCount, pickupDate, pickupSlot, status, ... } |

Prepare-and-handoff checkout

prepare_checkout is deliberately not a place-order tool. There is no place_order tool — the MCP server stops one click short of spending money. The flow:

  1. list_slotspick_slot (validated and persisted to state.json; no server-side reservation, intermarche.com has no such endpoint).
  2. prepare_checkout → re-verifies the slot, computes totals, returns a checkout_url (configurable via CHECKOUT_URL; defaults to https://www.intermarche.com/courses-en-ligne).
  3. Open the URL in your real browser (the one with the active session), click Payer. 3DS / SCA happens in your bank's UI, not here.

Blockers (no_slot, slot_expired, etc.) are returned in the payload, not thrown — the agent can pick a new slot without retrying.

Not in v1 (left to the user)

  • Clicking Payer. By design, there is no automated order submit. The agent prepares the basket; you finish in your browser.
  • Category browsing — deliberately out of scope. Add only when there's real demand.
  • get_order / reorder — order detail is server-rendered HTML on intermarche.com, not a JSON API. Would require either finding the SSR backend or scraping HTML. list_orders already exposes enough metadata (per-order total, item count, date, pickup slot, invoice URL) for an agent to summarize past orders.

Auth & credentials

This server does not handle your Intermarché password. Session cookies are synced from your logged-in browser via a small Chromium extension at extension/. Load it unpacked once; click the toolbar icon → "Sync now" whenever cookies expire. See extension/README.md for install steps.

Server-side, the cookies the extension POSTs are persisted to data/state.json only after a successful round-trip against /api/pdv-info. On verification failure, the previous cookies are restored — syncing a stale session won't wipe a working one.

PlaywrightDriver is bundled for future flows that need browser automation (e.g. handling pages without a clean JSON API), but the cookie-sync path does not use it.

Rate limiting & politeness

  • Hard cap of 1 request per 500ms against intermarche.com (configurable via env REQUEST_DELAY_MS).
  • Retries: max 2, exponential backoff, only on 5xx and network errors. Never retry on 4xx.
  • User-agent: a realistic Chrome UA, not a bot string. This is personal automation, not a crawler.
  • No concurrent scraping across tools — serialize HTTP requests through a single queue.

Error handling

  • Every tool returns either a success payload or throws an McpError with a clear message.
  • Network errors → { code: "upstream_unreachable", message, retryable: true }.
  • Auth errors → { code: "not_logged_in" } — the agent should call login and retry.
  • Out-of-stock products during checkout → partial success with a list of skipped items.

Observability

  • JSON logs to stderr (so they don't interfere with stdio transport).
  • One log line per tool call: { tool, duration_ms, ok, args_summary }.
  • Optional DEBUG=mcp:* flag for verbose Playwright traces saved to data/traces/.

Tests

  • Vitest for unit tests on parsers, cart math, and schema validation.
  • Playwright tests (tagged @live) that hit a staging/test account — skipped in CI unless LIVE=1 is set.
  • Contract tests: spawn the MCP server and exercise every tool via the official MCP test client, asserting against recorded fixtures.
  • Fixtures: record real intermarche.com HTML/JSON responses once, check them in under tests/fixtures/, never hit the real site in unit tests.

Deliverables

  1. src/ — MCP server (TypeScript, strict mode, no any).
  2. src/tools/ — one file per tool or per grouped-domain (catalog.ts, cart.ts, etc.).
  3. src/driver/ — abstraction layer with two implementations: HttpDriver (internal API) and PlaywrightDriver (browser). Tools depend only on the abstract Driver interface.
  4. Dockerfile, docker-compose.yml (with a volume for data/).
  5. README.md with: install, configure, register with Claude Desktop (claude_desktop_config.json snippet), register with Cursor, example prompts.
  6. CLAUDE.md — guidance for AI agents editing this repo.
  7. smoke.sh — end-to-end script: login → search → add to cart → dry-run checkout.
  8. Example claude_desktop_config.json snippet.

What to skip

  • No multi-account support. One user, one session.
  • No scheduling/cron inside this server — let the user/agent drive timing.
  • No price history / deal tracking (separate project).
  • No UI. Pure MCP server.
  • No web scraping of non-logged-in catalog at scale — this is for personal automation.

Legal / ToS

Automating your own grocery orders for personal use is the intended use. Do not:

  • Scrape the catalog at scale for resale or redistribution.
  • Run this server against someone else's account without permission.
  • Hammer the site with concurrent requests.

The rate limiter exists for a reason.

Definition of done

You should be able to:

  1. docker compose up -d and have a running MCP server over HTTP on localhost:7801.
  2. Add the stdio entry to claude_desktop_config.json and restart Claude Desktop.
  3. In Claude, say "log me in to Intermarché, switch to store 09571, search for whole milk, add 2 of the cheapest in-stock option to my cart, pick a Saturday morning slot, and prepare checkout — don't actually pay yet" and have every step actually happen.
  4. Say "prepare checkout" and the agent calls prepare_checkout. Open the returned checkout_url in your browser and click Payer.
  5. smoke.sh runs green and prints the same checkout_url.