Use when the user mentions ePromo, epromo.ee, or wants to search, compare prices, browse, or buy products at ePromo. Triggers on Estonian grocery requests mentioning ePromo (otsi, võrdle, hinnad, osta, ostukorv). ALWAYS use this skill's search scripts instead of navigating to epromo.ee in the browser.
CRITICAL: For product searches and browsing, ALWAYS use epromo-search.sh. NEVER open epromo.ee in the browser to search or browse products. The browser is ONLY needed for cart operations (add/view/clear) because Cloudflare blocks API PUT requests.
Build an ePromo.ee grocery cart through conversation, or help the user research and compare products.
Note: ePromo is a HoReCa/wholesale-oriented store — minimum order quantities are often 1–10 kg, and packages are larger than in regular grocery shops. Keep this in mind when suggesting products.
ePromo.ee is behind Cloudflare WAF. This plugin uses a hybrid approach:
epromo-search.sh (uses curl_cffi with Chrome TLS impersonation + authenticated POST to search-products)javascript_tool in a Chrome browser tab (Cloudflare blocks non-browser PUT requests)curl_cffi Python package installed (pip3 install curl_cffi)https://epromo.ee/auth/login and help the user log in firstThe search script needs authentication for correct results. Credentials are stored in ~/.config/epromo/credentials and persist across sessions.
If credentials are already saved, skip this section.
To set up or refresh credentials:
tabs_context_mcp — must be logged in at epromo.eejavascript_tool:JSON.stringify({
token: document.cookie.match(/token=([^;]+)/)?.[1] || 'not found',
address: decodeURIComponent(document.cookie.match(/DeliveryAddress=([^;]+)/)?.[1] || 'not found')
})
epromo-setup.sh:epromo-setup.sh <token> <address>
cf_clearance (optional, needed if Cloudflare blocks requests): ask the user to copy this cookie value from browser DevTools (Application > Cookies > cf_clearance), then re-run:epromo-setup.sh <token> <address> <cf_clearance>
Token validity: ~365 days. cf_clearance: hours to days (refresh when searches start failing).
epromo-search.sh (fast, no browser needed if env vars set)tabs_context_mcp — look for a tab with epromo.ee in the URLjavascript_tool in the browser tabAlways confirm product choices with the user before adding to cart. If a search returns multiple close matches, ask which one they want.
When the user wants to browse, research, or compare products (not necessarily buy):
epromo-search.shAll scripts are in the scripts/ directory relative to this file.
Search for products by keyword. Uses the search-products POST endpoint with authentication for Estonian names and correct stock levels.
epromo-search.sh <term> [count]
term — search query (e.g. "piim", "juust", "kana filee")count — number of results, defaults to 6~/.config/epromo/credentials (run epromo-setup.sh first)Returns JSON array with: id, name, price, unit, inStock, inStockAmount, minAmount, priceCoeff, storageType.
Save authentication credentials to ~/.config/epromo/credentials.
epromo-setup.sh <token> <address> [cf_clearance]
token — JWT token from browser cookie named tokenaddress — delivery address ID from browser cookie named DeliveryAddresscf_clearance — optional, Cloudflare clearance cookie (httpOnly, must be copied from DevTools)Cart operations must be made via mcp__claude-in-chrome__javascript_tool in an epromo.ee tab.
const token = document.cookie.match(/token=([^;]+)/)[1];
fetch('/api/proxy/quick-search?search=' + encodeURIComponent(TERM) + '&count=1&page=1')
.then(r => r.json())
.then(data => {
const product = data.products[0];
return fetch('/api/proxy/update-b2c-cart', {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
body: JSON.stringify({ cartItems: [{ amount: AMOUNT, product: product }] })
});
})
.then(r => r.json())
product object from search must be passed as-is to the cart API — it needs the full objectamount — quantity to add (respect minimumAmount from search results)Authorization: Bearer <token> header — extract token from cookiescartItems arrayconst token = document.cookie.match(/token=([^;]+)/)[1];
// products = array of { product: <full product object from search>, amount: N }
fetch('/api/proxy/update-b2c-cart', {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
body: JSON.stringify({ cartItems: products })
}).then(r => r.json())
const token = document.cookie.match(/token=([^;]+)/)[1];
fetch('/api/proxy/get-b2c-checkout-summary', {
headers: { 'Authorization': 'Bearer ' + token }
})
.then(r => r.json())
.then(data => JSON.stringify(data, null, 2))
To update quantities or remove items, send the full desired cart state:
const token = document.cookie.match(/token=([^;]+)/)[1];
fetch('/api/proxy/update-b2c-cart', {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
body: JSON.stringify({ cartItems: [] }) // empty array clears the cart
}).then(r => r.json())
curl_cffi (Chrome TLS impersonation). Cart PUT requests MUST go through the browser.token cookie (~365 days). cf_clearance expires in hours/days.languages: 'et' header, product names are in Estonian. Without auth, names default to English.addressid header for accurate availability. Without it, all products show as out of stock.minimumAmount > 1 — ePromo is HoReCa/wholesale, so packages are large (1–10 kg typical).storageType — "termo" (refrigerated), "frost" (frozen), "dry" (ambient).priceWithVat for the displayed price. priceCoefficient shows the per-unit price (e.g. "0,96€/l").