Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Lidl

БесплатноНе проверен

Self-hosted MCP server for Lidl Plus (NL + DE) that reads receipts and manages coupons via the lidl-plus library, using refresh tokens for authentication.

GitHubEmbed

Описание

Self-hosted MCP server for Lidl Plus (NL + DE) that reads receipts and manages coupons via the lidl-plus library, using refresh tokens for authentication.

README

Self-hosted MCP-server voor Lidl Plus (NL + DE) — kassabonnen uitlezen en coupons beheren. Analoog aan ah-mcp, gebouwd op de reverse-engineered lidl-plus library. Zie LIDL_MCP_HANDOVER.md voor de volledige achtergrond en ontwerpbeslissingen.

Ontwerpkeuzes (uit de bouw-sessie)

  • NL + DE: één server, elke tool-call neemt een country-parameter (NL/DE).

  • Coupon-activatie is inbegrepen (lidl_activate_coupon / lidl_deactivate_coupon) — dit zijn schrijf-acties op het echte Lidl-account.

  • Geen database. Alles wordt live bij de Lidl-API opgehaald, geen lokale cache.

  • Login gebeurt buiten de container. De lidl-plus-library logt in via headless Selenium/Chrome met een synchrone 2FA-callback — te zwaar en te fragiel om in deze lichte MCP-image te bakken. In plaats daarvan: je haalt lokaal, eenmalig per land een refresh-token op via de CLI, en die zet je als environment variable in de Portainer-stack. Er is dus bewust geen lidl_login-tool.

    Refresh-tokens zijn niet officieel gedocumenteerd qua levensduur, maar in de praktijk (zelfde tokenmechanisme als de Home Assistant Lidl Plus-integratie) blijven ze weken tot maanden geldig — herhaling is incidenteel, geen doorlopende taak.

Tools

Tool Type Functie
lidl_check_status read Check of er een geldig token is voor NL/DE (eerste stap bij problemen)
lidl_get_receipts read Lijst recente kassabonnen (datum, winkel, totaal, ticket-id)
lidl_get_receipt_detail read Itemdetails van één bon — JSON via de mobiele API, of platte bontekst via de web-sessie-fallback bij HTML-format bonnen (zie verderop)
lidl_get_coupons read Lijst beschikbare/geactiveerde coupons
lidl_activate_coupon write Activeer een coupon op het echte account

De schrijf-tool is gemarkeerd met readOnlyHint: false in de tool-annotations, zodat MCP-clients die dat respecteren automatisch voorzichtiger zijn / om bevestiging vragen.

lidl_deactivate_coupon bestaat wél in de code (_handle_set_coupon(activate=False) in server.py) maar is bewust niet als tool geregistreerd — Steno wil deactivatie niet aanbieden, geen agent kan 'm dus aanroepen. Zie de code-commentaar ter plekke voor de reden.

Transports — twee endpoints, verschillende clients

De server biedt twee MCP-transports tegelijk aan (zie server_sse.py), elk voor een andere client:

Endpoint Transport Voor Secret-vorm
/sse Legacy SSE (GET /sse + los POST /messages) Home Assistant MCP Client-integratie query-param: ?key=<MCP_SSE_SECRET>
/mcp/<secret> Streamable HTTP (één endpoint, GET+POST+DELETE) Claude.ai Custom Connectors pad-segment: /mcp/<MCP_SSE_SECRET>

Beide gebruiken dezelfde MCP_SSE_SECRET-waarde, alleen anders doorgegeven.

Waarom twee transports? Claude.ai's Custom-Connector-setup verwacht standaard de modernere Streamable-HTTP-transport en doet een POST naar de opgegeven URL. Onze legacy-SSE-route ondersteunt alleen GET, dus dat werd 405, waarna Claude terugvalt op OAuth-discovery (.well-known/oauth-*, /register) — die altijd faalt (geen OAuth-server) en een misleidende "Couldn't register with Lidl's sign-in service"-foutmelding geeft. Bevestigd via live container-logs (2026-08-14), zelfde patroon als eerder bij ah-mcp opgelost. Zie de steno-infra-patterns-skill voor de generieke versie van deze les (geldt voor élke toekomstige zelf-gehoste MCP-server, niet alleen dit project).

