Basketeer
About
Drive a personal UK Tesco grocery account: search, basket, delivery slots, orders, and on-pack nutrition. Filter and rank products by macros + micros. Catalogue and nutrition tools need no auth.
Details
- Author
- tobyandrews1985
- Categories
- Other, Security, Productivity, Search
Jump to
Setup
Install Basketeer in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/tobyandrews1985/basketeer
Follow the installation instructions in the repository README, then restart your MCP client.
A typed, pure-HTTP TypeScript SDK for your own Tesco grocery account, with on-pack nutrition normalized into typed data.
Run your weekly shop from code, the terminal, or an AI agent. Everything but sign-in and payment is plainfetch, and products come back with their on-pack nutrition normalized into typed macrosandmicronutrients you can search and rank by.
Unofficial · not affiliated with Tesco · for automating your own account · MIT
Filter and rank a live search by on-pack nutrition (protein ≥ 10g, sugar ≤ 7g), then read any product's full macros and micronutrients. Real data, no login.
Plain catalogue search is a one-liner:basketeer search "oat milk"piped tojq. Real, live, no login.
Tesco has no public API, and the usual approach (scraping the DOM) shatters on the next site redesign, stops at titles and prices, and can't be driven by an AI agent. basketeer talks to Tesco's GraphQL gateway directly:
- Nutritionally aware.Where Tesco lists on-pack nutrition, basketeer normalizes it into typed macrosandstructured micronutrients, free on anonymous reads, and lets you filter andranka search by them (searchByNutrition). A first-class API, not a scraped afterthought.
- Robust.Pure-HTTP GraphQL, not DOM scraping. A cosmetic site redesign won't break it.
- Complete.Book, amend, cancel, and reorder a delivered shop. The full order lifecycle, not just "add to basket."
- Agent-ready.A stdio MCP server lets Claude or any MCP client run the shop. Read-only and destructive tools are annotated, and checkout never pays.
- Typed and lean.One fully-typed client you import; the CLI and MCP server are built on it. The data path imports no third-party packages (the three runtime deps — commander, the MCP SDK, and zod — are pulled only by the CLI and MCP server). It is purefetchwith no Node-only APIs, so it runs on Node and Node-compatible runtimes.
- Safe.checkout()stops at the payment URL. A human finishes 3-D Secure in a browser, by design.
- Tested.75 tests across the data plane and its parsers.
When Tesco lists a product's on-pack nutrition, basketeer normalizes it into typed macros (energy, protein, fat, saturates, carbs, sugars, fibre, salt) and structured micronutrients (a named entry per vitamin and mineral, with amount, unit, and % of the Nutrient Reference Value). Free, onanonymous reads(nutritionisnullwhen a product has no usable rows). And you can searchand rankby it:
# "high-protein yogurt, >=10g protein, <=7g sugar, ranked by protein" — live, no login basketeer search "high protein yogurt" --min-protein 10 --max-sugar 7 --sort protein
import { Basketeer } from "basketeer"; const client = new Basketeer(); // no auth needed for nutrition reads const { results, hydrated, failed } = await client.searchByNutrition("high protein yogurt", { where: { protein: { min: 10 }, sugars: { max: 7 } }, sort: { by: "protein", dir: "desc" }, }); results[0]?.macros; // { energyKcal, protein, fat, saturates, carbs, sugars, fibre, salt } results[0]?.nutrition?.micros; // [{ name: "Calcium", amount: 120, unit: "mg", nrvPercent: 15 }, ...]
Nutrition-filtered search runs a keyword search, then fetches each candidate's nutrition (one throttled product call each, capped byhydrate, default 20) and filters locally. It filterswithina search; it does not scan the whole catalogue.hydrated/failedreport the exact cost.
[!IMPORTANT]Not affiliated with, endorsed by, or connected to Tesco.This is an unofficial, reverse-engineered client for automatingyour ownaccount, in the spirit of personal interoperability. It can break if Tesco changes their API. Use it for your own shopping, at your own risk, within Tesco's terms. Not for resale, scraping at scale, or operating accounts that aren't yours. SeeEthics & usage.
Catalogue search, product lookup, and nutrition need nothing but the public API key:
import { Basketeer } from "basketeer"; const client = new Basketeer(); const { results } = await client.search("wholemeal bread", { limit: 10 }); const top = results[0]; if (top) { const product = await client.getProduct(top.sku); console.log(product.title, product.price.actual); // => "Tesco Wholemeal Bread 800G" 0.75 } // Fetch up to 15 SKUs in one throttled HTTP request. Duplicates are fetched // once and missing products are omitted. const products = await client.getProducts(["282822189", "275280804"]);
Sign-in sits behind Akamai's bot defenses, so a real browser mints the session once. After that, the data plane is plainfetch; only a token refresh (about once an hour) briefly reopens the browser.
npm install basketeer playwright # playwright is an optional peer dep, only used for sign-in npx playwright install chrome # the Chrome channel sign-in drives (skip if you already have Google Chrome)
The full grocery lifecycle, typed end to end:
- Nutrition— typed macros and structured micros, normalized from a product's on-pack rows when present; filter and rank a search by nutrition (anonymous)
- Catalogue—search,getProduct, batchedgetProducts,browseCategory(anonymous);favourites/ "my usuals" (authed)
- Product images—imageUrlon every product/result;resizeImageUrl(url, { width, height })for thumbnails (anonymous)
- Basket—add,set,remove,get
- Slots— delivery and collection:list/book/release
- Orders—list,amend,cancel,lastFulfilled(reorder)
- Checkout—checkout()returns the payment URL; it never pays
→ Full reference (signatures, return types, the error catalogue, and where the browser runs):docs/api.md
Tesco's website talks to a GraphQL gateway atxapi.tesco.com. basketeer speaks that protocol directly.
Implement your own backend with the two-methodAuthBackend(login,refresh) and three-methodTokenStore(load,save,clear).FileTokenStoreandMemoryTokenStoreship in the box. The full host matrix is indocs/api.md.
Serverless note.A serverless function can't hold a browser, and Tesco's Akamai blocks sign-in fromdatacenterIPs, so a hosted browser needs aresidentialegress. Off-the-shelf managed-browser proxies (Browserbase and similar) are also commonly blocked for supermarket domains. The dependable pattern is a browser on a residential connection you control (a home server, a Pi, the user's device), with the pure-HTTP data plane running anywhere.
const orders = await client.orders.list(); for (const o of orders) console.log(o.orderNo, o.status, o.totalPrice, "amend until", o.amendExpiry); // Amend returns a scoped handle; basket edits apply to THAT order. const amendment = await client.orders.amend(orders[0]!.orderNo); await amendment.remove("258114107"); await amendment.set("292632440", 1); // ...then check out again to commit (pays any difference), or: await amendment.discard(); // leave the order unchanged client.amendingOrderNo; // the order currently open for amendment, or null await client.orders.cancel(orders[0]!.orderNo); // "Reorder my usual shop": const last = await client.orders.lastFulfilled(); for (const it of last?.items ?? []) await client.basket.set(it.productId!, it.quantity, it.unit ?? "pcs"); // Completed orders, newest first, offset-paged (Tesco has no cursor or total). // The result set is LIVE — dedupe by order.id and never persist nextOffset. let page = await client.orders.history(); // { orders, nextOffset } while (page.nextOffset !== null) page = await client.orders.history({ offset: page.nextOffset });
A stdio MCP server ships as thebasketeer-mcpbin, exposing tools (basketeer_search,basketeer_search_by_nutrition,basketeer_nutrition,basketeer_basket_set,basketeer_slots_list,basketeer_orders_list,basketeer_checkout, …) so Claude Desktop or any MCP client can shop. Read-only tools carryreadOnlyHint; mutating ones carrydestructiveHint, andbasketeer_orders_cancel/basketeer_checkouttake a two-step confirm token.basketeer_checkoutreturns the payment URL for the human. There is no "pay" tool. The search tools take an optionalselect— an array of dot-notation paths (e.g.["sku", "title", "price.actual", "promotions.description"]) that trims each result to just those fields, keeping token usage down in agent loops.
// claude_desktop_config.json — run basketeer login once first so it has a session. { "mcpServers": { "basketeer": { "command": "npx", "args": ["-y", "-p", "basketeer", "basketeer-mcp"] } } }
Thebasketeerbin prints JSON to stdout, coded errors to stderr. Install globally for the bare command, or prefix withnpx -p basketeer:
basketeer login # one-time browser sign-in basketeer search "oat milk" --limit 5 basketeer search "high protein yogurt" --min-protein 10 --max-sugar 7 --sort protein basketeer product 254656543 basketeer nutrition 292990463 # normalized macros + micros for a product basketeer favourites basketeer basket add 258114107 1 # increment; basket set <sku> <qty> for exact basketeer slots # --collection for click-and-collect basketeer orders list basketeer checkout # prints the payment URL; you finish in a browser
Runnable scripts inexamples/:lookup.ts(anonymous),login.ts,shop-flow.ts(search → basket → slot → checkout handoff),orders.ts, andbring-your-own-auth.ts.
Everything thrown is aBasketeerErrorsubclass, so you can branch on the type. The common cases:
- ApiKeyError(the public key was rejected).The bundledx-apikeyrotates roughly monthly. Set your own with theTESCO_API_KEYenv var ornew Basketeer({ apiKey }). Not retryable.
- AuthExpiredError(session could not be refreshed).Runbasketeer loginagain. Headless hosts cannot refresh (Akamai blocks headless sign-in), so they hit the ~1h token ceiling and must re-login on a machine with a display.
- RateLimitedError(429/403).The client stops rather than retry-storm. Back off; it already throttles to 1 req/s by default.
- AuthExpiredErroron a401.A single401triggers one transparent browser refresh and retry; a persistent401surfaces asAuthExpiredError.
- "Chrome channel not found" at login.BrowserAuthBackenddrives the system Google Chrome (chromechannel). Install Chrome, or runnpx playwright install chrome. Make sure the optionalplaywrightpeer is installed.
- UK Tesco only.Built against the UK groceries gateway; other regions are untested.
- Pre-release (v0.1).The public API may change between minor versions until 1.0.
- Reverse-engineered.No public contract from Tesco; an operation or the public key can change and break a call until updated.
- Auth needs a real browser on a residential connection.Datacenter IPs are blocked for sign-in; the pure-HTTP data plane runs anywhere.
- Nutrition search is bounded, not catalogue-wide: it filters within a keyword search, capped byhydrate.
- Collection slots need alocationUuidfor the store you collect from.
Personal-account interoperability automation: your account, your data. The client defaults to1 request/second, single concurrency, and stops on429/403. Please keep it that way. Not for resale, bulk scraping, or multi-account operation. This project is not affiliated with Tesco; "Tesco" is a trademark of its owner and is used here only to describe interoperability.
npm install npm test # 75 tests: vitest unit + regression + smoke npm run build # clean build to dist/ npm run example:lookup
PRs welcome. Keep code readable and minimal, add a test for any behaviour change, and never commit a session or API key.
Create, send, e-sign, and track PandaDoc documents from any AI client — search templates, create from templates, send for signature, and check status via OAuth.
Search jobs from employer career pages, build professional PDF resumes with 6 templates, and get AI-powered career advice. Free, no auth required.
Latvian property portal MCP: search rentals, sales & nightly stays, market stats; owner tools via OAuth. Remote server at https://bezbaseina.lv/mcp
Curated MCP server directory with install commands, auth notes, and remote-vs-local classification.
The 1Password MCP server creates a bridge that allows MCP clients such as Codex and Kiro to manage your 1Password Environments with secure authorization prompts.
Agent-native forum for the x402/A2A ecosystem. The hosted MCP server exposes the whole forum as tools — threads, comments, votes, USDC bounties (Coinbase x402 on Base), provider reviews, and search. Endpoint: https://api.achivx.com/mcp/ (HTTP, OAuth 2.1).
Search, audit, and install from 117K+ open-source AI agent skills & MCP servers — every result carries a security grade (SAFE/CAUTION/UNSAFE/UNAUDITED) and quality score checked BEFORE installing. Local cached index, works offline, no auth. Install: npx -y @agentskillshub/mcp
Search and compare 1,113 curated AI tools by pricing, rating, features and alternatives — hosted, no-auth Streamable HTTP.
Browser automation framework with savable auth profiles and compliance options viable to enterprise developments.
BudgetFitter is a free UK deal discovery platform with a public MCP server. Search verified discount codes, look up brand intelligence, and navigate deals — no auth required.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.




