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

grocery-delivery-mcp

v0.2.0

Published

MCP server + CLI for mein-aldi.de (ALDI Süd grocery delivery): product search, price & availability, categories. Public API, cached and rate-limited. Unofficial, unaffiliated.

Readme

grocery-delivery-mcp

npm MCP Registry License

Ask your AI assistant "is oat milk in stock for delivery?" — and get a real answer, with today's price.

This is an MCP server, CLI, and library for mein-aldi.de, ALDI Süd's grocery delivery platform. It lets a language model (or you, from a terminal) search the catalog, check price and availability, and browse categories.

Under the hood the catalog talks to the public api.mein-aldi.de JSON API — no login, no account, no scraping tricks. Search, price, and availability are all publicly readable. Every request is rate-limited, retried, and circuit-broken to stay polite to an undocumented API, and responses are cached (in memory and on disk) so repeat questions are instant. There's also an opt-in cart prefill for your own account — the one part that logs in.

Unofficial and unaffiliated with ALDI. It uses an undocumented API that may change or break without notice. Prices and availability are guidance, not a real-time inventory guarantee. See COMPLIANCE.md for exactly how it accesses the API responsibly — robots.txt, terms, and rate-limit posture.

Add it to your LLM

No install, no API key, no configuration. Point any MCP client at the package and start asking.

Claude Code — one command:

claude mcp add grocery-delivery -- npx -y grocery-delivery-mcp

Claude Desktop, Cursor, or any other MCP client — add this to your MCP config (e.g. claude_desktop_config.json):

{
  "mcpServers": {
    "grocery-delivery": {
      "command": "npx",
      "args": ["-y", "grocery-delivery-mcp"]
    }
  }
}

Restart the client and ask: "Is oat milk in stock for delivery on ALDI, and what does it cost?" That's it. 🎉

Or find it in the registry. It's published in the official MCP Registry as io.github.RayNCooper/grocery-delivery-mcp, so registry-aware clients can discover and install it by name.

Prefer the terminal or want to embed it in your own code? See the Quickstart.

Contents

What you can do with it

Connected to an MCP client like Claude Desktop, your assistant can answer questions like:

  • "Is bio milk available for delivery right now, and what does it cost?"
  • "Find me the five cheapest options for pasta."
  • "What's on sale at ALDI right now?"
  • "How much fat and sugar are in these chips, and what are the allergens?"
  • "What product categories are available?"

Straight from a terminal, the same thing:

grocery-delivery search "milch" --limit 5      # search the catalog
grocery-delivery check "bio bananen"           # is the top match in stock?
grocery-delivery offers --sort price_asc       # current deals, cheapest first
grocery-delivery product 434365                # full detail: nutrition, allergens
grocery-delivery categories --depth 2          # browse the category tree

Or from your own code, as a library — see Ways to use it.

Quickstart

Wiring it into an LLM is the one-liner above. This is the terminal / CLI path. For the read-only catalog (steps 1–3) you only need Node ≥ 22 — nothing to clone, build, or configure; the store is baked in (see Limitations). Cart prefill (step 4) is the one exception: it's authenticated and runs from a clone.

1. Install

npm install -g grocery-delivery-mcp

That puts the grocery-delivery command (and the grocery-delivery-mcp server binary) on your PATH.

2. Check it works

grocery-delivery doctor
# Checking https://api.mein-aldi.de (service point ADG045_1) ...
# ✓ categories: 32 roots
# ✓ search "milch": 25 hits
# ✓ The mein-aldi.de API is live and serving data — you're good to go.

doctor is a connectivity check — it exits non-zero if the API returns nothing, so scripts can gate on it.

3. Use it

grocery-delivery search "kaffee" --limit 5
grocery-delivery check "bio bananen"
grocery-delivery offers --sort price_asc --limit 10   # cheapest current deals
grocery-delivery product 434365                       # nutrition, ingredients, …
grocery-delivery categories --depth 2

4. Fill your own cart