Onderweg ook een losstaande bug gevonden en gefixt: de legacy /sse-handler was een gewone async def-functie die zelf al rechtstreeks over de ASGI send() schreef maar niets terugstuurde — Starlette's Route wrapt zulke functies via request_response(), die een Response-retourwaarde verwacht en crasht (TypeError: 'NoneType' object is not callable) zodra de verbinding normaal afsluit. Bleef verborgen in eerdere tests omdat die de verbinding altijd hard afkapten (curl -m 2) i.p.v. netjes lieten sluiten. Gefixt door beide handlers class-based te maken (Starlette behandelt class-instances als rauwe ASGI-apps, geen wrapping).

Login / refresh-token ophalen (lokaal, per land)

Aanbevolen: handmatige OAuth-code-capture (scripts/manual_login.py)

De ingebouwde lidl-plus ... auth-CLI stuurt Selenium een gescripte browser-sessie door Lidl's inlogscherm. Lidl heeft dat scherm intussen vernieuwd (de library-selectors zijn verouderd, zie PR #25) en er zijn meldingen dat Lidl automatisch/Selenium-gestuurde logins actief detecteert en blokkeert. Gebruik daarom liever dit script: het genereert dezelfde OAuth-login-URL, jij logt daarmee gewoon in je eigen echte browser in (geen automatisering die Lidl kan herkennen), en plakt de code uit de laatste (mislukkende) redirect terug in de terminal.

pip install -e .
python scripts/manual_login.py -c NL -l nl
  1. Open de getoonde URL in je eigen browser en log in zoals gebruikelijk (inclusief 2FA).
  2. Aan het eind probeert de browser te navigeren naar com.lidlplus.app://callback?code=... — dat mislukt altijd in een gewone browser (geen app geregistreerd voor dat schema). Dat is verwacht: de foutmelding of adresbalk toont dan de volledige URL met de code erin.
  3. Plak die (volledige URL of alleen de code) terug in de terminal → het script wisselt 'm in en print het refresh_token.

Herhaal voor Duitsland met -c DE -l de.

Alternatief: de lidl-plus CLI (Selenium, kan momenteel vastlopen)

Vereist: Python 3.11+ en Chrome. Werkt alleen zolang Lidl's inlogscherm overeenkomt met wat de library verwacht — momenteel dus niet gegarandeerd (zie hierboven).

pip install ".[auth]"
lidl-plus -c NL -l nl -u <telefoonnummer of e-mail> --2fa phone auth
# volg de 2FA-prompt in de terminal → refresh_token verschijnt in de output

Let op: installeer niet los pip install "lidl-plus[auth]" — dat trekt de nieuwste blinker en pyOpenSSL binnen, en selenium-wire (onderhoudt zelf al jaren niets meer) is met geen van beide compatibel:

  • nieuwe blinker mist het interne, inmiddels verwijderde blinker._saferef-attribuut → CLI toont de misleidende Can't connect to web browser. Please install Chrome, Chromium or Firefox, terwijl Chrome prima werkt (verbergt een ModuleNotFoundError: No module named 'blinker._saferef')
  • nieuwe pyOpenSSL mist X509.get_extension(), waar de meegeleverde mitmproxy-fork op leunt voor TLS-cert-interceptie tijdens de login → crasht pas ná het wachtwoord-prompt met AttributeError: 'X509' object has no attribute 'get_extension'

De [auth]-extra in pyproject.toml pint daarom blinker==1.4 en pyOpenSSL==22.1.0. Gebruik dus altijd pip install ".[auth]" (uit de project-root), niet het losse package.

