mcp-supermercados-cl
[](https://mseep.ai/app/nlace-com-mcp-supermercados-cl)
# 🛒 mcp-supermercados-cl
> Servidor **MCP** para buscar productos, comparar precios y armar la mejor lista
> de compra en supermercados chilenos con Claude, ChatGPT u otro cliente MCP.
[](https://www.npmjs.com/package/mcp-supermercados-cl)



El foco es **profundidad en la cadena donde tú ya compras** — precios club,
beneficios por RUT, productos frecuentes, carro — con la comparación entre
cadenas como capacidad secundaria. Cubre las cinco grandes cadenas del país.
100 % local: el tráfico sale de **tu** máquina, a ritmo humano, y tus
credenciales nunca tocan un servidor central.
---
## Tabla de contenidos
- [¿Se deploya? (importante)](#-se-deploya-en-vercelaws-no)
- [Instalación](#-instalación)
- [Cobertura por cadena](#-cobertura-por-cadena)
- [Tools disponibles](#-tools-disponibles)
- [Cómo funciona la sesión](#-cómo-funciona-la-sesión-sin-credenciales-en-el-servidor)
- [Desarrollo y tests](#-desarrollo-y-tests)
- [Arquitectura](#-arquitectura)
- [Cómo contribuir](#-cómo-contribuir)
- [Aviso legal](#-aviso-legal)
- [Licencia](#-licencia)
---
## 🚫 ¿Se deploya en Vercel/AWS? No
**Este MCP no tiene URL de producción y no se deploya en ningún servidor.** Es
intencional, y es la razón por la que funciona:
- Usa transporte **stdio** (local), no HTTP. Corre en tu máquina, junto a tu
cliente MCP (Claude Desktop, Claude Code, Cursor, ChatGPT Desktop).
- Unimarc, Tottus y Lider **bloquean el tráfico de datacenter** (antibots). Un
deploy en la nube **no funcionaría** para esas cadenas: necesitan tu IP
residencial.
- El precio socio, tus frecuentes y el carro viven en **tu navegador logueado**.
Las credenciales no deben tocar un servidor central — eso además evita el
mayor riesgo legal (un servicio que scrapee cuentas ajenas).
La forma de "producción" de un MCP como este es **instalarlo local** (vía `npx`
o clonando el repo) y conectarlo a tu cliente. Igual que la mayoría de los MCP
servers.
---
## 📦 Instalación
Requiere **Node.js ≥ 20**. Publicado en npm:
[`mcp-supermercados-cl`](https://www.npmjs.com/package/mcp-supermercados-cl).
**Opción 1 — vía `npx` (recomendada).** No instalas nada; tu cliente MCP lo
ejecuta al vuelo. En **Claude Desktop** / **Claude Code**
(`claude_desktop_config.json` o `.mcp.json`):
```json
{
"mcpServers": {
"supermercados": {
"command": "npx",
"args": ["-y", "mcp-supermercados-cl"]
}
}
}
```
**Opción 2 — desde el código (para desarrollar o contribuir):**
```bash
git clone https://github.com/NLACE-COM/mcp-supermercados-cl.git
cd mcp-supermercados-cl
npm install
npm run build
```
Y apunta tu cliente al build local:
```json
{
"mcpServers": {
"supermercados": {
"command": "node",
"args": ["/ruta/absoluta/al/repo/dist/index.js"]
}
}
}
```
Para desarrollo rápido:
```bash
npm run dev # servidor por stdio con tsx
npm run inspector # abre el MCP Inspector
```
---
## 🏬 Cobertura por cadena
| Cadena | Plataforma | Búsqueda | Precio socio | Detalle | Sesión / carro |
| ---------------- | ------------------------- | :------: | :-------------: | :-----: | :--------------------------: |
| **Jumbo** | Cencosud (Constructor.io) | ✅ | ✅ Prime | ✅ | ✅ frecuentes, listas, carro |
| **Santa Isabel** | Cencosud (Constructor.io) | ✅ | ✅ | ✅ | carro Cencosud¹ |
| **Unimarc** | VTEX (BFF propio) | ✅ | ✅ Club Unimarc | — | — |
| **Tottus** | Falabella (Next.js SSR) | ✅ | — | — | — |
| **Lider** | Walmart Glass (SSR) | ✅ | —² | — | — |
¹ El carro de Santa Isabel reutiliza el mismo BFF Cencosud que Jumbo; se activa
con tu sesión en santaisabel.cl.
² Lider no expone precio socio dual como el Prime de Jumbo; sus descuentos son
rebajas directas ("Precio Lider") + bundles.
> ⚠️ Unimarc, Tottus y Lider requieren **IP residencial** (tu máquina); desde
> datacenter bloquean. Como el MCP corre local, en tu equipo funcionan.
Todos los resultados vienen **enriquecidos**: nombre, marca, descripción, foto,
precio vigente, precio normal, precio socio, **precio por unidad normalizado**
(por kg/lt para comparar formatos) y **bundles** ("2 x $2.000", "Lleva 8 por $X").
---
## 🧰 Tools disponibles
**Núcleo — armar la mejor lista con tu sesión:**
| Tool | Qué hace |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `build_list` | Convierte una lista en lenguaje natural en productos concretos. Prioriza tus frecuentes, mejor precio por unidad y ofertas. Flags `onlyOffers` / `onlyInStock` y `maxBudget` (ajusta a alternativas más baratas para caber). Incluye resumen formateado. |
| `suggest_swaps` | Reemplazos convenientes por precio por unidad. Con `preferNatural`: alternativas de precio similar con menos ingredientes. |
| `get_frequent_purchases` | Tus productos habituales, con precio Prime (requiere sesión). |
| `get_saved_lists` | Tus listas guardadas (requiere sesión). |
| `add_to_cart` / `get_cart` | Deja la lista en el carro de Jumbo; total, ahorro y ahorro Prime. |
**Lectura de catálogo:**
| Tool | Qué hace |
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
| `search_products` | Busca en cualquier cadena. Filtros `maxPrice`/`minPrice`/`inStockOnly`, orden `sortBy` (price / unitPrice). |
| `get_product` | Detalle por URL/slug: precio socio, EAN, **ingredientes** y sellos nutricionales. |
| `get_offers` | Ofertas vigentes de Jumbo; `primeOnly`, filtro por categoría. |
| `find_opportunities` | Mayores descuentos con stock, ordenados por `discountPct`. `excludeIds` para destacar lo que no tienes. |
**Comparación y diagnóstico:**
| Tool | Qué hace |
| ----------------- | --------------------------------------------------------------------------------------------------- |
| `compare_stores` | Total de una lista en varias cadenas; marca la más barata y advierte si compara formatos distintos. |
| `discover_branch` | Descubre tu sucursal (branchId) leyéndola del navegador, para no pedírtela a mano. |
| `adapter_status` | Qué cadenas responden ahora y con qué latencia. |
### 💬 Prompts guiados
Para no adivinar qué pedir, el servidor expone plantillas que tu cliente MCP
muestra como sugerencias: **`armar_lista`** (con presupuesto opcional),
**`conectar_sesion`**, **`comparar_carro`** y **`ofertas_frecuentes`**. El
servidor además trae `instructions` para que el modelo te guíe en el primer uso
(qué cadena, cuándo pedir sesión, cómo leer los errores).
Los errores vienen **accionables**: cada uno trae un campo `action` con el
siguiente paso concreto (re-loguearte, reintentar, usar IP residencial…) en vez
de un mensaje técnico.
---
## 🔐 Cómo funciona la sesión (sin credenciales en el servidor)
El precio socio, los frecuentes y el carro viven detrás del login. En Jumbo, el
token vive en el `localStorage` del navegador, así que **el servidor nunca ve
credenciales**: el cliente (junto a tu navegador logueado) extrae los datos del
DOM o ejecuta las llamadas autenticadas, y el MCP solo normaliza el resultado.
Ver [`src/adapters/session.ts`](src/adapters/session.ts) y
[`docs/captura-cencosud-2026-07-06.md`](docs/captura-cencosud-2026-07-06.md).
### ¿Y esas API keys que aparecen en el código?
Verás claves como `key_JopvNXKS61kwGkBe` (Jumbo) o
`be-reg-groceries-sisa-catalog-wdhhq5a2fken` (Santa Isabel) en los adaptadores.
**No son secretos.** Son las claves **públicas del frontend** de Constructor.io
y del BFF de catálogo: van embebidas en el JavaScript de jumbo.cl y
santaisabel.cl, y son visibles en las DevTools de cualquier visitante. Solo
identifican el índice de búsqueda del lado cliente — **no dan acceso a ninguna
cuenta ni permiten escribir**. Sin ellas, el buscador no responde.
Los datos que **sí** son sensibles (token de sesión, precio socio, carro) viven
en tu navegador logueado y **nunca** están en este repositorio. Un escáner
automático puede marcar estas claves públicas como "token expuesto"; es un
falso positivo.
---
## 🧪 Desarrollo y tests
```bash
npm test # tests de contrato con fixtures reales (sin red) — 160 tests
npm run test:live # smoke contra los sitios reales (opt-in, LIVE=1)
npm run test:session # smoke de las tools de sesión de Jumbo (requiere tu navegador)
npm run typecheck # tsc --noEmit
npm run lint # ESLint
npm run format # Prettier (--write); format:check para verificar
```
### Smoke de sesión (`test:session`)
Las tools de sesión de Jumbo (carro, listas guardadas, frecuentes) necesitan el
token que vive en el `localStorage` de un navegador logueado, así que ni los
tests de contrato ni el smoke live las cubren. Este smoke cierra ese hueco
(issue #12) con el puente de Playwright sobre un perfil **dedicado**:
```bash
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --no-save playwright # una vez
npm run session:login # una vez: abre Chrome, inicias sesión y eliges tienda
npm run test:session # cada vez que quieras verificar (~30 s)
```
`session:login` abre una ventana con un perfil aparte
(`~/.supermercados-smoke-profile`, configurable con `SUPERMERCADOS_SMOKE_PROFILE`),
espera a que inicies sesión y se cierra sola. Tus credenciales las escribes tú
en esa ventana; el proyecto solo comprueba que el token exista, nunca su valor.
> **Por qué un perfil aparte y no el tuyo**: `launchPersistentContext` toma el
> lock exclusivo del perfil, así que usar el de tu Chrome diario obliga a
> cerrarlo en cada corrida — y sobre un perfil grande (7,8 GB al probarlo) el
> lanzamiento ni siquiera completa: muere por timeout sin llegar a conectar. Con
> perfil dedicado arranca en ~2 s y tu navegador ni se entera.
Qué valida: que el sitio real siga **aceptando** los requests que arman los
snippets de producción y que los parsers entiendan la respuesta.
**Vale la pena correrlo antes de publicar una versión.** Sin él, un cambio de
contrato de Jumbo (rotación de `apiKey`, cambio del DOM de frecuentes) llega a
producción sin aviso: pasó en la 1.4.4, donde el carro y las listas estuvieron
rotos dos días hasta que lo reportó un usuario.
Los tests de contrato usan respuestas reales grabadas en
[`tests/fixtures/`](tests/fixtures). Los live requieren red y, para
Unimarc/Tottus/Lider, IP residencial. Regraba una fixture cuando una cadena
cambie su formato, anotando la fecha.
Cada push y PR corre `lint + typecheck + build + test` en CI (GitHub Actions,
Node 20 y 22). Un smoke live semanal avisa por issue si una cadena cambia su
formato. Para contribuir, revisa [CONTRIBUTING.md](CONTRIBUTING.md).
### Flujo de sesión: manual o automatizado
Las tools que requieren sesión (`get_cart`, `get_frequent_purchases`,
`get_saved_lists`, `add_to_cart`) devuelven un `browserSnippet`: un fetch de
**una sola llamada** para ejecutar en una pestaña ya logueada del sitio. Pasas
el JSON de vuelta y la tool lo normaliza — el servidor nunca ve tu token.
Para automatizarlo (sin copiar/pegar), existe un puente opcional con Playwright
(`src/adapters/playwrightBridge.ts`) que reusa el perfil de Chrome donde ya
tienes la sesión. Playwright **no** viene con el paquete (es pesado); instálalo
aparte si lo quieres:
```bash
npm install playwright
npx playwright install chromium
```
#### Líder y Tottus: puente automático contra el antibot
Líder y Tottus bloquean el fetch del servidor por **fingerprint del cliente**
(TLS/JA3 + desafío JS de PerimeterX; en Líder además F5 BIG-IP con `307 → /blocked`).
No es tu IP: la misma IP en un navegador real carga los datos ([issue #2](https://github.com/NLACE-COM/mcp-supermercados-cl/issues/2)).
Hay dos formas de sortearlo en `search_products`, `compare_stores` y
`build_cheapest_basket`:
1. **Manual**: `search_products` devuelve `openUrl` + `browserSnippet`; abres esa
búsqueda en tu navegador, ejecutas el snippet (lee `__NEXT_DATA__` del DOM) y
reintentas pasando el resultado en `browserHtml`.
2. **Automático**: si configuras el puente Playwright por entorno, el servidor
navega solo reusando tu perfil de Chrome (nunca ve tu token) y resuelve esas
cadenas sin intervención. Variables (en la sección `env` de tu cliente MCP):
| Variable | Requerida | Descripción |
| ----------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SUPERMERCADOS_PLAYWRIGHT_PROFILE` | sí | Carpeta del perfil de Chrome con tu sesión (`userDataDir`). Activa el puente. |
| `SUPERMERCADOS_PLAYWRIGHT_PATH` | con `npx` | Carpeta del paquete `playwright` cuando está instalado **global** (por `npx` el server no lo resuelve solo). Valor: salida de `npm root -g` + `/playwright`. |
| `SUPERMERCADOS_PLAYWRIGHT_CHANNEL` | no | `chrome` o `msedge` para usar el navegador instalado (si no, el Chromium de Playwright). |
| `SUPERMERCADOS_PLAYWRIGHT_HEADLESS` | no | `1` para headless (por defecto con ventana, evita gatillar antibots/2FA). |
Requiere Playwright instalado y **Chrome cerrado** (para no chocar con el lock
del perfil). Sin estas variables, el comportamiento es el manual de arriba.
**Con `npx` (Claude Desktop, etc.):** el paquete corre en un cache efímero sin
Playwright, así que instálalo global (`npm install -g playwright`) y apunta
`SUPERMERCADOS_PLAYWRIGHT_PATH` a su carpeta. `NODE_PATH` **no** sirve: el
server carga Playwright con `import()` (ESM) y `NODE_PATH` solo aplica a
`require()` de CommonJS.
```json
{
"mcpServers": {
"supermercados-cl": {
"command": "npx",
"args": ["-y", "mcp-supermercados-cl@latest"],
"env": {
"SUPERMERCADOS_PLAYWRIGHT_PROFILE": "/Users/tu-usuario/Library/Application Support/Google/Chrome",
"SUPERMERCADOS_PLAYWRIGHT_PATH": "/ruta/de/npm-root-g/playwright",
"SUPERMERCADOS_PLAYWRIGHT_CHANNEL": "chrome"
}
}
}
}
```
---
## 🏗 Arquitectura
- **Un servidor, un adaptador por cadena** (`src/adapters/`). Esquema
normalizado con zod (`src/core/types.ts`): precio normal y precio socio
**separados**, precio por unidad normalizado a base canónica.
- **HTTP a ritmo humano, por tipo de host**: los endpoints de API (Constructor.io
y los BFF de Cencosud/Unimarc/Santa Isabel) van a ~350 ms; los sitios que se
scrapean por SSR (Tottus, Lider, PDPs `www.*`) mantienen ~1 s. Reintentos con
backoff, user-agent realista (`src/http/client.ts`). Cache TTL 15 min.
Ajustable por entorno: `SUPERMERCADOS_MIN_DELAY_MS`, `SUPERMERCADOS_FAST_DELAY_MS`,
`SUPERMERCADOS_TIMEOUT_MS`, `SUPERMERCADOS_MAX_RETRIES`.
- **Feedback en vivo**: `build_list` y `compare_stores` emiten notificaciones de
progreso MCP (`notifications/progress`) si el cliente las soporta, para no
quedar en silencio durante listas largas. `compare_stores` limita cada cadena
a 25 s y devuelve resultado parcial en vez de bloquear a las demás.
- **Adaptadores aislados**: un cambio de sitio rompe un adaptador, no todo.
- Endpoints documentados en [`docs/`](docs) y en
[`docs/PLAN-arquitectura.md`](docs/PLAN-arquitectura.md).
```
src/
├── index.ts # entrada MCP (stdio)
├── server.ts # registro de tools
├── core/ # types, registry, normalize, listBuilder, compare, ...
├── adapters/ # cencosud (Jumbo+Santa Isabel), unimarc, tottus, lider
├── tools/ # una tool MCP por archivo
└── http/ # cliente HTTP con rate limit y reintentos
```
---
## 🤝 Cómo contribuir
¡Bienvenidas las contribuciones! Este proyecto está pensado para crecer con la
comunidad. Ver [CONTRIBUTING.md](CONTRIBUTING.md).
Ideas de alto impacto:
- Carro/sesión en Unimarc, Tottus y Lider (cada una con su login propio).
- Detalle (`get_product`) para Unimarc/Tottus/Lider.
- Nuevas cadenas o farmacias.
- Mantener las fixtures al día cuando una cadena cambie su API.
Cuando una cadena cambie su formato, `npm run test:live` lo detecta.
---
## ⚖️ Aviso legal
Herramienta **personal**, de código abierto, sin backend central. Cada usuario
opera su propia cuenta desde su propia IP, a ritmo humano, sin redistribuir
datos. Revisa los Términos y Condiciones de cada cadena antes de usarla. **No
afiliado** a Cencosud, SMU, Falabella ni Walmart. Las marcas mencionadas
pertenecen a sus respectivos dueños. Úsalo bajo tu propia responsabilidad.
---
## 📄 Licencia
[MIT](LICENSE) © contribuidores de mcp-supermercados-cl
TDQS
Scored across 14 tools
Most tools target distinct resources/actions, and rich descriptions separate search, offers, cart, lists, and cross-store comparisons. Some pairs overlap conceptually (get_offers vs find_opportunities, build_list vs suggest_swaps, compare_stores vs build_cheapest_basket), but the descriptions provide enough cues to avoid serious misselection.
Names overwhelmingly follow a clear verb_object snake_case pattern (get_saved_lists, build_list, search_products, compare_stores). The main deviation is adapter_status, which is a noun phrase rather than an imperative verb and would be more consistent as get_adapter_status.
At 14 tools, the count is within the ideal 3-15 range and matches the broad scope of a multi-chain supermarket assistant. Each tool covers a distinct workflow step, from product discovery to cart management and store comparison, so none feels redundant.
The surface covers the core shopping lifecycle well: search, product details, offers, list building, frequent products, cart reads/updates, and cross-store cost comparison. Minor gaps remain, like no saved-list mutation and no explicit cart item removal, but agents can work around these without dead-ending.