Skip to main content
Glama
Lumenco Catalog MCP Server

Lumenco Catalog MCP Server

Lumenco Catalog (Phase 1 scraper + Phase 2 MCP)

Dieses Repository hat zwei Ebenen:

  1. Phase 1 extrahiert Daten von https://en.staging.lumenco.ca/ in PostgreSQL.

  2. Phase 2 stellt diesen Katalog über einen schreibgeschützten Model Context Protocol Server bereit, sodass Claude Produkte, Spezifikationen, Listeneinträge und Empfehlungs-Kandidaten abrufen kann, ohne auf Lumenco zu browsen.

CLAUDE
  │ MCP / HTTPS
  ▼
Lumenco Product Database (Streamable HTTP)
  │ tools → services → repositories
  ▼
PostgreSQL  (Phase 1 catalog)

Phase 1 extrahiert. Phase 2 stellt bereit. Claude denkt.

Der MCP-Server extrahiert niemals Lumenco, lädt niemals Spezifikations-PDFs herunter, ruft niemals ein LLM auf und schreibt niemals in die Datenbank.

Wie die Website aussieht

Lumenco-Staging ist ein Magento-2-Shopfrontend.

Bereich

Verhalten

Marken

https://en.staging.lumenco.ca/brand listet alle Marken auf (Amasty Brands). Markenkarten auf dieser Seite zeigen oft auf staging.lumenco.ca; der Scraper schreibt sie auf den englischen Host um.

Markenlistings

https://en.staging.lumenco.ca/brand/{slug} mit Magento-Paginierung ?p=2 (24 Produkte pro Seite). Die Gesamtseitenzahl befindet sich in #am-page-count.

Produkte

Kanonische URLs wie /aaled-aa-900018-1x4-bl.html. Serverseitig gerendertes HTML enthält JSON-LD, SKU, Preis, Lagerbestand, Spezifikationstabelle und einen Spezifikationsblatt-Link.

Spezifikationsblätter