(-p <wachtwoord> kan als vlag, maar laat 'm weg om interactief te worden gevraagd i.p.v. in je shell-historie te belanden. --2fa email als je liever een code per e-mail krijgt.)

Herhaal voor Duitsland met -c DE -l de als je dat ook wilt gebruiken.

Losse bonnen/coupons opvragen kan ook direct via de CLI, handig om te verifiëren dat een token werkt zonder de MCP-server erbij te halen:

lidl-plus -c NL -l nl -r <refresh_token> receipt --all
lidl-plus -c NL -l nl -r <refresh_token> coupon

Bewaar het resulterende refresh-token niet in dit repo — zet het direct in de Portainer-stack env vars (zie hieronder), of lokaal in een .env (zie .env.example, staat in .gitignore).

Als een token later verlopen blijkt (lidl_check_status of een andere tool meldt een auth-fout): herhaal deze stap en werk de env var bij.

Bekende API-quirks (lidl-plus library, gemeten 2026-08-14)

Twee losse problemen die je kunt tegenkomen zodra je met een geldig refresh-token daadwerkelijk data probeert op te halen — geen van beide is een bug in dit project, maar in de onderliggende lidl-plus-library / Lidl's eigen API:

  1. WAF-tarpit op de "app-headers". De library stuurt standaard drie headers mee bij elke geauthenticeerde aanvraag om zich voor te doen als de officiële app: App-Version: 999.99.9, Operating-System: iOs, App: com.lidl.eci.lidl.plus. Lidl's WAF lijkt precies deze (overduidelijk statische/nep) fingerprint te herkennen en te tarpitten: de TLS-handshake slaagt gewoon, maar de server stuurt daarna 0 bytes terug tot de timeout — geen foutcode, gewoon stilte. Verwijder je die drie headers en stuur je alleen Authorization (+ Accept-Language) mee, dan antwoordt dezelfde endpoint binnen ~0,3s gewoon met 200 en echte data. lidl_client.py patcht dit automatisch weg via _strip_waf_fingerprint_headers() — dus met de tools uit dit project heb je hier verder geen last van, maar als je de kale lidl-plus-CLI of -library ergens anders rechtstreeks gebruikt, loop je hier alsnog tegenaan.
  2. Coupon-lijst-endpoint (v2) is dood. LidlPlusApi.coupons() (GET /api/v2/{country}) geeft een kale 404 — waarschijnlijk een endpoint dat Lidl heeft opgeruimd. Het oudere coupon_promotions_v1() (GET /app/api/v1/promotionslist) werkt wél en levert dezelfde soort data. Dit project gebruikt daarom overal coupon_promotions_v1() / activate_coupon_promotion_v1() in plaats van coupons() / activate_coupon(). Er is geen v1-tegenhanger voor deactiveren — die tool is daarom bewust niet geregistreerd (zie _handle_set_coupon in server.py).
  3. Coupon-activatie werkt alleen voor "Standard"-type coupons, niet voor "AssignablePromotion"-type coupons (bijv. persoonlijk toegewezen aanbiedingen). Getest (2026-08-14): een "Standard"-coupon activeren via activate_coupon_promotion_v1() geeft gewoon HTTP 200 en isActivated klopt daarna. Dezelfde aanroep op een "AssignablePromotion"-coupon geeft een echte 404 van Lidl's eigen API (dus geen WAF-blokkade meer, na de header- en Content-Length-fixes) — er is geen bekend werkend activatie-endpoint voor dit coupon-type in deze library. lidl_activate_coupon controleert nu wel expliciet de HTTP-status en geeft een duidelijke foutmelding i.p.v. ten onrechte "geactiveerd" te melden (was zelf een bug, ondertussen gefixt).
  4. coupon_promotions_v1() dedupliceert niet per winkel-instantie. Generieke acties (bijv. "Wiedereröffnung", "Aktionsrabatt") komen meerdere keren voor in de lijst — één keer per winkel — met identieke titel/korting/geldigheidsdatum maar een andere promotionId. De Lidl-app/website groepeert die kennelijk tot 1 zichtbare coupon; deze library niet. Getest (2026-08-14): aantal unieke combinaties van (titel, korting, startdatum) kwam voor Steno's account exact overeen met het aantal dat de website toonde (17 AllStores + 11 OnlineShop = 28 = 15 winkel + 13 online). De app zelf toont daarnaast nog extra content (bijv. een "jetzt spielen"-swipe-spel, combi-coupons met meerdere producten) die niet via deze API-endpoints beschikbaar blijkt — een bekende dekkingslacune, geen bug.
  5. Het availability.text-veld bevat vrijwel altijd generieke boilerplate-tekst ("Mit diesem Coupon haben wir leider Schwierigkeiten...") — ook bij coupons die nooit aangeraakt zijn en gewoon activeerbaar/bruikbaar zijn. Dit veld is geen betrouwbare indicator of een coupon werkt; gebruik de HTTP-status van de activatie-aanroep zelf (zie punt 3) en isActivated in een verse her-ophaal, niet dit tekstveld.
  6. AssignablePromotion-coupons zijn wél te activeren — maar niet via de mobiele API. De website (www.lidl.de/prm/...) gebruikt een apart endpoint: POST https://www.lidl.de/prm/api/v1/{country}/promotions/{id}/activation?language={taal} (let op: het id-veld, niet promotionId). Bevestigd werkend (2026-08-14) — Steno activeerde hiermee "Grafschafter Goldkrüstchen" en isActivated klopte daarna ook via de mobiele API. Dit endpoint gebruikt echter cookie/CSRF-gebaseerde websessie-auth (in plaats van de OAuth refresh-token-flow die de rest van dit project gebruikt) — een sessie die maar ~1 uur geldig blijft, geen refresh-token-achtige langlevende variant. Nog niet geïmplementeerd in dit project: zou een tweede, apart login-mechanisme vereisen (web-sessie + CSRF-token onderhouden) voor een relatief smalle usecase. Zie git-geschiedenis/chatlog voor de volledige curl indien dit later alsnog gebouwd wordt.
  7. Itemdetails van bonnen (lidl_get_receipt_detail) zijn kapot voor recente bonnen via de mobiele API. LidlPlusApi.ticket() (GET tickets.lidlplus.com/api/v2/{country}/tickets/{id}) geeft een lege HTTP 400 terug voor bonnen met isHtml/hasHtmlDocument: true in de lijst (kennelijk alle bonnen sinds medio 2026 — Lidl slaat ze nu op als kant-en-klaar HTML-document i.p.v. gestructureerde JSON-itemdata). Wél een werkend alternatief gevonden (2026-08-15), zie hieronder.

Itemdetails van HTML-format bonnen (web-sessie-fallback)

lidl_get_receipt_detail probeert eerst de mobiele API (ticket()), en valt bij een lege HTTP 400 (zie quirk 7 hierboven) automatisch terug op een tweede, apart auth-mechanisme: een cookie-gebaseerde www.lidl.de-websessie, los van de OAuth refresh-token-flow die de rest van deze server gebruikt.

Endpoint: GET https://www.lidl.de/mre/api/v1/tickets/{id}?country={land}&languageCode={taal}-{land} — retourneert JSON met een ticket.htmlPrintedReceipt-veld: de volledige bon als kant-en-klaar HTML-document (<pre>-blok met de regeleinden al als letterlijke tekst erin). lidl_client.fetch_receipt_text() strip de HTML-tags (stdlib html.parser, geen extra dependency nodig) en geeft de leesbare platte bontekst terug — geen gestructureerde itemlijst (geen los JSON-array met artikel/aantal/prijs), maar wel de complete, leesbare inhoud (artikelen, kortingen, BTW-overzicht, betaalmethode — identiek aan wat de Lidl Plus-website zelf toont).

Levensduur: de authToken-JWT zelf is ~1 uur geldig, maar er is een silent-refresh- endpoint gevonden (GET www.lidl.de/mla/api/v1/token, alleen cookies nodig — ontdekt 2026-08-15 doordat de website 'm zelf periodiek aanroept zonder gebruikersactie). fetch_receipt_text() roept dit voor elke aanvraag eerst aan en vervangt de authToken-waarde in de cookie door het verse token uit de respons, vóór de eigenlijke bon-aanvraag. Bleek in de praktijk (14 augustus) niet voldoende om de sessie na het originele ~1-uur-punt levend te houden — de sessie was alsnog verlopen, dus dit is hooguit een kleine verlenging, geen structurele oplossing.

Persistente web-sessie (lidl-web-refresher)

Structurele oplossing (16 augustus): een aparte sidecar-container die een al ingelogde sessie periodiek hergebruikt, in plaats van steeds een nieuwe cookie handmatig te vangen. Bewust geen geautomatiseerde login-flow (zelfde afweging als bij AssignablePromotion-coupon-activatie) — alleen gewone paginanavigatie met een bestaande sessie, geen herhaalde login-pogingen die Lidl's bot-detectie kunnen raken.

Opzet (eenmalig):

  1. Lokaal (niet op de server): pip install playwright && playwright install chromium, dan python scripts/save_web_session.py -c NL (en/of -c DE). Er opent een echte browser, jij logt zelf in zoals altijd (2FA etc.), sluit af met Enter. Resultaat: web_session/nl_state.json / de_state.json — bevat live sessie-cookies, nooit committen (staat al in .gitignore).

  2. Kopieer die state-file(s) naar het web-session-data-volume op de server:

    docker cp web_session/nl_state.json lidl-web-refresher:/data/web-session/nl_state.json
    docker cp web_session/de_state.json lidl-web-refresher:/data/web-session/de_state.json
    

    (Of via Portainer UI → Volumes → web-session-data → bestand uploaden, als docker cp niet praktisch is vanaf je eigen machine.)

  3. lidl-web-refresher (zie docker-compose.yml) draait vanaf dan elke REFRESH_INTERVAL_MINUTES (default 20) een headless navigatie naar de aankoopgeschiedenis-pagina, vangt daarbij het verzoek op dat de pagina zelf naar de tickets-lijst-API maakt om een echt ticket-ID te pakken, en doet vervolgens een echte in-browser fetch (credentials: "include") naar de bon-DETAIL-API voor dat ticket — pas dán worden de cookies geëxtraheerd en geschreven naar {land}.cookie in hetzelfde volume, door lidl-mcp (src/lidl_client.py, _read_web_cookie()) als eerste geprobeerd, vóór de env var-fallback.

    Waarom die detail-fetch-stap nodig is (ontdekt 15/16 augustus): aanvankelijk navigeerde de refresher alléén naar de lijstpagina. Dat gaf cookies die er geldig uitzagen (geen login-redirect, JWT nog binnen zijn 1-uur-venster) maar bij de daadwerkelijke bon-detail-aanvraag alsnog consistent een 401 opleverden — ook zonder de silent-refresh-tussenstap in lidl_client.py, dus de cookie zelf was het probleem, niet die tussenstap. Blijkbaar valideert/ververst Lidl iets sessie-gerelateerds specifiek bij een bon-detail-aanvraag dat de lijstpagina niet triggert. De detail-fetch-stap repliceert daarom exact het soort verzoek dat bij de oorspronkelijke handmatige her-vangst (DevTools op een geopende bon-detail) al wél werkte. Bevestigd werkend voor DE (status 200, gevolgd door een geslaagde echte bon-opvraging).

    Bekende beperking: als een land al lange tijd geen actieve aankopen/bonnen heeft (getest met een NL-account waarvan de laatste bon van maanden terug was), kan de refresher geen tickets-lijst-response vangen binnen de timeout (20s) — de detail-fetch- stap wordt dan overgeslagen en de cyclus valt terug op het oude gedrag (cookie ververst, maar niet expliciet gevalideerd). Geen crash, gewoon een duidelijke waarschuwing in de logs; zodra dat land weer een recente bon heeft zou dit vanzelf weer moeten oppakken.

Robuustheid: een corrupte/onvolledige {land}_state.json (bijv. door een verminkte copy-paste, zie hieronder) crasht de container niet meer — de cyclus wordt overgeslagen met een duidelijke logregel, de container blijft draaien en probeert het gewoon de volgende cyclus opnieuw. Vóór deze fix (14/15 augustus) leidde dit tot een crash-loop.

Bestanden correct naar de server krijgen: copy-paste van een lange JSON-string via de Portainer-webconsole knipt af bij een terminal-buffergrens (bevestigd: exact 4096 bytes, ongeacht encoding) — dus niet doen. Werkende routes: docker cp (als je Docker-CLI met de host kan praten), of via de Samba-share-add-on (bestand naar de share-map, dan op de server met een tijdelijk hulpcontainertje — bind-mount van /mnt/data/supervisor/share plus het web-session-data-volume — ernaartoe kopiëren). SCP werkte in de praktijk niet betrouwbaar vanaf Windows (herhaaldelijke "Corrupted MAC on input"-fouten met OpenSSH-for-Windows 9.5p2, ook na cipher/MAC-overrides) — niet verder uitgezocht, Samba-route was sneller.

Als de sessie toch doodloopt (zichtbaar in docker logs lidl-web-refresher als "Sessie lijkt niet meer geldig"): stap 1-2 hierboven herhalen. Hoe vaak dit nodig is, is nog niet in de praktijk bevestigd — in theorie zou een normale, actief hergebruikte sessie veel langer moeten meegaan dan de oude ~1-uur-cyclus, maar Lidl kan sessies ook om andere redenen ongeldig maken.

Oude methode (handmatig, als noodgreep of om zonder de sidecar te testen): environment variable LIDL_{NL|DE}_WEB_COOKIE rechtstreeks zetten in Portainer:

  1. Log in op www.lidl.de (of de NL-variant), open een willekeurige bon in je aankoopgeschiedenis.
  2. DevTools (F12) → Network-tab → filter op Fetch/XHR → ververs/open de bon-pagina.
  3. Zoek het verzoek naar mre/api/v1/tickets/... → rechtsklik → Copy as cURL.
  4. Haal uit die cURL de volledige waarde van de -b/Cookie-header (alles tussen de aanhalingstekens na -b), en zet die in LIDL_{NL|DE}_WEB_COOKIE.

Lokaal draaien

cp .env.example .env
# vul LIDL_NL_REFRESH_TOKEN (en/of LIDL_DE_REFRESH_TOKEN) in .env in
docker compose up --build
curl http://localhost:8000/health

Deployment — Portainer-stack op de HA-host (steno-infra-patterns-skill)

Geverifieerd tegen de bestaande stacks (vault-broker-mcp, npm-mcp) op dezelfde Portainer-omgeving (endpoint "primary", id 1):

  1. Git-repository-methode. Maak een leeg GitHub-repo (bijv. StenoJS/lidl-mcp, zelfde account als vault-broker-mcp/nginx-proxy-manager-mcp), en push dit repo ernaartoe:
    git remote add origin https://github.com/StenoJS/lidl-mcp.git
    git branch -M main
    git push -u origin main
    
    Deploy daarna de Portainer-stack met StackCreateDockerStandaloneRepository (RepositoryURL = bovenstaande, ComposeFile = docker-compose.yml, endpointId = 1) — of handmatig via Portainer UI → Stacks → Add stack → Repository.
  2. Netwerk: hassio (extern, gedeeld met de andere MCP-stacks) — al in docker-compose.yml opgenomen.
  3. Poort: 8934 (host) → 8000 (container) — vrij op dit moment; check bij twijfel welke poorten al in gebruik zijn (ah-mcp: 3000/9876, supermarkt-mcp: 8000, vault-broker-mcp: 8933, playwright-mcp's: 8931/8932).
  4. Secrets als stack-environment-variables (Portainer UI, niet in de compose-YAML of dit repo): LIDL_NL_REFRESH_TOKEN, LIDL_DE_REFRESH_TOKEN, MCP_SSE_SECRET (genereer met python -c "import secrets; print(secrets.token_hex(16))"). LIDL_NL_WEB_COOKIE/LIDL_DE_WEB_COOKIE zijn nu optioneel (alleen nog fallback) — zie "Persistente web-sessie (lidl-web-refresher)" hierboven voor de aanbevolen route.
  5. lidl-web-refresher-sidecar: staat al in docker-compose.yml, wordt automatisch meegedeployed. Vereist wél de eenmalige handmatige state-upload (zie boven) voordat lidl_get_receipt_detail voor HTML-format bonnen werkt — zonder die state doet de sidecar niets schadelijks, hij logt alleen "nog geen state aanwezig" en slaat over.
  6. Nginx Proxy Manager (HA-add-on, UI op poort 81 van de HA-host): nieuwe Proxy Host, bijv. lidlmcp.steno.nl → forward naar lidl-mcp:8000 (containernaam, via het gedeelde hassio-netwerk) of naar <host-ip>:8934 (published poort) — beide werken, eerste is consistenter met hoe vault-broker-mcp het doet. SSL via Let's Encrypt (automatisch). Geen speciale NPM-config nodig voor het secret — dat wordt nu applicatie-side gecheckt (MCP_SSE_SECRET, zie server_sse.py), NPM hoeft alleen het volledige pad+querystring gewoon door te geven, wat het standaard al doet. Client-URL's worden dan (zie Transports hierboven voor waarom er twee zijn):
    • HA MCP Client: https://lidlmcp.steno.nl/sse?key=<MCP_SSE_SECRET>
    • Claude.ai Custom Connector: https://lidlmcp.steno.nl/mcp/<MCP_SSE_SECRET>
  7. HA MCP Client-integratie: nieuwe instance toevoegen naast de bestaande twee (ah-mcp, nl-supermarkt-mcp), wijzend naar de /sse-URL uit stap 5. Interactieve config-flow (Instellingen → Apparaten & diensten → Integratie toevoegen → MCP Client).
  8. Conversation-agent-prompt: uitgebreid met een derde tool-beschrijving — gebruik lidl-mcp specifiek voor vragen over eigen Lidl-aankopen/bonnen/coupons, niet voor prijsvergelijking (dat blijft nl-supermarkt-mcp). Zit in de conversation-subentry van de Google Generative AI-integratie in HA (niet in dit repo — HA-config).
  9. Claude.ai Custom Connector (optioneel, los van de HA-integratie): Instellingen → Connectors → Add custom connector → de /mcp/<secret>-URL uit stap 5.

Alle 8 stappen zijn doorlopen voor de live deployment (2026-08-14) — dit is dus ook een logboek van wat er staat, niet alleen een handleiding voor de eerste keer.

Beperkingen / risico's

  • lidl-plus is niet-officieel, reverse-engineered van de Lidl Plus-app-API en kan zonder waarschuwing stoppen met werken.
  • Response-schema's van tickets() / ticket() / coupons() zijn niet door Lidl gedocumenteerd; deze server geeft de JSON grotendeels ruw door in plaats van op veldnamen te gokken die kunnen wijzigen.
  • Coupon-activatie is een echte schrijf-actie op het Lidl-account — er is geen "dry run".

from github.com/StenoJS/lidl-mcp

Установка Lidl

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/StenoJS/lidl-mcp

FAQ

Lidl MCP бесплатный?

Да, Lidl MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Lidl?

Нет, Lidl работает без API-ключей и переменных окружения.

Lidl — hosted или self-hosted?

Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.

Как установить Lidl в Claude Desktop, Claude Code или Cursor?

Открой Lidl на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Lidl with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development