Your assistant can also prefill your own mein-aldi.de cart — via two tools: connect_aldi_account (opens ALDI's login in a browser, once) and prefill_cart (adds items). You still check out, pay, and pick a slot yourself; it never places an order.

Run the server from a clone (the cart tools aren't in the published npm build yet), plus a browser engine for the login:

git clone https://github.com/RayNCooper/grocery-delivery-mcp.git
cd grocery-delivery-mcp && pnpm install && pnpm build
npx playwright install chromium          # the browser used for the ALDI login

Point your MCP client at it — no session file, no extra config:

{
  "mcpServers": {
    "grocery-delivery": {
      "command": "pnpm",
      "args": ["-C", "/abs/path/to/grocery-delivery-mcp", "start"]
    }
  }
}

Then just chat: "Add 2× oat milk, bananas, and coffee to my ALDI cart." The first time, the assistant offers to connect — a browser window opens on the ALDI login, you sign in once, and it's remembered (and refreshed automatically after). Your cart fills; open mein-aldi.de to finish. Details + compliance scope: docs/CART-PREFILL.md.

See Ways to use it for the full command list, the HTTP transport, and the library API.

Ways to use it

One shared core, consumed three ways.

Local MCP server (stdio)

For Claude Desktop, Cursor, and other desktop MCP clients. Add it to your LLM shows the npx config, which needs no install and is the recommended setup. If you've installed the package globally, you can run the server directly instead:

grocery-delivery-mcp        # the MCP server (stdio) — same as: grocery-delivery serve

One-click install: a prebuilt MCPB bundle (.mcpb) installs into Claude Desktop with a double-click — no configuration — built from manifest.json and attached to releases.

Remote MCP server (Streamable HTTP)

For networked or hosted use.

grocery-delivery serve --http
# → HTTP server listening on http://127.0.0.1:3000/mcp

Point a Streamable-HTTP MCP client at http://127.0.0.1:3000/mcp. There's also a GET /health endpoint, and sessions are keyed by the Mcp-Session-Id header.

Self-host with Docker: docker compose up (see docker-compose.yml and Dockerfile) runs your own single-tenant instance on your own IP. It's deliberately not a shared multi-user service; COMPLIANCE.md explains why.

A note on security: the server binds to 127.0.0.1 by default and enforces DNS-rebinding protection (allowedHosts). It has no built-in auth — if you expose it beyond localhost (ALDI_HTTP_HOST=0.0.0.0), put it behind a reverse proxy that adds auth and TLS.

CLI & library

Install globally (npm install -g grocery-delivery-mcp) for the grocery-delivery command, or run any command ad-hoc with npx -p grocery-delivery-mcp grocery-delivery <cmd>. Every command supports --json for machine-readable output and --force-refresh to bypass the cache.

grocery-delivery search "milch" --limit 5
grocery-delivery search "joghurt" --diet Vegan --sort price_asc   # filter + sort
grocery-delivery search "wasser" --limit 10 --offset 10          # page 2
grocery-delivery stock 229396 000000000000228798      # availability + price by SKU
grocery-delivery check "bio bananen"                  # is the top match available?
grocery-delivery product 434365                       # full detail: nutrition, allergens
grocery-delivery offers --sort price_asc --limit 10   # current deals, cheapest first
grocery-delivery categories --depth 2
grocery-delivery browse 19000000 --limit 20 --brand MILSANI      # filter a category
grocery-delivery status                               # cache / breaker / rate-limit
grocery-delivery doctor                               # health check (API reachable?)
grocery-delivery <cmd> --json                         # machine-readable output

search and browse accept --sort (relevance|price_asc|price_desc|name_asc|name_desc), server-side --offset paging, and the facet filters --label (Bio|Neu|Aktion), --diet, --brand, and --country. Text and --json output both include the available facets (brands, diet types, labels, with counts) so you can see how to refine.

Use the same core from your own code — npm install grocery-delivery-mcp, then:

import { createService, loadConfig } from "grocery-delivery-mcp";

const service = createService(loadConfig(process.env));
const result = await service.search({ query: "milch", limit: 5 });
console.log(result.products, result.cacheState);

Tools & resources

The MCP server exposes these tools:

| Tool | What it does | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | search_products | Free-text search → products + facets; label/diet/brand/country filters, sort, paging | | get_products | Exact lookup by SKU(s) — "check stock" for known items | | get_product_details | Full detail by SKU: nutrition, ingredients, allergens, storage, origin | | check_availability | One-shot "is X available right now?" (search → top match → live check) | | list_offers | Current promotional products ("Aktion") — the "what's on sale" list | | list_categories | Browse the category tree (with numeric keys) | | browse_category | List products in a category — same filters, sort, and paging as search | | get_delivery_slots | Delivery time-slots — experimental (see Limitations) | | get_service_status | Diagnostics: cache, breaker, rate-limit — plus store metadata from /v1/stores | | connect_aldi_account | Connect your ALDI account (opt-in) — opens ALDI's login in a browser so you sign in once; remembers the session for cart prefill — see Fill your own cart | | prefill_cart | Prefill your own cart (authenticated, opt-in) — add products by name/SKU; you still check out. Prompts to connect first if needed |

And these resources: aldi://categories (the full tree), aldi://product/{sku} (price & availability), aldi://product-details/{sku} (full detail), and aldi://offers (current deals).

Every returned product carries the usual price and availability plus depositDisplay (Pfand), sizeDisplay (pack size), labels (badges like Bio / Aktion), and deepLink (the mein-aldi.de product page). Every result carries a cacheState (fresh / stale / miss / bypass / degraded) and a fetchedAt timestamp, so a model always knows how fresh a price is. Pass force_refresh: true (or --force-refresh) when you need an authoritative, up-to-the-second read.

Configuration

Everything is set through environment variables, and every default is sensible — out of the box, there's nothing you need to set. The full list with defaults lives in .env.example. The ones you're most likely to touch:

| Env | Default | Meaning | | ----------------------------------- | -------------------- | ----------------------------------------------- | | ALDI_SERVICE_TYPE | delivery | delivery (verified), pickup, instore | | ALDI_CACHE_DIR | OS cache dir | Where the on-disk cache lives | | ALDI_CACHE_DISABLED | false | Turn the cache off | | ALDI_TTL_PRODUCTS_MS | 300000 | Price/availability freshness (5 min) | | ALDI_TTL_SEARCH_MS | 600000 | Search freshness (10 min) | | ALDI_TTL_CATEGORY_MS | 86400000 | Category tree freshness (24 h) | | ALDI_RATE_MIN_INTERVAL_MS | 250 | Min spacing between requests (~4 rps) | | ALDI_HTTP_HOST / ALDI_HTTP_PORT | 127.0.0.1 / 3000 | HTTP transport bind | | ALDI_LOG_LEVEL | info | silent/error/warn/info/debug (stderr) |

How it stays polite and fast

It treats an undocumented, unowned API with care, and caches aggressively so it rarely has to ask twice:

  • Rate limiting — a minimum interval plus jitter between request starts, with capped concurrency.
  • Retries — exponential backoff (equal jitter) on 429/5xx/network/timeout, honoring Retry-After; fails fast on other 4xx.
  • Single-flight — identical concurrent requests share one underlying fetch.
  • Circuit breaker — after repeated failures it backs off, and tools degrade to best-effort/empty results (degraded: true) instead of erroring.
  • Cache — memory + disk, per-resource TTLs, and stale-while-revalidate (serve stale instantly, refresh in the background). Delivery slots are never served stale.

Limitations

  • Delivery slots (get_delivery_slots) aren't implemented yet — no public endpoint is confirmed. The tool returns supported: false with a reason. See scripts/slots-probe.mjs and Development.
  • Account features are limited to cart prefill (opt-in). The catalog side is read-only and never logs in. A separate, opt-in flow prefills your own cart — you authenticate once, and it never checks out, pays, or places an order (see Fill your own cart). Checkout, payment, and order history stay out of scope. COMPLIANCE.md covers the access posture.
  • One store, baked in. mein-aldi.de delivery is a single-store pilot: the API serves exactly one live merchant (ADG045_1), identical for every address (the postal code is ignored, and no other store resolves). So there's nothing to select — the service point is hardcoded and there's no ALDI_SERVICE_POINT setting. If ALDI expands delivery to multiple stores, this becomes configurable again.

Development

Working on the project itself? Build from source — this needs pnpm:

git clone https://github.com/RayNCooper/grocery-delivery-mcp.git
cd grocery-delivery-mcp
pnpm install
pnpm build               # compiles TypeScript → dist/

Then the usual loop:

pnpm dev                 # tsx watch src/stdio.ts
pnpm dev:http            # tsx watch src/http.ts
pnpm typecheck
pnpm test                # unit tests (no network)
pnpm test:integration    # live API, gated by ALDI_INTEGRATION=1, serial + rate-limited
pnpm lint
pnpm format

# Phase 2: discover the delivery-slots endpoint (read-only probe)
node scripts/slots-probe.mjs

Integration tests hit the real API and are gated behind ALDI_INTEGRATION=1 — keep them out of hot CI loops to avoid hammering ALDI. Architecture notes live in docs/ARCHITECTURE.md, and contributions are welcome — see CONTRIBUTING.md.

Releasing

Releases are driven by the VERSION file. Bump it, and merging to main triggers .github/workflows/publish.yml, which stamps that version into package.json and server.json, publishes the package to npm, and publishes the entry to the MCP Registry via mcp-publisher. Both artifacts always match VERSION, so they can't drift.

# from the /ship flow, or by hand:
echo "0.2.0" > VERSION   # (also bump package.json / CHANGELOG)
git commit -am "release: v0.2.0" && git push   # merge to main → publish

The job is idempotent — if the version is already on npm it skips cleanly, so re-runs stay green. No secrets are required: npm uses trusted publishing (OIDC) and the MCP Registry uses GitHub OIDC — both ride the workflow's id-token: write permission, and npm provenance is generated automatically. The one prerequisite is that the npm trusted publisher is configured on npmjs.com to point at this repo and the publish.yml workflow.

License

GPL-3.0-or-later. See LICENSE.