Normalerweise Same-Origin-PDFs unter /dev/*.pdf.

Sitemap

/sitemap.xml gibt derzeit HTTP 500 zurück. Der Crawler versucht weiterhin bekannte Sitemap-Pfade und fällt dann auf Marken- und Kategorie-Ermittlung zurück.

GraphQL

/graphql existiert, aber das Staging-Schema ist defekt (Config element "String" is not declared). HTML-Crawling ist die zuverlässige Quelle.

Abrufen

Produktseiten werden serverseitig gerendert. Scraplings HTTP-FetcherSession ist die Standardeinstellung. DynamicSession ist als Lazy-Fallback registriert, falls einer Produktseite erforderliche Felder fehlen.

Der Crawler bleibt auf /de.staging.lumenco.ca. Externe Spezifikations-PDFs können als Produktdokumente heruntergeladen werden. Anzeigen, Analysen, ID, Checkout und Social-Media-URLs werden ignoriert.

robots.txt ist für öffentliche Suchmaschinen ( User-Agent: * verbietet sehen Sie die meisten Pfade außer /brand und einigen CMS-Seiten). Dieser Scraper ist ein genehmigter Ingest für das Staging; daher ist ROBOTS_TXT_OBEY standardmäßig false. Setzen Sie auf true, wenn Scrapling diese Datei berücksichtigen soll.

Related MCP server: Catalog Services MCP Server

Projektstruktur

scraper/                    Phase 1 Scrapling crawler
  config.py
  spider.py
  discovery.py
  fetcher.py
  cli.py
  selectors/
  parsers/
  pipelines/
  database/                 shared SQLAlchemy models + repositories
  utils/
app/                        Phase 2 read-only MCP server
  server.py                 Streamable HTTP + /health
  config.py
  auth/middleware.py        bearer token (replaceable with OAuth)
  tools/                    MCP tool layer
  services/                 catalog / product / search / recommendations
  repositories/             read-only queries over Phase 1 tables
  schemas/
  database/session.py       pooled, read-only sessions
alembic/                    PostgreSQL migrations
tests/
scripts/create_readonly_user.sql

1. Abhängigkeiten installieren

Python 3.10+ ist erforderlich.

python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt

Die HTTP-/Browser-Erweiterungen von Scrapling sind in scrapling[fetchers] enthalten. Falls Sie einen Browser-Fallback (DynamicFetcher) benötigen, installieren Sie die Browser-Binaries:

scrapling install

Optionale OCR für gescannte / bildbasierte PDFs:

pip install pytesseract Pillow
# plus a Tesseract OCR engine on the host

OCR ist standardmäßig deaktiviert (ENABLE_OCR=false). Bildbasierte PDFs werden gespeichert und als ocr_required markiert, nicht als leerer Text gespeichert.

2. PostgreSQL konfigurieren

Der schnellste lokale Weg:

docker compose up -d postgres

Das startet PostgreSQL 16 mit:

  • Benutzer: lumen

  • Passwort: lumen

  • Datenbank: lumen

  • Host-Port: 5433 (Container-Port bleibt 5432; 5433 vermeidet eine Windows- PostgreSQL-Installation, die bereits 5432 nutzt).

Kopieren Sie die Umgebungskonfiguration:

copy .env.example .env   # Windows
cp .env.example .env     # macOS / Linux

Standardverbindungszeichenfolge:

DATABASE_URL=postgresql+psycopg2://lumenco:lumenco@127.0.0.1:5433/lumenco
LUMENCO_BASE_URL=https://en.staging.lumenco.ca/

Erstellen Sie die Tabellen (beide Wege funktionieren):

python -m scraper init-db
python -m alembic upgrade head

3. Test-Crawl mit 5 Produktionen

python -m scraper crawl --limit 5

Das findet Produkte auf der Live-Seite, verarbeitet nur die ersten 5, lädt die zugehörigen Speicblätter herunter, speichert in PostgreSQL und druckt einen Crawl-Bericht.

Sie können auch eine Marke festlegen:

python -m scraper crawl --limit 5 --url https://en.staging.lumenco.ca/brand/aaled

Oder ein einzelnes Produkt:

python -m scraper crawl --url https://en.staging.lumenco.ca/aaled-aa-900018-1x4-bl.html

4. Den vollständigen Crawl ausführen

python -m scraper crawl

Das durchläuft alle Marken (und Kategorien), folgt jeder Seitenzahl-Seite und extrahiert alle erreichbaren Produkte. Verwechseln Sie --limit nicht mit einer Obergrenze für den Produktionskatalog – --limit ist nur für die Entwicklung.

Beschränkung ist integriert: concurrency, limit, Befehl, ... Drosselung, Wiederholungen mittels exponentiellem Backoff und optional AutoThrottle. Passen Sie sie in .env an:

MAX_CONCURRENCY=5
CONCURRENT_REQUESTS_PER_DOMAIN=3
DOWNLOAD_DELAY=0.5
RETRY_COUNT=3
AUTOTHROTTLE_ENABLED=true

5. Einen Crawl fortsetzen

Scrapling Checkpointing wird über CRAWL_DIR aktiviert (Standard ./data/crawl). Drücken Sie einmal Ctrl+C, um sauber zu pausieren. Verwenden Sie es erneut mit:

python -m scraper crawl --resume

Fortsetzungsverhalten:

  • Scrapling stellt ausstehende Anfragen aus CRAWL_DIR wiederher.

  • Produkte bereits mit einer Steur; scrape_status=success werden übersprungen, sofern nicht --force gesetzt ist.

  • Fehlgeschlagene Produkte werden erinnert.

  • Spezifikations-PDFs werden nicht erneut extrahiert, wenn der Dokument-Hash unverändert ist.

6. Datenbank inspizieren

python -m scraper stats
python -m scraper validate
python -m scraper product --sku aa-900018-1x4-bl

Oder mit psql:

psql postgresql://lumenco:lumenco@127.0.0.1:5433/lumenco

Nützliche Datenbankabfragen:

SELECT count(*) FROM products;
SELECT sku, product_name, price, brand FROM products ORDER BY last_scraped_at DESC LIMIT 20;

SELECT p.sku, d.filename, d.extraction_status, left(d.extracted_text, 200)
FROM specification_documents d
JOIN products p ON p.id = d.product_id
WHERE d.extraction_status = 'extracted'
LIMIT 10;

7. Wie Spezifikationsblätter verarbeitet werden

Für jede Produktseite sucht der Parser:

  • a.document-item-link (Lumenco’s „Specification Sheet“-Steuerelement)

  • Äquivalente Bezeichnungen: Specification Formula, Defekt Sheet, Specifications, Technical Data, PDF, Fiche technique usw.

Dann funktioniert die Pipeline:

  1. Speichern Sie die Dokument-URL.

  2. Download der Datei mit httpx (nicht mit einem Browser).

  3. Validierung der PDF-Magic-Bytes (%PDF).

  4. Speichern eine definierte Kopie: data/specifications/{per_sku}_{hash16}.pdf.

  5. Extrakt mit dem PyMuPDF.

  6. Einrücken von Leerzeichen bei Seiten-/Abschnittswechseln. (nicht genau)

  7. Bereinigen von Leerzeichen, Seiten-/Abschnitten bleiben erhalten.

  8. Auszug (gefüllt) für Text, SHA-256-Hash, Methode und Status.

  9. Parse Label: Value und ohne Feld zu erfinden.

  10. Spezifikationen aus PDF und Produktseite zusammenführen, Quelle spiegelt sich:

{
  "Voltage": {
    "value": "120-277V",
    "source": "product_page",
    "raw": "120-277V",
    "normalized": {"min": 120, "max": 277, "unit": "V"}
  }
}

Wenn ein PDF nur sehr wenig Text enthält, ist der Status ocr_required (oder OCR wird bei ENABLE_OCR=true versucht). Leere erfolgreiche Extraktionen werden nicht gespeichert.

Unveränderte PDFs werden bei späteren Crawls über den Inhalt-Hash übersprungen.

8. Fehlerbehebung bei fehlerhaften Produkten

Symptom

Mögliche Maßnahme

python -m scraper validate berichtet Fehler

Lesen Sie die JSON-issues-Liste (missing_name, invalid_url, empty_extracted_text, …).

Produkt HTTP 5xx / Timeout

Führen Sie python -m scrape crawl --resume. Fehler in crawl_errors gespeichert.

Fehlendes Spezifikationsblatt

Bei einigen SKUs erwartet: Status ist not_found; die Produktzeile wird weiterhin gespeichert.

PDF hat ocr_required

Aktivieren Sie OCR oder prüfen Sie die Datei unter data/specifications/.

PDF mit invalid_pdf

Verlinktes Datei ist nicht PDF (z. B. HTML – Siehe specification_documents.error_message).

Duplikate

Sollte nicht vorkommen: eindeutige product_url / canonical_url / sku plus upsert. validate starten.

Markenseiten scheinen leer

Sicherstellen, dass Sie auf en.staging.lumeno.ca sind, nicht auf dem Französisch Host. Der Spider ändert dies automatisch.

DynamicFetcher-Fehler

Führen Sie scrapling install aus. Der HTTP-Fetch ist für das aktuelle Staging-HTML ausreichend.

Datenverbindungsfehler

Prüfen Sie DATABASE_URL, docker compose ps und python -m scraper init-db.

Strukturprotokolle sehen wie folgt aus:

[INFO] PRODUCT_FETCH url=https://en.staging.lumenco.ca/aaled-aa-900018-1x4-bl.html sku=aa-900018-1x4-bl status=success
[INFO] SPEC_SHEET sku=aa-900018-1x4-bl status=extracted duration=0.84s
[ERROR] SPEC_SHEET sku=... status=failed error=...

Tests

pytest

Die Coverage beinhaltet generierte URL, Produkt-/SKU-, Parsee-, Spezifikations-Erkennung, PDF-Extraktion, Datenbank-Upsert / Duplikate, Mitgliedschafts-Reihenfolgen, Scoring und MCP-Tool-Integ.

CLI-Refling

python -m scraper crawl --limit 100
python -m scraper crawl --mode development --limit 100 --url https://en.staging.lumenco.ca/brand/aaled
python -m scraper crawl --resume
python -m scraper reprocess-specs
python -m scraper embeddings --limit 100
python -m scraper embedding-stats
python -m scraper recommend --sku ABC123 --type related --limit 5
python -m scraper recommendation-eval
python -m scraper validate
python -m scraper stats
python -m scraper sample
python -m scraper product --sku ABC123
python -m scraper init-db
python -m app.server

Standardmodus ist Entscheid: Durchschnittlich 100 erfolgreich abgerufene Produkte, nur Marken (keine Kategorie-Lauf). Ein voller Katalog-Crawl wird verweigert, Unless --mode full --limit N oder --mode full --confirm- übergeben wird.

Phase 2.5 – Datenqualität für 100 Produkt

Dieses Projekt zielt auf einen kontrollierten Lumenco-Datensatz von ~100 Produkten ab. Der Live-Katalog hat über 30.000 SKU; vollständiges Katalog-Crawlen ist bewusst ausgeschlossen.

Pipeline

Scrapling → ExtractionProduct → PDF-Download → PDF-Guid → Normierung → PostgreSQL → Read-only MCP-Modell.

PDF-Größe wäre zuerst. OCR (Tesseract via pytesseract) greift nur bei kein Text. Set ENABLE_OCR=true und install T. Plus pip install pytesseract Pillow.

Normalisierte Spezifikationen behalten Quelle und Ausgleich. Rohermerkungen sind in spec_ AllDocs.extract1. MCP get_product liefert kompakte Strukturen; get_product_specifications kann rohen Text mit Rücksicht.

Französische Magento––– Kategorie URLs (z.B. /eclairageintérieur – electricité) werden in der Header des English-Host mitgefürt, funktionieren dort aber mit 404. Der Crawler fügt diese Pfade nicht ein. Neue Crawls verwenden zudem isolierte Checkpoint (data/crawl/run-<id>), um eine Pause-Datei zu verhindern. Verwenden Sie --resume nur für den gemeinsamen Checkpoint in data/crawl.

Französische, auf Englisch umgeschriebene Kategorie URLs werden als expected 4 gekappt und gelten nicht als Fehler.

Nach einem Crawl:

python -m scraper stats
python -m scraper validate
python -m scraper sample
python -m scraper product --sku L0110TUT8002020

Phase 3A – Vektorsuche + Produkt-Embeddings

Phase 3A fügt semantikt PostgreSQL + pgvector hinzu. Keine Implementierung von Related/Upsell/Cross-sell, das ist Phase 3B.

Architecture

~100 product dataset
        ↓
Canonical product text (cleaned, no HTML)
        ↓
EmbeddingService (OpenAI-compatible API)
        ↓
product_embeddings (pgvector)
        ↓
VectorSearchService
        ↓
MCP tool: search_similar_products

Setup

  1. Verwenden Sie ein PostgreSQL mit pgvector (Docker–compose – pgvector/pgvector:pg16).

  2. Vektor-Umgebungsvariablen in .env (Kopiere aus .env--example).

  3. Migrieren:

python -m alembic upgrade head
  1. Erzeugen Sie die Embeddings für den DevCatalog:

python -m scraper embeddings --limit 100
python -m scraper embedding-stats

Unveränderte Produkte jeweils über content_hash überflogen. Alleinkraft mit — Wiederherstellen.

Index strategy

HNSW über Kosinus-Distanz (vector ops``, m=16, ef_construction=64) – ideal für das ~100‑Produkte. IVFFlat nur zwei schlicht.

MCP

Neue Methode search_similar_products – Liest ausschließlich Vektoren, ruft keine Embed API auf, keine Weiten Scrabe. Existing Recommendation tools unchanged.

Phase 3B – Hybridrous Empfehlungsengine

Recommendations combine pgvector similarity and structured rules. Vector similarity alone is insufficient: an 18W e.g. Example two lighting, m.d. (model) and 30W T8 are related but semantically different, but they map differently to Related, Upsell, Cross-sell.

Product → vector candidates + structured neighbors
                ↓
        hard exclusions
                ↓
   Related / Upsell / Cross-sell scorers
                ↓
     scores + confidence + reasons → MCP

Category

Meaning

Related

Similar usage / category / specs

Upsell

Same family and measurable improvement (not just price)

Cross-sell

Complementary (driver, casing, bracket, fitting/replacement)

No LLM is used in ranking. As MCP tools find_related_products, find_upsell_products and find_cross_sell_products check RecommendationService (read.

CLI

python -m scraper recommend --sku L0110TUT8002020 --type related --limit 5
python -m scraper recommend --sku L0110TUT8002020 --type upsell --limit 5 --debug
python -m scraper recommend --sku L0110TUT8002020 --type cross-sell --limit 5
python -m scraper recommendation-eval --sample-size 10 --limit 3

Weights are configures via env RELATED_VECTOR_WEIGHT, UPSELL_TECHNICAL_WEIGHT, CROSS_SELL_COMPATIBILITY_WEIGHT (see .env.example).

Phase 3C – Claude + MCP Workflow

User → Claude → MCP (/mcp) → PostgreSQL + pgvector + RecommendationService → Claude → User

Responsibilisierung.

Ebene

Funktion

Scrapling

Crawlen / Speichern

PostgreSQL + pgvector

Maßgebliche Datenquelle + Vektoren

RecommendationService

Deterministisches Related-/Upsell-/Cross-sell-Ranking

MCP

Nur-Lese-Abruf (kein Scraping, keine Schreibzugriffe, kein LLM)

Claude

Konversation, Tool-Auswahl, Erläuterung

Claude-Skill

Projekt-Skill: .cursor/skills/lumenco-product-mcp/SKILL.md

End-to-End-Prompts

Siehe docs/claude-e2e-tests.md.

Claude / Inspector verbinden

  1. docker compose up -d postgres

  2. python -m app.server

  3. Richten Sie den Client auf http://localhost:8000/mcp aus (Streamable HTTP).

  4. Optional: MCP_AUTH_TOKEN + Authorization: Bearer …

Für die spätere Remote-Bereitstellung: nur den MCP-HTTPS-Endpunkt freigeben; PostgreSQL privat halten.

Entwicklungsdatensatz

Aktueller Katalog: ~100 Produkte. Der vollständige Lumenco-Katalog (30k+) ist bewusst ausgeklammert.

Phase 2 — Lumenco Product Database MCP

Schreibgeschützter Streamable-HTTP-MCP-Server namens Lumenco Product Database.

Architektur

Claude
  │ MCP / Streamable HTTP
  ▼
Lumenco MCP Server   (/mcp, /health)
  │
  ▼
MCP Tool Layer
  │
  ▼
Service Layer          catalog / product / search / similarity / recommendation
  │
  ▼
Repository Layer       SQLAlchemy, no raw SQL in tools
  │
  ▼
PostgreSQL + pgvector  products, specs, listings, product_embeddings

Lokale Einrichtung

  1. Schließen Sie die Phase-1-Einrichtung ab (PostgreSQL + .env + python -m alembic upgrade head).

  2. Führen Sie einen Crawl aus, damit der Katalog befüllt wird.

  3. Installieren Sie die MCP-Extras, falls sie nicht bereits in requirements.txt enthalten sind:

pip install -r requirements.txt
  1. Legen Sie die MCP-Variablen in .env fest:GXP32

Für die Produktion erzeugen Sie eine Rolle mit ausschließlich SELEC-Berechtigung:GXP33

Richten Sie DATABASE_URL anschließend auf lumenco_mcp aus.

Ausführung

python -m app.server

Oder:

uvicorn app.server:app --host 0.0.0.0 --port 8000

Docker:

docker compose up --build mcp

MCP-Endpunkt

http://localhost:8000/mcp

Health

GET http://localhost:8000/health

{
  "status": "ok",
  "service": "lumenco-product-mcp",
  "database": "connected"
}

MCP Inspector

npx -y @modelcontextprotocol/inspector

Verbinden Sie sich über den Transport Streamable HTTP mit http://localhost:8000/mcp. Wenn MCP_AUTH_TOKEN gesetzt ist, fügen Sie Folgendes hinzu:

Authorization: Bearer <token>

Bestätigen Sie, dass alle acht Tools aufgelistet und ausführbar sind.

Verfügbare Tools

Alle Tools lesen ausschließlich aus PostgreSQL. Keines ruft Lumenco-URLs ab.

get_catalog_status

Kataloggröße und Aktualität des letzten Crawls. Keine Eingabe.

get_listing_products

Produkte auf einer Marken-/Kategorie-Listing-URL in der ursprünglichen Listenposition.

Eingabe

Erforderlich

Hinweise

listing_url

ja

Normalisiert und als Datenbankschlüssel verwendet

limit

nein

Standard 20, maximal 100

offset

nein

Standard 0

get_product

Vollständiger Produktdatensatz per product_id und/oder sku.

get_product_specifications

Strukturierte Spezifikationen plus gespeicherter Text des Spezifikationsblatts. Es lädt keine PDFs herunter.

search_products

Lokale Katalogsuche (SKU, Name, Marke, Kategorie, Beschreibung, Spezifikationen).

Optionale Filter: brand, category, subcategory, sku, min_price, max_price.

search_similar_products

Semantische Nachbarn aus gespeicherten pgvector-Embeddings (Kosinus-Distanz). Es erzeugt keine Embeddings und ruft kein LLM auf.

Optionale Filter: brand, category, subcategory, min_price, max_price.

Hybride Related-Kandidaten (Vektor + Kategorie/Anwendung/Spezifikationen). Enthält match_score, confidence, score_breakdown und match_reasons. Optional debug=true.

find_upsell_products

Hybride Upsell-Kandidaten. Bedingung ist eine messbare Verbesserung (nicht nur der Preis). Gründe in upgrade_reasons.

find_cross_sell_products

Hybride Cross-sell-Kandidaten. Die Kompatibilität dominiert; Alternativen aus derselben Familie ausgeschlossen.

Die Empfehlungstools schließen das Quellprodukt aus und entfernen Duplikate. Claude soll einen Kandidatenpool anfordern und dann die endgültigen 3 Related / 4 Upsell / 7 Cross-sell selbst wählen.

Beispielbezogener Ablauf

Benutzer: Analysiere die ersten 10 Produkte von https://en.staging.lumenco.ca/brand/aaled und gib je 3 Related, 4 Upsell, 7 Cross-sell zurück.

  1. get_listing_products(listing_url=..., limit=10)

  2. get_product(product_id=...) für jedes Quellprodukt

  3. find_related_products / find_upsell_products / find_cross_sell_products mit limit=10

  4. Claude wählt die endgültige Auswahl aus den Kandidatenpools.

Produktionsbereitstellung

Nur den MCP-HTTPS-Endpunkt freigeben; PostgreSQL privat lassen.

Internet → HTTPS → MCP server → private PostgreSQL

Geeignete Hosts: Geeignete Hosts: Railway, Render, Google Cloud Run, AWS, Cloudflare.

Anforderungen:

  • HTTPS-Implementierung vor uvicorn / dem Docker-Image

  • MCP_AUTH_TOKEN gesetzt (Bearer-Middleware ist so isoliert, dass später OAuth sie ersetzen kann)

  • schreibgeschützte DATABASE_URL

  • Health-Check unter /health

Port 5432 nicht veröffentlichen.

Claude-Custom-Connector

Nachdem der Server über eine öffentliche HTTPS-URL erreichbar ist:

  1. Fügen Sie in Claude einen Custom-Connector hinzu.

  2. MCP-URL: https://your-host/mcp

  3. Der Servername sollte als Lumenco Product Database erscheinen.

  4. Konfigurieren Sie die Bearer-Authentifizierung mit MCP_AUTH_TOKEN oder OAuth, wenn Sie die Middleware ersetzen.

  5. Fragen Sie: „Wie viele Produkte sind derzeit in der Lumenco-Datenbank?“ Claude sollte get_catalog_status aufrufen.

Temporäres öffentliches HTTPS für lokale Tests: Cloudflare Tunnel, ngrok oder Ähnliches vor localhost:8000.

Sicherheit

  • Keine execute_sql--, fetch_url--, run_command--- oder Crawl-Tools

  • Nur SQLAlchemy parametrisierte Abfragen

  • Sternenbegrenzung

  • Sitzungen abgeschlossen SET TRANSACTION READ ONLY auf PostgreSQL

  • Geheimnisse werden nicht in Tool-Fehlern zurückgegeben

Related MCP Connectors

Related MCP Servers