Skip to main content
Glama
ai-code-co

Lumenco Catalog MCP Server

by ai-code-co

Catálogo Lumenco (Fase 1 scraper + Fase 2 MCP)

Este repositorio tiene dos capas:

  1. La Fase 1 extrae https://en.staging.lumenco.ca/ a PostgreSQL.

  2. La Fase 2 expone ese catálogo a través de un servidor de Protocolo de Contexto de Modelo (MCP) de solo lectura para que Claude pueda recuperar productos, especificaciones, listados y candidatos de recomendación sin navegar por Lumenco.

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

La Fase 1 extrae. La Fase 2 expone. Claude razona.

El servidor MCP nunca extrae Lumenco, nunca descarga PDFs de especificaciones, nunca llama a un LLM y nunca escribe en la base de datos.

Cómo es el sitio

Lumenco staging es una tienda Magento 2.

Área

Comportamiento

Marcas

https://en.staging.lumenco.ca/brand enumera todas las marcas (Amasty Brands). Las tarjetas de marca en esa página a menudo apuntan a staging.lumenco.ca; el scraper las reescribe al host en inglés.

Listados de marcas

https://en.staging.lumenco.ca/brand/{slug} con paginación de Magento ?p=2 (24 productos por página). El número total de páginas está en #am-page-count.

Productos

URLs canónicas como /aaled-aa-900018-1x4-bl.html. El HTML renderizado en el servidor incluye JSON-LD, SKU, precio, stock, tabla de especificaciones y un enlace a la Hoja de especificaciones.

Hojas de especificaciones

Normalmente PDFs del mismo origen en /dev/*.pdf.

Mapa del sitio

/sitemap.xml actualmente falla con el error HTTP 500. El rastreador intenta rutas de sitemap conocidas y luego recurre al descubrimiento por marca y categoría.

GraphQL

/graphql existe pero el esquema de staging está roto (Config element "String" is not declared). El rastreo HTML es la fuente fiable.

Obtención

Las páginas de producto se renderizan en el servidor. El FetcherSession HTTP de Scrapling es el predeterminado. AsyncDynamicSession está registrado como respaldo diferido si a una página de producto le faltan campos obligatorios.

El rastreador permanece en en.staging.lumenco.ca. Los PDFs de hojas de especificaciones externas pueden descargarse como documentos de producto. Se ignoran los anuncios, análisis, carrito, pago y URLs de redes sociales.

robots.txt está escrito para motores de búsqueda públicos (User-agent: * no permite la mayoría de las rutas excepto /brand y algunas páginas CMS). Este scraper es una ingesta de catálogo autorizada contra staging, por lo que ROBOTS_TXT_OBEY está en false por defecto. Establézcalo en true si desea que Scrapling respete ese archivo.

Related MCP server: Catalog Services MCP Server

Estructura del proyecto

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. Instalar dependencias

Se requiere Python 3.10+.

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

pip install -r requirements.txt

Los extras HTTP/navegador de Scrapling se incluyen a través de scrapling[fetchers]. Si necesita el respaldo del navegador (DynamicFetcher), instale los binarios del navegador:

scrapling install

OCR opcional para PDFs de especificaciones escaneados o solo imagen:

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

El OCR está desactivado por defecto (ENABLE_OCR=false). Los PDFs basados en imagen se almacenan y se marcan como ocr_required en lugar de guardarse como texto vacío.

2. Configurar PostgreSQL

La configuración local más rápida:

docker compose up -d postgres

Eso inicia PostgreSQL 16 con:

  • usuario: lumenco

  • contraseña: lumenco

  • base de datos: lumenco

  • puerto del host: 5433 (el puerto del contenedor permanece en 5432; 5433 evita que una instalación de PostgreSQL de Windows que ya usa 5432)

Copie la configuración de entorno:

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

Cadena de conexión predeterminada:

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

Cree las tablas (cualquier enfoque funciona):

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

3. Ejecutar un rastreo de prueba de 5 productos

python -m scraper crawl --limit 5

Esto descubre productos del sitio en vivo, procesa solo los primeros 5, descarga sus hojas de especificaciones, almacena filas en PostgreSQL e imprime un informe de rastreo.

También puede fijar una marca:

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

O un solo producto:

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

4. Ejecutar el rastreo completo

python -m scraper crawl

Esto recorre todas las marcas (y listados de categorías), sigue cada página de paginación y extrae cada producto detectable. No confunda --limit con un tope de catálogo de producción — --limit es solo para desarrollo.

La limitación de velocidad está integrada: concurrencia, topes por dominio, retraso de descarga, reintentos con retroceso exponencial y AutoThrottle opcional. Ajústelos en .env:

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

5. Reanudar un rastreo

El punto de control de Scrapling está habilitado a través de CRAWL_DIR (por defecto ./data/crawl). Presione Ctrl+C una vez para una pausa elegante. Ejecute de nuevo con:

python -m scraper crawl --resume

Comportamiento de reanudación:

  • Scrapling restaura las solicitudes pendientes desde CRAWL_DIR.

  • Los productos ya almacenados con scrape_status=success se omiten a menos que pase --force.

  • Los productos fallidos se reintentan.

  • Los PDFs de especificaciones no se vuelven a extraer cuando el hash del documento no ha cambiado.

6. Inspeccionar la base de datos

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

O con psql:

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

Consultas útiles:

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. Cómo se procesan las hojas de especificaciones

Para cada página de producto, el analizador busca:

  • a.document-item-link (el control "Hoja de especificaciones" de Lumenco)

  • Etiquetas equivalentes: Hoja de especificaciones, Ficha técnica, Especificaciones, Datos técnicos, PDF, Fiche technique, etc.

Luego, la canalización:

  1. Almacena la URL del documento.

  2. Descarga el archivo con httpx (no un navegador).

  3. Valida los bytes mágicos del PDF (%PDF).

  4. Guarda una copia determinista: data/specifications/{sku}_{hash16}.pdf.

  5. Extrae el texto con PyMuPDF.

  6. Limpia los espacios en blanco manteniendo los saltos de página/sección.

  7. Almacena el texto extraído, el hash SHA-256, el método y el estado.

  8. Analiza las líneas Etiqueta: Valor del PDF sin inventar campos.

  9. Fusiona las especificaciones del PDF con las de la página del producto, conservando la fuente:

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

Si un PDF tiene poco o ningún texto, el estado es ocr_required (o se intenta el OCR cuando ENABLE_OCR=true). Las extracciones exitosas vacías no se almacenan silenciosamente.

Los PDFs sin cambios se omiten en rastreos posteriores mediante el hash de contenido.

8. Solución de problemas de productos fallidos

Síntoma

Qué hacer

python -m scraper validate informa problemas

Lea la lista JSON issues (missing_name, invalid_url, empty_extracted_text, …).

El producto falló con HTTP 5xx / tiempo de espera agotado

Vuelva a ejecutar python -m scraper crawl --resume. Los fallos están en crawl_errors.

Falta la hoja de especificaciones

Esperado para algunos SKUs. El estado es not_found; la fila del producto aún se almacena.

PDF marcado como ocr_required

Habilite los extras de OCR o inspeccione el archivo guardado en data/specifications/.

PDF marcado como invalid_pdf

El archivo vinculado no era un PDF (página de error HTML, etc.). Compruebe specification_documents.error_message.

Productos duplicados

No debería suceder: product_url / canonical_url / sku únicos más upsert. Ejecute validate.

Las páginas de marca se ven vacías

Confirme que está en en.staging.lumenco.ca, no en el host francés. El spider lo reescribe automáticamente.

Errores de DynamicFetcher

Ejecute scrapling install. La obtención HTTP es suficiente para el HTML de staging actual.

Errores de conexión de base de datos

Compruebe DATABASE_URL, docker compose ps y python -m scraper init-db.

Los registros estructurados se ven así:

[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=...

Pruebas

pytest

La cobertura incluye normalización de URL, análisis de producto/SKU/precio, detección de hojas de especificaciones, extracción de PDF, upsert/prevención de duplicados en la base de datos, orden de pertenencia a listados, puntuación de recomendaciones e integración de herramientas MCP.

Referencia de CLI

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

El modo de rastreo predeterminado es desarrollo: como máximo 100 productos procesados correctamente, solo marcas (sin recorrido de categorías). Se rechaza un rastreo de catálogo completo a menos que pase --mode full --limit N o --mode full --confirm-full.

Fase 2.5 — Calidad de datos de 100 productos

Este proyecto actualmente se dirige a un conjunto de datos Lumenco controlado de ~100 productos. El catálogo en vivo tiene más de 30,000 SKUs; el rastreo de catálogo completo está explícitamente fuera de alcance.

Canalización

Scrapling → extracción de productos → descarga de PDF → texto PDF u OCR → normalización de especificaciones → PostgreSQL → MCP de solo lectura

La extracción de texto PDF se intenta primero. El OCR (Tesseract a través de pytesseract) se ejecuta solo cuando el PDF no tiene texto significativo. Establezca ENABLE_OCR=true e instale Tesseract más pip install pytesseract Pillow.

Las especificaciones normalizadas mantienen la fuente y las banderas de conflicto. El texto sin procesar de la hoja de especificaciones se almacena en specification_documents.extracted_text. El get_product de MCP devuelve especificaciones estructuradas compactas; get_product_specifications puede incluir texto sin procesar cuando include_raw_text=true.

Las URLs de categorías francesas de Magento (por ejemplo, /eclairage-interieur y /electricite) aún aparecen en el encabezado compartido en el host en inglés. Allí devuelven 404. El rastreador no pone en cola esas rutas. Los nuevos rastreos también usan un directorio de punto de control de Scrapling aislado (data/crawl/run-<id>) para que un archivo de pausa antiguo no pueda reanudar miles de URLs de categorías. Use --resume solo para continuar el punto de control compartido data/crawl.

Las URLs de categorías francesas de Magento reescritas en el host en inglés se clasifican como expected_404 y no se cuentan como fallos de producto.

Después de un rastreo:

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

Fase 3A — Búsqueda vectorial + incrustaciones de productos

La Fase 3A agrega representaciones semánticas de productos con PostgreSQL + pgvector. No implementa la clasificación de Relacionados/Mejora/Venta cruzada (eso es la Fase 3B).

Arquitectura

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

Configuración

  1. Use una imagen de Postgres con pgvector (docker-compose.yml usa pgvector/pgvector:pg16).

  2. Establezca las variables de entorno de incrustación en .env (vea .env.example).

  3. Migre:

python -m alembic upgrade head
  1. Genere incrustaciones para el catálogo de desarrollo:

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

Los productos sin cambios se omiten mediante content_hash. Use --force para regenerar todo.

Estrategia de índice

HNSW en distancia coseno (vector_cosine_ops, m=16, ef_construction=64) — bueno para el conjunto de datos de ~100 productos y sigue siendo utilizable a medida que crece el catálogo. IVFFlat se puede considerar más adelante para catálogos mucho más grandes.

MCP

Nueva herramienta de solo lectura: search_similar_products. Solo lee vectores almacenados; no llama a la API de incrustación ni extrae Lumenco. Las herramientas de recomendación existentes no cambian.

Fase 3B — Motor de recomendación híbrido

Las recomendaciones combinan similitud de pgvector con reglas de productos estructurados. La similitud vectorial por sí sola no es suficiente: un tubo T8 de 18 W, un tubo T8 de 30 W y una luminaria T8 pueden ser semánticamente cercanos, pero se asignan a Relacionados, Mejora y Venta cruzada respectivamente.

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

Tipo

Significado

Relacionados

Caso de uso / categoría / especificaciones similares

Mejora

Misma familia y mejora medible (no solo precio)

Venta cruzada

Complementario (driver, recorte, carcasa, luminaria↔tubo)

No se usa ningún LLM dentro de la clasificación. Las herramientas MCP find_related_products, find_upsell_products y find_cross_sell_products llaman a RecommendationService (solo lectura).

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

Los pesos se pueden configurar a través de variables de entorno como RELATED_VECTOR_WEIGHT, UPSELL_TECHNICAL_WEIGHT, CROSS_SELL_COMPATIBILITY_WEIGHT (vea .env.example).

Fase 3C — Flujo de trabajo Claude + MCP

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

Responsabilidades

Capa

Función

Scrapling

Rastreo / almacenamiento

PostgreSQL + pgvector

Fuente de verdad + vectores

RecommendationService

Ranking determinista de Relacionados/Upsell/Cross-sell

MCP

Recuperación de solo lectura (sin rastreo, sin escrituras, sin LLM)

Claude

Conversación, selección de herramientas y explicación

Habilidad de Claude

Habilidad del proyecto: .cursor/skills/lumenco-product-mcp/SKILL.md

Prompts de extremo a extremo

Consulta docs/claude-e2e-tests.md.

Conectar Claude / Inspector

  1. docker compose up -d postgres

  2. python -m app.server

  3. Apunta el cliente a http://localhost:8000/mcp (Streamable HTTP)

  4. Opcional: MCP_AUTH_TOKEN + Authorization: Bearer …

Para el despliegue remoto más adelante: expón solo el endpoint HTTPS del MCP; mantén PostgreSQL privada.

Conjunto de datos de desarrollo

Catálogo actual: ~100 productos. El catálogo completo de Lumenco (30k+) está intencionadamente fuera de alcance.

Fase 2 — Lumenco Product Database MCP

Servidor MCP Streamable HTTP de solo lectura llamado Lumenco Product Database.

Arquitectura

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

Configuración local

  1. Completa la configuración de la Fase 1 (PostgreSQL + .env + python -m alembic upgrade head).

  2. Ejecuta un rastreo para que el catálogo quede poblado.

  3. Instala los extras de MCP si aún no están en requirements.txt:

pip install -r requirements.txt
  1. Define las variables de MCP en .env:

MCP_HOST=0.0.0.0
MCP_PORT=8000
MCP_AUTH_TOKEN=replace-with-a-long-random-token
DATABASE_URL=postgresql+psycopg2://lumenco:lumenco@127.0.0.1:5433/lumenco
DB_POOL_SIZE=10
DB_MAX_OVERFLOW=20
DB_POOL_TIMEOUT=30

Para producción, crea un rol de solo SELECT:

psql postgresql://lumenco:lumenco@127.0.0.1:5433/lumenco -f scripts/create_readonly_user.sql

Después apunta DATABASE_URL a lumenco_mcp.

Ejecución

python -m app.server

O:

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

Docker:

docker compose up --build mcp

Endpoint de MCP

http://localhost:8000/mcp

Estado de salud

GET http://localhost:8000/health

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

MCP Inspector

npx -y @modelcontextprotocol/inspector

Conecta con http://localhost:8000/mcp con el transporte Streamable HTTP. Si MCP_AUTH_TOKEN está definido, añade:

Authorization: Bearer <token>

Confirma que las ocho herramientas aparecen listadas y se pueden ejecutar.

Herramientas disponibles

Todas las herramientas solo leen PostgreSQL. Ninguna recupera las URLs de Lumenco.

get_catalog_status

Tamaño del catálogo y frescura del último rastreo. Sin entrada.

get_listing_products

Productos de una URL de listado de una marca/categoría, en la posición original del listado.

Entrada

Requerido

Notas

listing_url

Normalizada y usada como clave de base de datos

limit

no

Por defecto 20, máximo 100

offset

no

Por defecto 0

get_product

Registro completo del producto por product_id y/o sku.

get_product_specifications

Especificaciones estructuradas más el texto de la ficha de especificaciones almacenada. No descarga PDFs.

search_products

Búsqueda en el catálogo local (SKU, nombre, marca, categoría, descripción, especificaciones).

Filtros opcionales: brand, category, subcategory, sku, min_price, max_price.

search_similar_products

Vecinos semánticos a partir de los embeddings pgvector almacenados (similitud por coseno). No genera embeddings ni llama a un LLM.

Filtros opcionales: brand, category, subcategory, min_price, max_price.

Candidatos híbridos de Related (vector + categoría/aplicación/especificaciones). Incluye match_score, confidence, score_breakdown y match_reasons. debug=true opcional.

find_upsell_products

Candidatos híbridos de Upsell. Requiere una mejora medible (no solo el precio). Razones en upgrade_reasons.

find_cross_sell_products

Candidatos híbridos de Cross-sell. La compatibilidad domina; se excluyen alternativas de la misma familia.

Las herramientas de recomendación excluyen el producto fuente y deduplican los candidatos. Claude debe solicitar un pool de candidatos y luego elegir la selección final: 3 Related / 4 Upsell / 7 Cross-sell.

Flujo de ejemplo

El usuario: analiza los primeros 10 productos de https://en.staging.lumenco.ca/brand/aaled y devuelve 3 Related, 4 Upsell, 7 Cross-sell.

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

  2. get_product_list get_product(product_id=...) para cada fuente

  3. find_related_products / find_upsell_products / find_cross_sell_products con limit=10

  4. Claude selecciona el conjunto final de los pools de candidatos

Despliegue en producción

Expone solo el endpoint HTTPS de MCP. Mantén PostgreSQL privada.

Internet → HTTPS → MCP server → private PostgreSQL

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

Requisitos:

  • Terminador HTTPS delante de uvicorn / de la imagen Docker

  • MCP_AUTH_TOKEN establecido (el middleware Bearer está aislado, de modo que OAuth podrá sustituirlo más adelante)

  • DATABASE_URL de solo lectura

  • Health check en /health

No publicar el puerto 5432.

Conector personalizado de Claude

Cuando el servidor sea accesible mediante una URL HTTPS pública:

  1. En Claude, añade un conector personalizado.

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

  3. El nombre del servidor debe aparecer como Lumenco Product Database.

  4. Configura la autenticación Bearer con MCP_AUTH_TOKEN, u OAuth si sustituyes el middleware.

  5. Pregunta: "¿Cuántos productos hay actualmente en la base de datos de Lumenco?" Claude debe llamar a get_catalog_status.

HTTPS público temporal para pruebas locales: Cloudflare Tunnel, ngrok o similar delante de localhost:8000.

Seguridad

  • No hay herramientas execute_sql, fetch_url, run_command ni de rastreo

  • Solo consultas parametrizadas de SQLAlchemy

  • Límites de consulta aplicados

  • Las sesiones abren SET TRANSACTION READ ONLY en PostgreSQL

  • Los secretos no se devuelven en errores de las herramientas

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Federated commerce search across independent WooCommerce merchants. Keyless, read-only MCP server.

  • Agent-native product catalog for AI shopping agents. 296M+ products, 28 countries.

  • Manage products, EU Digital Product Passports, operator parties, and GS1 EPCIS supply-chain events.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ai-code-co/Claude_MCP_Lumenco'

If you have feedback or need assistance with the MCP directory API, please join our Discord server