alza-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@alza-mcpFind me the best pro-grade wheel cleaner under 600 Kč and tell me where I can pick it up in Prague."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
alza-mcp-community
Let your AI agent shop on Alza.cz — Central Europe's largest e-commerce store.
Unofficial, community-built project. Not made, endorsed or supported by Alza.cz a.s.
alza-mcp-community is an unofficial Model Context Protocol server that gives Claude (or any MCP-aware agent) an interface to Alza through browser-based catalog scraping and reverse-engineered mobile/web APIs: search products, pull full detail, read reviews, use anonymous/account data, manage a cart, select delivery/AlzaBox pickup, preview checkout, and submit an order only with an explicit one-time confirmation token.
Ask: "Find me the best pro-grade wheel cleaner under 600 Kč and tell me where I can pick it up in Prague." The agent calls search_products → get_product → find_pickup_points and gives you a real answer with real prices and a real address.
This project isunofficial — not affiliated with, endorsed by, or sponsored by Alza.cz a.s. It's a community wrapper for personal/research use. Read the disclaimer before deploying or sharing widely.
Quick install
Renamed from alza-mcp. At Alza's request, the project is now alza-mcp-community, so it's clear it isn't an official Alza product. If you installed the old package, replace alza-mcp with alza-mcp-community in your MCP config. Your saved login in ~/.alza-mcp/ keeps working.
One-click install
Both buttons install the same thing as the manual config below: npx -y alza-mcp-community, no environment variables, no secrets. With npx/npm, install runs a postinstall step that downloads Playwright's headless Chromium (~92 MB) and sets up the optional curl_cffi venv, so the first install takes a while and later starts take a few seconds. The .mcpb bundle and --ignore-scripts installs skip postinstall: Chromium is then downloaded on the first browser-backed call (~30 s) and the venv is not created (see Install paths). For Claude Desktop, download the one-click alza-mcp-community-<version>.mcpb bundle from the latest release and open it, or use the JSON config below.
Claude Code
claude mcp add alza --scope user -- npx -y alza-mcp-communityThat's it. Restart Claude Code, type /mcp to confirm, and start asking. The install downloads Playwright's headless Chromium browser (~92 MB) via postinstall; if that was skipped, the first browser-backed call downloads it (~30 s).
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"alza": {
"command": "npx",
"args": ["-y", "alza-mcp-community"]
}
}
}Restart the app. Same install-time download.
Cursor / Continue / any MCP client
Same shape — command: "npx", args: ["-y", "alza-mcp-community"]. Stdio transport, standard MCP everywhere.
Related MCP server: MCP Playwright Server
What you can ask
A real session captured during testing:
You: Find me the best pro-grade wheel cleaner on Alza, around 500 Kč. I want a detailer-tier brand.
Claude: (calls
search_products"čistič disků kol" sort=rating, thenget_producton the top picks)Top three from the live catalog:
#
Product
Brand
Volume
Price
★
1
Sonax Xtreme Full Effect
Sonax 🇩🇪
750 ml
559 Kč
4.8
2
Kenotek Wheel Cleaner Ultra
Kenotek 🇧🇪
1 L
455 Kč
4.8
3
Auto Finesse Reactive 1L
Auto Finesse 🇬🇧
1 L
599 Kč
4.4
Pick: Sonax Xtreme Full Effect 750 ml — the de-facto detailer recommendation. It's iron-fallout reactive (turns purple on contact with brake-dust particles so you can watch it work), Sonax is a German workshop standard, and it's in stock.
That's the agent calling four MCP tools across two parallel searches and synthesizing real Alza data. No hallucinated SKUs.
What it does
With 63 domain tools (66 including list_toolsets, set_toolset and report_issue), listing every tool on every tools/list call would front-load an agent's context with dozens of tools it may never touch in a given conversation. So they're grouped into toolsets, and only two are enabled by default, exposing 15 domain tools plus the two toolset controls and report_issue:
Toolset | Enabled by default? | Covers |
| ✅ | Search, product detail, side-by-side comparison, reviews, categories, pickup-point lookup |
| ✅ | OAuth handshake, account status, the mutation-token issuer |
| — | Cart, delivery/pickup selection, checkout, order placement & cancellation |
| — | Profile, contacts, addresses, registration, credential/identity changes |
| — | Order history, payment methods, after-order payments, claims, documents |
| — | Reviews, complaints, AlzaSubscription, attachments, EAN lookup |
| — | Alza's native price-drop / back-in-stock watchdog (list, set, delete) |
| — | Alza's in-app chatbot |
| — | Compatibility-checked PC parts lists: |
| — |
|
Call list_toolsets to see every group and set_toolset({id, enabled: true}) to turn one on before using its tools — e.g. enable basket_and_checkout before adding something to a cart. This is standard MCP progressive disclosure (RegisteredTool.enable()/.disable(), which fires the normal tools/list_changed notification) — no functionality is removed, it's just not all visible at once.
Reporting problems. report_issue is always available, whichever toolsets are on. When a tool fails unexpectedly, returns clearly wrong data, or breaks because Alza changed something, the server instructions and the error message itself point the agent to it. It returns a redacted draft (with version, Node, platform, storefront, transport and this session's recent tool errors), a gh issue list command to check for duplicates, a ready-to-run gh issue create --repo lukabudik/alza-mcp-community … command, and a prefilled new-issue link for agents without a shell. The server files nothing itself: the agent shows the draft to you and files it from your GitHub account only if you agree. Credentials (including cookies and API keys), e-mails, phone numbers, UUIDs, URL query values, account ids and home-directory paths are redacted automatically, and recent errors keep only route and status. Invalid arguments and unknown products don't trigger the hint.
Catalog tools:
Tool | Purpose |
| Keyword search, price/stock/screen-size filters, and bounded client-side sorting (price filters and sorts apply to the top ~72 ranked candidates, |
| Full detail for one product — price, availability, brand, image, URL |
| 2–6 products side by side — one aligned table of price, availability, rating and every spec row; optional |
| Aggregate rating + review count + individual reviews (author, date, rating, body, pros/cons) via the reviews API, newest first; the list may be longer than the aggregate count (it appears to include other storefronts' reviews) |
| Cheaper / better-rated / same-brand alternatives to a product (Alza's own alternatives list, same-category search fallback) |
| Nearest AlzaBox lockers and AlzaShop showrooms by 5-digit postal code, merged by distance, with opening hours, no cart needed. The postal code is geocoded with the public OpenStreetMap Nominatim service ( |
| Category brands ( |
| Discounted products (alza.cz only) with current/original price and discount % computed from observed prices — scans category listing pages for a |
| Top-level categories, or real subcategories when |
| Search-box suggestions over plain HTTP (no page render): phrases, categories, brands and products with ids/codes — refine a messy Czech query before |
| Looks up catalog products by barcode/EAN (the app's camera barcode-scan API, AT3; read-only, no account required; enable |
PC builder tools (enable pc_builder):
Tool | Purpose |
| Checks a parts list (Alza codes). Checks socket, RAM generation/slots, PSU wattage + headroom, GPU length and cooler height/radiator vs case, form factors, and display output. Returns prices, total, stock, and one verdict per rule with the spec values used |
| Proposes a compatible build within a CZK budget from Alza's real component categories (gaming / workstation / office, pinned |
Account and checkout tools:
Tool | Purpose |
| Creates a mobile-API OAuth PKCE authorization URL |
| Exchanges the returned authorization code for mobile-API tokens |
| Reads live OIDC metadata from |
| Reads fixed APK-confirmed catalog, navigation, account, order-history, list, branch, alternative-product, basket, cost-estimate, web after-payment-dialog, web zip-code (WCF |
| Creates a one-time token for a fixed, source-confirmed mutation without sending a request; the token is bound to the action and the exact call arguments ( |
| Executes a validated APK-confirmed low-risk mutation (lists, coupons, basket, country/ISIC, gift, watchdog, feedback, discussion) with that token |
| Checks whether a mobile API access token is loaded |
| Reads the current cart and total |
| Adds a product by Alza code |
| Reads delivery + AlzaBox/pickup options from the APK |
| Despite the name, returns the delivery → payment associations from |
| Previews checkout and returns a one-time confirmation token |
| Cancels an order part using its cancel form, a reason, and a one-time |
| Runs the mobile API order sequence only when supplied the preview token and required API payloads |
| Reads the live web pickup family (AlzaBox/branches/24-7 availability, place list, place detail) for web-checkout delivery selection (read-only) |
| Adds a product to the live web HATEOAS basket ( |
| Reads the live web checkout cart state + item list for a basket id from |
| Reads the live chatbot HATEOAS navigation ( |
| Opens/continues a chatbot session with page context (session-scoped, visitor-keyed; returns |
User-management, payments, orders, and post-purchase tools:
Tool | Purpose |
| Reads the authenticated profile + address book (APK |
| Reads the account contact list |
| Registers a new Alza account (credential-bearing, one-time token) |
| Creates/edits a delivery address through the server-provided address form |
| Deletes a delivery address via its per-address action |
| Zip/city search via the verified |
| Lists payment methods from the APK delivery-payment-group endpoint |
| Lists after-order payment options for an order part |
| Executes an after-order payment (APK |
| Places an order through the live-verified legacy web WCF pipeline (SaveOrder2→3, 113-gate retry, CheckOrder4, SendOrder4; one-time token) — the working submission path while mobile |
| Executes a web after-order payment through the live-verified WCF |
| Reads a user order by numeric |
| Submits a product review through the server-provided review form |
| Lists active or archived warranty claims by |
| Reads the account's subscription section (the navigation's |
| Activates AlzaSubscription (one-time token) |
| Changes the installment plan (one-time token) |
| Uploads image attachments via the multipart server-provided action (one-time token) |
| Searches the account's orders by term (OR6; read-only) |
| Reads the account's archived orders (OR7; read-only; the "Skrýt zrušené" include/hide-cancelled toggle) |
| Downloads an order invoice/document from its server-provided href (OR10; origin-validated to the Alza host family; the body is returned in |
| Reads the GDPR section + export dialog (A17; read-only — where the data export will be sent) |
| Reads one warranty claim's detail from its |
| Changes the account password (A14; one-time token; logs the user out of every device) |
| Enables/disables SMS two-factor (A15; one-time token) |
| Changes the contact phone number (A16; one-time token) |
| Changes the contact email (A16 sibling; one-time token) |
| Deletes the account (A18; one-time token; irreversible — disposable accounts only) |
| Lists the account's Alza watchdogs: price-drop and back-in-stock alerts (B9a; read-only; no email in the output) |
| Sets a watchdog on a product ( |
| Deletes a watchdog by |
High-impact mutations require one-time confirmation tokens (checkout_preview for mobile place_order, prepare_mutation for the other guarded mutations); the full route inventory, exposure decisions, and verification labels live in docs/mobile-endpoint-coverage.md.
OAuth sign-in happens outside the MCP; auth_exchange exchanges the returned code and the server holds access/refresh tokens in memory or loads them from the configured token file. An access token that is expired (or within 60 s of its JWT exp) is refreshed before the next account call, and parallel calls share one refresh. account_status reports expiresAt/expired. When the tokens were loaded from the token file, refreshed tokens are written back to it (atomically, mode 0600), so a restart does not begin with a stale token; tokens from an in-process auth_exchange stay in memory.
"Při přihlášení došlo k chybě." after signing in? The sign-in usually worked. Alza redirects to
alza://identity?code=…&state=…, which a desktop browser can't open, so the page stalls and a 40-second timer in Alza's login page shows that message. Before signing in, open DevTools and turn on Network → Preserve log. After you sign in, copy thealza://identity?code=…URL from the redirect'sLocationheader, or from the Console error about failing to launchalza://. Pass the whole URL toauth_exchangeascode. The code expires quickly, so exchange it right away. Registration and credential-change tools do accept passwords or verification codes as arguments, guarded by one-time confirmation tokens. Account/cart/order tools issue API requests and can fall back to browser-backed requests when challenged; they do not automate checkout forms.
Checkout paths: web_place_order submits the cart populated by add_to_cart, after delivery_options and any cart-scoped web_pickup_places lookup. The web_add_to_cart → web_cart HATEOAS basket is separate. The mobile place_order route was blocked by server-side HTTP 500 in the recorded live tests; the legacy WCF path was live-verified. See known limitations for dated evidence.
Mobile API environment variables:
Env var | Purpose |
| Mobile API base URL, default |
| Optional anonymous visitor UUID; otherwise generated per process |
| OAuth client id used by |
| OAuth redirect URI, default |
|
|
| OAuth authority, default |
| The |
| Same secret for the PKCE exchange scripts ( |
| JSON token store written by |
Plus:
📦 Resource —
alza://product/{code}lets agents read a product as a URI.💬 Prompt —
/find-productis a guided shopping helper.🌍 Multi-locale — catalog locale configuration supports
alza.cz,.sk,.hu,.at,.de,.co.ukviaALZA_BASE_URL; account/checkout verification is for CZ and some routes are fixed to the CZ host.
Configuration
All optional — alza-mcp-community works out of the box.
Env var | Default | Purpose |
|
| Switch locale: |
| unset | Connect to your already-running Chrome via CDP instead of launching a managed Chromium. Reuses the existing browser session; set |
|
| Set |
|
| Close the headless Chromium after this many ms with no tool calls. Lower it on memory-constrained machines; raise it (or disable by setting absurdly high) if you make many calls in quick succession and don't want the relaunch latency. |
| unset | Route Alza traffic through an HTTP(S) or SOCKS5 proxy ( |
|
| Verbose stderr logging (equivalent to |
|
| Minimum stderr log level: |
| enabled | Set |
| auto | Python interpreter that has |
Install paths and the curl_cffi venv
The Chrome-fingerprint sidecar needs a Python venv with curl_cffi (pinned to >=0.16,<0.17, the tested range). What each install path does:
Install path | Chromium headless-shell |
|
| downloaded by postinstall | created by postinstall if |
| skipped (downloaded at runtime when needed) | still created |
| downloaded | skipped |
| downloaded at runtime when needed | not created. For |
This repository's own checkout ( | skipped | skipped; run |
Without the venv the server still works: the account stack falls back to plain fetch and the browser, which Cloudflare challenges more often. Windows: the interpreter lookup also tries .venv-cf\Scripts\python.exe, python and py, and the bash script does not run on plain Windows. Use npx -y alza-mcp-community --setup-cf instead: it is implemented in Node, finds py -3/python/python3, runs python -m venv .venv-cf in the package directory, installs curl_cffi>=0.16,<0.17, verifies the import, prints success or failure and exits 0/1. It is idempotent, removes a venv it half-built if pip fails, honours ALZA_MCP_SKIP_VENV=1, and is never run automatically (the server never installs packages at runtime; when the sidecar is unavailable it only logs a one-line hint to run it). The venv is created inside the package copy that runs the command (for npx, its cache directory, which is replaced when a new version is fetched or the npm cache is cleared; re-run --setup-cf then), so the .mcpb bundle, which has its own directory, does not pick it up: after running it, set ALZA_CF_PYTHON to the interpreter it prints (or to any interpreter that has curl_cffi) in the bundle's environment. The Windows path (Scripts\python.exe, py -3) is unit-tested with mocked processes only and is unresolved on a real Windows machine (not tested live).
Running over HTTP (Streamable HTTP)
stdio is the default and what the install snippets above use. To serve the same server over MCP Streamable HTTP instead, for example for a client that only takes a URL:
npx -y alza-mcp-community --http --port 3000 # or: ALZA_TRANSPORT=http ALZA_HTTP_PORT=3000 npx -y alza-mcp-community
# → MCP endpoint http://127.0.0.1:3000/mcp, health check http://127.0.0.1:3000/healthz
claude mcp add --transport http alza http://127.0.0.1:3000/mcpOn HTTP the server is stricter than on stdio, because a network endpoint can be reached by more than one client:
Catalog only by default. Only the read-only, anonymous toolsets are usable:
catalog(on) andpc_builder(enable withset_toolset). Every other toolset (auth, basket/checkout, account, orders/payments, reviews/subscriptions, chat,advanced_raw) is locked:list_toolsetsshows it with the reason, andset_toolsetrefuses to enable it. These tools sign in to and act on a real Alza account (orders, payments, credentials), so a shared endpoint must not offer them by accident. SetALZA_HTTP_ENABLE_ACCOUNT=1to unlock them.One server per MCP session. Each
Mcp-Session-Idgets its own server instance: its own toolset state, OAuth tokens and one-time confirmation tokens. WithALZA_HTTP_ENABLE_ACCOUNT=1, each session also gets its own browser context and Chrome-fingerprint sidecar, because both keep Alza cookies. Sessions expire after 30 idle minutes.No token file.
ALZA_TOKEN_FILE(~/.alza-mcp/tokens.json) holds one person's login, so HTTP mode does not load it. Each session signs in withauth_start→auth_exchange. For a single-user localhost setup you can setALZA_HTTP_ALLOW_TOKEN_FILE=1(together withALZA_HTTP_ENABLE_ACCOUNT=1); every session then starts signed in as that account, so never do this on a shared host. All sessions start from the same refresh token. Alza may rotate it on refresh (reported on 2026-09-10; a 2026-10-07 run saw the same refresh token come back six times, so treat rotation as possible, not guaranteed), in which case the first session that refreshes invalidates the copy the others hold (they then needauth_start→auth_exchange). Each session writes its refreshed tokens back to the token file, so a restart picks up the newest ones; keep to one active session in this mode.Localhost only by default. It binds
127.0.0.1and rejects requests whoseHostorOriginis not a loopback name (DNS-rebinding protection). There is no built-in authentication or TLS. If you bind elsewhere (--host 0.0.0.0), put it behind a reverse proxy that does both, and setALZA_HTTP_ALLOWED_HOSTS.
Env var / flag | Default | Purpose |
| stdio | Serve over Streamable HTTP |
| Print usage or the version and exit | |
|
| Listen port ( |
|
| Bind address |
| loopback names when bound to loopback, otherwise no check | Comma-separated hostnames accepted in |
| off | Unlock the auth/account/checkout/order/payment toolsets (per-session logins) |
| off | Also load |
|
| Concurrent session cap (HTTP 503 beyond it) |
|
| Close a session after this long without a request (a session holding an open GET/SSE stream is not closed) |
Hosting is not supported yet. A hosted endpoint (Vercel mcp-handler, Fly, Railway, …) is a follow-up. The main obstacle is Cloudflare, not the transport: Alza is behind Cloudflare Bot Management, and both the headless browser and the curl_cffi sidecar get through it from a residential IP (live-verified) but are far more likely to be challenged from a datacenter IP. A hosted instance will probably need a residential proxy (ALZA_PROXY_URL) or a browser-as-a-service (for example Browserbase via ALZA_CDP_URL). The daily canary's GitHub-hosted runs show this: they get Cloudflare's interactive challenge. Other things a host needs: Chromium and Python with curl_cffi in the image, enough memory for one browser per account session, sticky routing (sessions live in one process's memory), and authentication in front of the endpoint. Keep the account toolsets locked on any multi-user host.
Pi agent integration
Register the local build in pi's global MCP config (~/.pi/agent/mcp.json):
{
"alza": {
"command": "node",
"args": ["/absolute/path/to/alza-mcp-community/dist/index.js"]
}
}Run /reload (or mcp connect alza — the gateway respawns the stdio process, so a freshly built dist/ takes effect without /reload). The gateway exposes the tools (the domain tools plus list_toolsets/set_toolset/report_issue; only the catalog and auth toolsets are enabled until you call set_toolset) under the alza_ prefix (alza_search_products, alza_get_product, alza_cart, …). Auth auto-loads from ~/.alza-mcp/tokens.json. Verified in both modes: (a) in-session through the gateway — search/filter/detail, authenticated add-to-cart, bogus-coupon round-trip → server-side err:1 envelope (docs/live-evidence/pi-integration-2026-09-10.md); and (b) headless (pi -p one-shot prompt), where the agent discovers the alza_* tools, calls search_products with the right args, and reports the correct cheapest in-stock product (docs/live-evidence/headless-pi-2026-09-12.json); the 2026-09-13 re-run adds three more headless scenarios — catalog search with price-ascending sort, the authenticated account stack (account_status + cart), and the one-time mutation token flow (prepare_mutation) — all captured in docs/live-evidence/headless-pi-2026-09-13.json.
How it works
Alza has no public consumer API. This project reverse-engineers the Android app's REST surface (route names and DTOs recovered from the APK) and the website's own checkout pipeline. Neither is a supported or documented interface, so any of it can change or break without notice.
Cloudflare Bot Management. Alza sits behind it and returns HTTP 403 to plain HTTP clients. This server gets past it in two ways: a headless Chromium (catalog scraping) and a Chrome-fingerprint HTTP sidecar (scripts/cf-transport.py, using curl_cffi to impersonate Chrome's TLS/HTTP2 fingerprint) for the account/checkout API. That is circumvention of a bot-protection measure, which Alza's terms of use may prohibit. The sidecar and its setup script ship in the npm package, and postinstall tries to set up its curl_cffi venv. It stays optional: without Python or curl_cffi, the account stack (including OAuth sign-in) falls back to plain fetch and, for same-origin API calls, the browser. A request that changes something (POST/PUT/PATCH/DELETE) is only re-sent another way when the sidecar never sent it; if the sidecar fails after sending (timeout, connection error), the tool reports that the outcome is unknown instead of sending the order, payment or account change a second time. See the Disclaimer and SECURITY.md before using it.
Link attribution. Product, category and suggestion links that the catalog tools return (search_products, get_product, compare_products, recommend_alternatives, get_deals, autocomplete, list_categories, pc_build_*, watchdog_list and the alza://product/{code} resource) carry utm_source=alza-mcp-community&utm_medium=mcp, at Alza's request, so Alza can see visits that came through this server. The parameters are only added to Alza storefront links shown to you. The server's own requests to Alza, sign-in, payment, API, PDF and image URLs, and the account and checkout tools' raw responses are not tagged. No other data is added.
No generic arbitrary-route tool is exposed. What is and isn't covered:
Excluded: administrative login routes, telemetry/audit routes, device-token and anonymous-activity routes, and external payment hand-offs (Klarna, Google Pay) plus the quick-order payment family (documented as
blockedin the coverage matrix).Included, behind one-time tokens: order placement and cancellation, account registration, and credential/identity changes (password, 2FA, phone, email, account deletion —
delete_accountis irreversible).Server-driven action URLs are followed only when returned by a confirmed response, through the origin-validated
AppActionExecutor(GET/POST, path allowlist, a per-tool route family and method, a denylist of credential/payment/order and GET-write routes, sensitive-field blocklist, one-time confirmation token); they are not accepted as arbitrary MCP URLs.
The complete 12-family route inventory with method, DTO, prerequisites, side effects, exposure, and verification status is maintained in docs/mobile-endpoint-coverage.md.
Legacy catalog compatibility still uses the original page adapter:
Search navigates
/search.htm?exps=...and scrapes.browsingitemcards.Product detail comes from page JSON-LD.
Reviews use JSON-LD aggregate ratings.
Pickup points combine branch data and geocoding.
Per-process caching remains enabled.
Image, font, and analytics requests are blocked at the route level. Catalog pages still load scripts, and sorted searches may fetch multiple result pages. Typical latencies: search ~2 s, product detail ~5 s warm.
┌────────────────────────────────────────────┐
│ stdio transport (npx alza-mcp-community) │
├────────────────────────────────────────────┤
│ MCP tools / resources / prompts │
│ grouped into toolsets (toolsets.ts) │
├────────────────────────────────────────────┤
│ Domain: catalog · reviews · pickup · │
│ mobile-account (cart/checkout/…) │
├────────────────────────────────────────────┤
│ Infra: │
│ • browser (Playwright, CDP) │
│ • impersonate-transport (curl_cffi │
│ Chrome-fingerprint sidecar) │
│ • mobile-api (APK-derived REST client) │
│ • jsonld (schema.org parser) │
│ • cache (LRU + TTL) │
│ • locale (multi-country) │
└────────────────────────────────────────────┘For deeper architecture notes — including why we don't ship the HTTP/okhttp recipe — see ARCHITECTURE.md.
Development
git clone https://github.com/lukabudik/alza-mcp-community.git
cd alza-mcp-community
npm install # dev checkout: postinstall is skipped on purpose
npm run setup:cf # optional curl_cffi venv (needs bash + python3)
npx playwright install chromium --only-shell # browser for live runs
npm test # unit tests, no network
npm run typecheck
npm run build # → dist/
npm run eval # agent-driven MCP eval harness (6 scenarios) → docs/live-evidence/
npm run validate:api # hits real Alza — runs every tool end-to-end
npm run pentest:app-action # Node AppAction transport comparison
npm run live:endpoint-matrix # bounded read-only APK route matrix; requires ALZA_API_BASE_URL
npm run live:user-journeys # catalog, delivery/cart, and anonymous-account journeys
npm run auth:login:py # PKCE login step 1 (prints browser URL + pending-login.json)
npm run auth:exchange # PKCE login step 2 (Node, needs a CF-friendly egress)
node scripts/e2e-order-payment.browser.mjs exchange "<pasted alza://identity redirect>"
# step 2 via Playwright (works even when Cloudflare challenges plain HTTP)
npm run live:e2e # real order + after-order payment through the MCP tools
# (browser-backed transport; use ALZA_HEADLESS=false to watch it)
npm run live:e2e:node # same journey over plain HTTP (in-memory MCP client); needs a
# non-challenged egress or a fresh ~/.alza-mcp/tokens.json
STOP_BEFORE_ORDER=1 npm run live:e2e # dry run: stop right before order submission
ALLOW_ANON=1 STOP_BEFORE_ORDER=1 npm run live:e2e
# anonymous dry run; ALLOW_ANON alone does not prevent ordering
node dist/index.js # run the server (waits for stdio MCP messages)
node dist/index.js --http # or serve MCP Streamable HTTP on http://127.0.0.1:3000/mcpFurther reading:
ARCHITECTURE.md — why the code looks the way it does (CF, Playwright, hydration strategy)
ROADMAP.md — what's planned next
CONTRIBUTING.md — repo layout and how to add a tool
Roadmap
main already covers catalog, filtering, cart, checkout, order placement/cancellation and account management (see What it does and the known limitations in docs/gap-analysis.md). Next up:
PC builder follow-ups (#15;
pc_buildertoolset shipped) and a hosted HTTP endpoint (follow-up to #16; local--httpmode is shipped)
Priorities live in ROADMAP.md; everything is tracked in issues — good first issue is the place to start.
FAQ
Why the 92 MB Chromium download?
Alza's bot protection can challenge ordinary HTTP requests. The catalog uses headless Chromium to render product pages; the account stack can also use the optional Chrome-fingerprint sidecar, with browser-backed requests as a fallback. Chromium is installed by the postinstall hook or on first launch if needed. See How it works.
How are login and ordering protected?
OAuth sign-in does not pass the Alza password as an MCP tool argument — sign-in happens in the user's browser (OAuth PKCE) and the MCP only exchanges the returned code. Tools that necessarily carry credentials as arguments (
register,change_password,phone_change,email_change) require an explicit one-time token and should only be called with the user's direct instruction.checkout_previewcreates a one-time confirmation token after the cart and delivery choice are reviewed;place_orderrefuses arbitrary tokens.web_place_order(the currently working submission path — mobileplace_orderreturns HTTP 500 server-side) and every other high-impact mutation (cancel_order,pay_after_order,delete_account, …) require a one-time token fromprepare_mutation. Tokens are single-use, bound to one action and to the exact arguments passed aspayload, and expire after 5 minutes. MFA and 3-D Secure remain user-controlled browser interactions.These tools create, change and cancel real orders and accounts. The token is a guard against accidental calls by an agent, not a substitute for the user confirming the action — have your agent show the order summary and ask first.
Can I avoid the Chromium download?
Yes. Set ALZA_CDP_URL to your existing Chrome's debug port:
# launch Chrome with debugging
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222
# tell alza-mcp-community to attach
ALZA_MCP_SKIP_INSTALL=1 ALZA_CDP_URL=http://localhost:9222 npx alza-mcp-communityThe MCP will use your Chrome — no separate download, faster cold starts, and it inherits any Alza cookies you already have.
Will Alza take this down?
It might, and you should assume that's possible. The project has no commercial intent, caches to minimize traffic, and provides a takedown contact path via issues — if Alza requests removal, we'll comply. But it does get past Alza's bot protection (see How it works), so it is not a polite scraper by Alza's standards, and Alza's terms of use may forbid it. Use it for personal automation, not at scale.
How does this compare to rohlik-mcp?
tomaspavlin/rohlik-mcp is the inspiration. Differences:
Rohlik isn't behind a Cloudflare challenge → rohlik-mcp uses plain HTTP. We're forced to a real browser because Alza is.
Alza is a much larger catalog (millions of SKUs vs. a grocery list).
We cover the whole purchase path (cart, checkout, order placement and cancellation) plus account management, not only catalog reads, and group the tools into toolsets so only the catalog and auth toolsets are enabled by default.
We expose MCP resources and prompts in addition to tools.
Disclaimer
alza-mcp-community is not affiliated with, endorsed by, or sponsored by Alza.cz a.s. "Alza", "Alza.cz", and "AlzaBox" are trademarks of their respective owners.
What this software does. It is a reverse-engineered client. Its mobile-API routes and data shapes were recovered from the Alza Android application, and its catalog tools scrape alza.cz pages with a headless browser. To reach Alza's servers it circumvents Cloudflare Bot Management (headless Chromium plus a Chrome-fingerprint HTTP sidecar). The Android app's OAuth client credential, which is embedded in the public APK, is used as the default for the token exchange.
Legal. None of this is a published or supported interface, and Alza's terms of use may prohibit automated access, bot-protection circumvention and reverse engineering. Whether and how you may use this software depends on your jurisdiction and your agreement with Alza. You are solely responsible for that determination. This is not legal advice, and the maintainers make no representation that use of this software is lawful or permitted.
It acts on real accounts and spends real money. The checkout, order, payment, registration and account-deletion tools operate on live Alza accounts. Orders placed are real and binding; cancellation is not guaranteed to succeed. One-time tokens guard against accidental agent calls but are not a substitute for confirming each action yourself. Test only with accounts and orders you are prepared to lose, and never with credentials you are not prepared to expose to your agent's context.
No warranty. Provided "as is" under the MIT license. The maintainers make no guarantees of availability, accuracy, or fitness for any purpose, and are not liable for orders, charges, account lockouts or bans resulting from its use. The upstream interfaces can change without notice, so any tool may stop working. Do not rely on this for commercial decisions.
Security issues — see SECURITY.md. Alza employees or rights holders with concerns: please open an issue or contact the maintainers — we will respond promptly and comply with reasonable removal requests.
License
MIT. See LICENSE.
Contributors
A big thank you to Samuel Seidel, the project's first outside contributor and now a co-maintainer. He built the account, cart, checkout and order tools, toolsets, typed output schemas, category filtering and the live-verified test harness, which together took the project from a 5-tool catalog browser to a full shopping agent (#1, #5).
Thanks also to @jankryh, who tracked down why OAuth login never worked from the npm package and fixed it (#29, #30).
Contributions are welcome — see CONTRIBUTING.md and the open issues.
Acknowledgements
tomaspavlin/rohlik-mcp — direct inspiration; layout patterns we mirror.
topmonks/hlidac-shopu — reference Alza scraper recipe (HTTP + proxies).
microsoft/playwright-mcp — official Playwright MCP, proof that browser-driven MCPs are the right abstraction for many websites.
Model Context Protocol and the TypeScript SDK.
Available Tools
17 toolsaccount_statusCheck mobile API auth statusARead-onlyIdempotent
Report whether a mobile API access token is loaded in this server process. Use as a first check before account-scoped tools (cart, profile, order, add_to_cart), or to diagnose "not authenticated" failures. If no token is loaded, run auth_start, have the user complete the browser sign-in, then auth_exchange. Read-only; no network call.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| visitorId | Yes | |
| apiBaseUrl | Yes | |
| authenticated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, but the description adds critical behavioral context: 'Read-only; no network call.' This goes beyond the annotations by specifying that the tool performs no network request, implying it is a local in-process check. This is additional transparency that helps the agent understand the tool's runtime behavior and side effects. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, each carrying essential information: the primary purpose, the usage context and diagnostic role, and the follow-up workflow plus safety note. It is front-loaded with the core function and avoids any redundant phrasing. Every sentence adds value, making it both concise and well-structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that the tool has no parameters and an output schema exists (so return values are presumably covered elsewhere), the description is fully complete. It explains what the tool does, when to use it, how to handle the negative case (no token), and its non-network nature. No additional information is needed for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema is empty, so there is no parameter semantics to document. The baseline of 4 applies for tools with no parameters; the description does not need to add anything here, and it correctly avoids inventing irrelevant parameter details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Report whether a mobile API access token is loaded in this server process.' It specifies the exact resource (mobile API access token) and the action (report status), and distinguishes itself from sibling tools like auth_start and auth_exchange by being a status check rather than an authentication action. This leaves no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Use as a first check before account-scoped tools (cart, profile, order, add_to_cart), or to diagnose "not authenticated" failures.' It also gives a concrete alternative workflow: 'If no token is loaded, run auth_start, have the user complete the browser sign-in, then auth_exchange.' This directly instructs the agent on the proper usage context and fallback steps, fully satisfying this dimension.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
auth_discoveryRead Alza OAuth discoveryARead-onlyIdempotent
Read the live OpenID Connect discovery document that the Alza mobile app uses (issuer, authorization endpoint, token endpoint). Use only when debugging the OAuth flow or verifying which identity endpoints Alza exposes before calling auth_start. Do not use for everyday shopping — it returns a configuration document, not account data, and changes nothing. Read-only; no credentials are ever sent.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| issuer | No | |
| jwks_uri | No | |
| token_endpoint | No | |
| grant_types_supported | No | |
| authorization_endpoint | No | |
| code_challenge_methods_supported | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and non-destructive behavior, so the bar for the description is lower. The description adds useful behavioral context beyond annotations by stating that the tool "changes nothing," returns a configuration document rather than account data, and that "no credentials are ever sent." This goes beyond the structured hints without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loads the resource and purpose, and every sentence earns its place: what it reads, when to use it, and what it does not do. There is no fluff or repetition of schema data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only tool with an output schema and clear annotations, the description fully covers all the operational context an agent needs: purpose, scope, exclusions, side-effect absence, and credential safety. Nothing required for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool accepts zero parameters ja the input schema is empty, so there are no parameter semantics to document. The baseline for a no-parameter tool is 4, and the description appropriately clarifies what the returned document contains rather than inventing parameter details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: "Read the live OpenID Connect discovery document that the Alza mobile app uses," and it even lists the key fields the document contains. It clearly distinguishes this tool from shopping and OAuth action tools by stating it returns a configuration document, not account data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: "Use only when debugging the OAuth flow or verifying which identity endpoints Alza exposes before calling auth_start." It also explicitly states when not to use it: "Do not use for everyday shopping," which prevents misuse even though no direct alternative is named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
auth_exchangeExchange Alza OAuth codeA
Complete the OAuth 2.0 PKCE sign-in: exchange the authorization code for mobile API tokens and load them into this server. Use immediately after the user finishes the auth_start flow in the browser. Pass the whole alza://identity?code=...&state=... redirect URL as code (state is then read from it), or the bare code plus state — never a password and never a refresh token here. Fails if the state does not match a pending auth_start session (start over from auth_start in that case). Side effect: replaces the token set currently loaded in this process; afterwards account tools such as cart, profile, and order are authenticated.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Authorization code from the `alza://identity` redirect (the `code` query parameter), or the full redirect URL. | |
| state | No | State value returned by `auth_start`; must match the pending PKCE session exactly. Optional when `code` is the full redirect URL. |
Output Schema
| Name | Required | Description |
|---|---|---|
| err | No | |
| msg | No | |
| data | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing a real side effect ('replaces the token set currently loaded in this process'), the failure condition ('fails if the state does not match a pending `auth_start` session'), and the downstream consequence that account tools like `cart`, `profile`, and `order` become authenticated. The annotations only cover the generic write/open-world/idempotency profile, so this added context is substantial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action, then sequencing, then parameter contract, then failure mode, then side effect. Every sentence carries distinct information with no filler despite the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained; the description still covers the prerequisites, failure handling, and post-call state change. Nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantics: `code` may be the full `alza://identity?...` redirect URL (in which case state is parsed from it) or the bare code, and it explicitly forbids passing a password or refresh token. This clarifies the dual-mode contract better than the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Complete the OAuth 2.0 PKCE sign-in: exchange the authorization code for mobile API tokens') and clearly distinguishes itself from the sibling `auth_start` by positioning itself as the step that follows it. An agent can tell this apart from `auth_discovery`/`auth_start` without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('immediately after the user finishes the `auth_start` flow in the browser') and gives exclusions ('never a password and never a refresh token here'). It also names the recovery path when it fails ('start over from `auth_start`'), which is exactly the alternative routing an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
auth_startStart Alza mobile API OAuthA
Start an OAuth 2.0 PKCE sign-in for the Alza mobile API: returns an authorization URL plus a state value. Use when account_status reports no loaded token, or when account tools start failing with authentication errors. Flow: open the returned authorization URL in a browser, sign in to Alza, the app redirects to alza://identity?code=...&state=... — then call auth_exchange with that redirect URL (or its code and this state). Desktop browsers cannot open the alza:// scheme, so the page appears to stall and after ~40 s shows "Při přihlášení došlo k chybě." — that message is a client-side timer, not a failed sign-in. Tell the user to open DevTools before signing in (Network tab with "Preserve log" on) and copy the alza://identity?code=... URL from the redirect's Location header, or from the Console error about failing to launch alza://. The code is short-lived, so exchange it promptly. This call only creates a local PKCE session: the user's credentials never enter the MCP and nothing changes on Alza's side. Do not call it repeatedly for one sign-in — each call supersedes the previous state.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| state | Yes | |
| authorizationUrl | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, destructiveHint=false. The description adds substantial context those flags cannot carry: it creates only a local PKCE session, credentials never enter the MCP, nothing changes on Alza's side, the code is short-lived, and repeated calls invalidate the prior state (explaining the idempotentHint=false in practical terms). It also pre-empts a specific failure mode (the ~40 s 'Při přihlášení došlo k chybě.' client-side timer on desktop). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and trigger are front-loaded, then the flow, then the footgun and the constraint against repeated calls. It is long, but nearly every sentence (DevTools workaround, Location header, short-lived code) is operationally necessary for a browser-based PKCE flow, so the length is largely earned rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, multi-step OAuth flow with a known platform footgun, the description covers trigger, mechanics, hand-off to auth_exchange, failure-signal interpretation, and safety. An output schema exists, yet the description still usefully names the two returned values, and nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the schema has no semantics to add; baseline for an empty schema is 4. The description compensates by explaining the returned state value and how it feeds into auth_exchange (code + state), which is the only 'parameter-like' information an agent needs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Start an OAuth 2.0 PKCE sign-in for the Alza mobile API') and immediately distinguishes itself from the sibling auth tools by naming what it returns ('an authorization URL plus a state value') versus what auth_exchange does. An agent can route among auth_start/auth_discovery/auth_exchange without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger conditions ('Use when account_status reports no loaded token, or when account tools start failing with authentication errors') and names the follow-up tool ('call auth_exchange'). It also states a prohibition ('Do not call it repeatedly for one sign-in — each call supersedes the previous state'), which is a rare and useful when-not-to-use rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
autocompleteAlza search suggestionsARead-onlyIdempotent
Cheap search-box suggestions (plain HTTP, no page render): suggested query phrases plus matching categories, brands and products with ids/codes. Use it to refine a messy Czech query before a full search_products — pass a category id as category_id, or a product code to get_product.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max entries per section (suggested phrases, categories, products, brands, articles). Default 5, max 10. | |
| query | Yes | Partial or full search text, e.g. "čistič kol" or "iphone". Czech diacritics are fine. |
Output Schema
| Name | Required | Description |
|---|---|---|
| query | Yes | |
| brands | Yes | |
| articles | Yes | |
| products | Yes | |
| categories | Yes | |
| suggestions | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/openWorld/idempotent, so the safety profile is covered; the description adds genuinely new behavioral context by noting it is 'cheap' via 'plain HTTP, no page render' and by summarizing the response shape. It stops short of stating rate limits or latency expectations, so it falls just under full marks.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences: the first front-loads the cost/behavior differentiator, the second the usage and chaining. No filler, no repetition of schema content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations covering the safety profile and an output schema covering return values, the description only needs purpose, cost behavior, and routing — all present. Nothing required to call or chain this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both `query` (Czech diacritics supported) and `limit` (default 5, max 10) are already documented in the schema. The description adds no syntax or format detail beyond that, so the baseline 3 applies; the `category_id` mention refers to another tool's parameter, not this one.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (search-box suggestions) and enumerates the returned content (phrases, categories, brands, products with ids/codes), which cleanly separates it from the full `search_products` sibling. An agent can tell instantly that this is a lightweight suggestion endpoint, not a product search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('refine a messy Czech query before a full `search_products`') and names the downstream alternatives, including how to feed results forward (category id into `category_id`, product code into `get_product`). Routing to the correct sibling is fully specified.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
compare_productsCompare products side by sideARead-onlyIdempotent
Fetch 2–6 products by their Alza codes (the code from search_products) and return one aligned comparison table: a column per product, rows for price, availability and rating, then every spec row present in any of them (exact spec-name matching; a product missing a row shows —). Use instead of calling get_product repeatedly when the user asks which of several candidates is better. A code that fails to load is reported in its own column (ok: false, error) while the others still compare. Pages load at most two at a time, so 6 products take roughly 15–30 s on a cold cache. Optional summarize: true adds a short verdict generated by your client's LLM via MCP sampling, grounded only in the table (skipped, not an error, when sampling is unsupported). Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| codes | Yes | 2–6 Alza product codes to compare side by side, e.g. ['WEXOA002B0', 'JA190b1']. These are the `code` values from `search_products` (not numeric ids). Duplicates are collapsed. | |
| summarize | No | Opt-in: also ask your own client LLM (MCP sampling) for a short verdict grounded only in the table. Ignored with `summary.status: "unavailable"` when the client does not support sampling — the table is always returned. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| summary | No | |
| products | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/openWorld/idempotent annotations by disclosing partial-failure semantics ('A code that fails to load is reported in its own column (`ok: false`, `error`) while the others still compare'), performance characteristics ('Pages load at most two at a time, so 6 products take roughly 15–30 s on a cold cache'), and the sampling-dependent behavior of `summarize` (skipped, not an error, when unsupported).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and output shape, then failure handling, timing, and the optional summarize flag. Dense but nearly every clause carries distinct information; slightly long, though nothing is clearly redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even though an output schema exists, the description explains the table's structure, partial-failure columns, latency, and the sampling caveat. Combined with the schema and annotations, an agent has everything needed to invoke and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: codes are Alza codes from `search_products` rather than numeric ids, duplicates are collapsed, and `summarize` triggers a client-LLM verdict grounded only in the table with defined unavailability semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch 2–6 products ... return one aligned comparison table') and describes the exact output shape (column per product, rows for price/availability/rating/specs). It clearly distinguishes itself from the sibling get_product by framing the result as a side-by-side table rather than per-product lookups.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative and the condition that selects it: 'Use instead of calling `get_product` repeatedly when the user asks which of several candidates is better.' The 2–6 code constraint and the `code`-from-`search_products` sourcing are also stated, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_pickup_pointsFind AlzaBox lockers and Alza showroomsARead-onlyIdempotent
Find AlzaBox parcel lockers and AlzaShop showrooms near a Czech/Slovak postal code: name, address, GPS, distance and opening hours, sorted nearest first. Use when the user asks where the nearest AlzaBox or Alza store is, or where they could pick up an order. No cart or login needed. Lockers come from Alza's public locker map and are cached; each has a parcelShopId. Locker hours vary (many are nonstop, mall lockers follow mall hours) and are looked up for the first 10 lockers returned. Important: a standalone locker list can't tell whether a given product fits. Alza excludes large items (observed: 34"+ monitors) from the whole AlzaBox network and routes them to a few oversized-item pickup points. To check a specific product, add it to the cart and read delivery_options (or web_pickup_places, which is cart-scoped). Read-only. Example: find_pickup_points({postal_code: '500 02', types: ['alzabox'], limit: 5})
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of pickup points to return. Default 10. | |
| types | No | Restrict to specific pickup-point types. 'alzabox' = self-service AlzaBox parcel lockers (live list from Alza's public locker map, no cart needed). 'branch' = brick-and-mortar AlzaShop showrooms with staff. Default: both, merged and sorted by distance. | |
| radius_km | No | Search radius in kilometres. Default 15 km. | |
| postal_code | Yes | Czech (or other supported country) postal code. Examples: '110 00', '11000', '602 00'. Spaces are tolerated. |
Output Schema
| Name | Required | Description |
|---|---|---|
| points | Yes | |
| warnings | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover read-only/idempotent/open-world, and the description goes well beyond them: no cart or login required, lockers come from a cached public map, hours are only resolved for the first 10 results, and the critical caveat that large items are excluded from the entire AlzaBox network. The `parcelShopId` identifier and the oversized-item routing behavior are non-obvious traits an agent could not infer.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, scope and sort order, then trigger conditions, then caveats. Dense and mostly waste-free, though the large-item/AlzaBox-network digression is lengthy relative to the core listing function and could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, yet the description still notes key output fields (name, address, GPS, distance, opening hours, parcelShopId). Caching, auth requirements, and the product-fit limitation are all disclosed, so an agent has everything needed to call and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description earns above baseline by tying `limit` to observable behavior (hours looked up only for the first 10 lockers) and supplying a concrete call example with postal_code/types/limit. It doesn't add much beyond the schema for radius_km or the enum values, which are already documented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Find') and resource ('AlzaBox parcel lockers and AlzaShop showrooms'), plus the geographic scope (Czech/Slovak postal code) and the returned fields. This is clearly distinguishable from siblings like search_products, get_product, or the auth_* tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the trigger ('when the user asks where the nearest AlzaBox or Alza store is, or where they could pick up an order') and routes the agent elsewhere for a related but different need ('to check a specific product, add it to the cart and read delivery_options or web_pickup_places'). When-to-use and when-not-to-use are both present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_dealsFind discounted Alza productsARead-onlyIdempotent
Find currently discounted products on Alza.cz, with current price, original price, savings and a discount % COMPUTED from the observed prices (never from marketing badges). Alza has no 'sale' facet or product-grid sale page, so this scans category listing pages (up to 3 pages ≈ 72 cards for a given category_id; page 1 of five popular categories when omitted) for cards showing a crossed-out original price or an 'Ušetříte' savings amount — it covers a bounded sample, not Alza's whole sale inventory (candidatesScanned says how many cards were checked). Czech store only (alza.cz price-box wording); errors on other locales. Prices are the shelf price, not code/AlzaPlus+ coupon prices. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum deals to return, best discount first. Default 20, max 50. | |
| category_id | No | Leaf category id to scan for discounts (from `list_categories`/`search_products`; top-level hub categories such as 'Počítače a notebooky' have no product grid and return nothing). Omit to scan the first page of a fixed set of popular categories (phones, notebooks, monitors, TVs, headphones). | |
| min_discount_percent | No | Keep only products whose computed discount is at least this many percent. Default 0 (any discount). |
Output Schema
| Name | Required | Description |
|---|---|---|
| deals | Yes | |
| total | Yes | |
| categoryIds | Yes | |
| candidatesScanned | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/openWorld/idempotent annotations: discloses the bounded sample size (up to 3 pages ≈ 72 cards), the candidatesScanned signal, Czech-only store with errors on other locales, shelf price vs coupon price, and that discount is computed rather than read from badges. This is exactly the behavioral context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose in the first sentence, then adds limitations. It is dense and relies on long parenthetical clauses, but nearly every clause carries useful constraint information, so little is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and the description fully covers the non-obvious constraints (bounded coverage, locale restriction, price type). An agent can call this correctly and interpret the result without further context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so limit, category_id and min_discount_percent are already documented with defaults and bounds. The description reiterates category_id behavior (top-level hubs return nothing, popular-category fallback) without adding syntax beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Find currently discounted products on Alza.cz') and enumerates the returned data (current price, original price, savings, computed discount %). It is clearly distinguishable from search_products and get_product, which do not target discounts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the operative context (no 'sale' facet exists, so it scans category listings) and how to drive it (pass a category_id from list_categories/search_products, or omit it to scan popular categories). It never explicitly routes to a sibling alternative, but the discounted-only scope makes the boundary clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_productGet product detailsARead-onlyIdempotent
Fetch details for a single product by its Alza code (the code from search_products, e.g. 'WEXOA002B0' — not the numeric id): name, price (with the original price when discounted), availability, rating, brand, category, primary image, URL, and the spec table when the product page carries one (up to 30 rows, merged from both the DOM spec table and the JSON-LD additionalProperty list some page templates use instead — fixed 2026-09-27 after a product with only the latter returned no params at all). Use after search_products to compare shortlisted candidates in depth, and to get the canonical URL to show the user. For reviews use get_product_reviews; for the complete spec sheet (parameterGroups) use mobile_read with operation=router_product and product_id = the numeric d######## id from the product URL. Sourced from the product page's JSON-LD schema, so values are accurate and stable. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Alza product code, e.g. 'WEXOA002B0'. This is the canonical identifier returned by `search_products` (the `code` field). Not the numeric id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| product | Yes | Scraped product detail (JSON-LD sourced; stable fields listed, rest passthrough). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, so the safety profile is covered; the description adds genuinely useful behavioral detail about the return payload (JSON-LD sourced, spec table capped at 30 rows, merged from DOM and JSON-LD additionalProperty). It does not mention rate limits, error behavior, or auth requirements, so it falls short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and information-dense, but it carries dead weight: the parenthetical changelog ('fixed 2026-09-27 after a product with only the latter returned no params at all') is release-note noise that does not help an agent decide or invoke, and the enumerated return fields duplicate the output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, yet the description still lists them — harmless redundancy. Routing, identifier semantics, and alternatives are all present; only edge behavior (errors on unknown code, missing spec table handling) is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents `code` including the 'not the numeric id' warning and the same example. The description largely restates that, so it adds little semantic value beyond the schema — the baseline 3 for a fully documented single parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Fetch details for a single product') and pins the identifier precisely (Alza code from search_products, not the numeric id), with a concrete example. It also names the siblings it is not (search_products, get_product_reviews, mobile_read), so an agent can disambiguate without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: 'Use after search_products to compare shortlisted candidates in depth', plus two named alternatives with the exact condition selecting each — get_product_reviews for reviews and mobile_read with operation=router_product for the full parameterGroups spec sheet. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_product_reviewsGet product reviewsARead-onlyIdempotent
Fetch reviews for a single product by its Alza code: the aggregate rating and review count plus up to limit individual reviews (author as Alza displays it, date, rating, body, pros/cons) from Alza's reviews API. Use after get_product when the user wants real-world feedback before deciding. If the reviews API is unavailable you receive the aggregate only (empty reviews array) — in that case rely on the rating/count. Do not use for the aggregate rating alone when you already have it from search_products/get_product. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Alza product code, e.g. 'WEXOA002B0'. Same as the `code` from `search_products`. | |
| limit | No | Maximum number of individual reviews to include. Default 10, max 50. |
Output Schema
| Name | Required | Description |
|---|---|---|
| code | Yes | |
| reviews | Yes | |
| reviewCount | No | |
| ratingAverage | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotentHint, openWorldHint), yet the description adds a real degradation behavior: if the reviews API is unavailable the caller gets the aggregate only with an empty reviews array and should fall back to rating/count. That is exactly the kind of operational context annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then usage, then the failure mode, then the read-only note. Dense and information-rich, though the single long opening sentence packs in more return-shape detail than strictly necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read tool with a full output schema and complete annotation coverage, the description supplies everything needed: identity of the product code, the limit behavior, the sibling routing, and the degraded-response path. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both `code` and `limit` (including the default 10 / max 50). The description reinforces the limit semantics but adds no syntax or format detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (fetch reviews for a single product by Alza code) and names exactly what is returned: aggregate rating/count plus individual reviews. It is clearly separable from siblings like get_product and search_products, which it references by name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger ('use after get_product when the user wants real-world feedback before deciding') and an explicit exclusion ('do not use for the aggregate rating alone when you already have it from search_products/get_product'). The agent knows both when and when not to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_categoriesList Alza categoriesARead-onlyIdempotent
Browse the Alza category tree one level at a time. Useful for narrowing a product search — find the right category id, then pass it to search_products as category_id. Without arguments, returns top-level categories.
| Name | Required | Description | Default |
|---|---|---|---|
| parent_id | No | Parent category id. Omit to list top-level categories. Pass an id from a previous result to drill down. |
Output Schema
| Name | Required | Description |
|---|---|---|
| categories | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, openWorld, and idempotent behavior. The description adds meaningful traversal semantics: one level at a time, top-level without arguments, and drill-down by passing a parent id. This goes beyond the annotations and makes the interaction model clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences carry all essential information with no wasted words. The primary purpose and default behavior are front-loaded, and the linkage to search_products is stated efficiently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-parameter read-only tool with an output schema, the description is complete. It explains what the tool does, how to navigate the tree, what happens without arguments, and how results should be used downstream.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully documents parent_id with 100% coverage, including its optionality and drill-down behavior. The description reinforces the same concept but does not add new parameter-level detail beyond what the schema already provides, so the baseline score applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Browse the Alza category tree one level at a time.' It clearly distinguishes this tool from product search and other siblings by explaining that it returns category ids for use with search_products. The no-arguments top-level behavior is also specified, removing ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear usage context: use it to narrow a product search by finding a category id, then pass that id to search_products. It does not explicitly say when not to use it or list alternative tools, but the context is strong enough for typical routing decisions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_category_filtersList a category's filtersARead-onlyIdempotent
List the brands and attribute filters (facets) Alza defines for a category, with real ids and product counts. The attribute set differs per category (laptops have CPU/RAM facets, monitors have panel/resolution facets, …). Pass brands[].valueId to search_products as producer_ids (brand filtering works in every category), and filterable: true groups' param_id/value_id pairs as filters. Not every filterable facet is honoured by Alza — when one isn't, search_products returns an error rather than unfiltered results; drop that filter and compare candidates with get_product's params instead. Slider groups (filterMode: "range" — screen size, refresh rate, brightness, weight, port counts, …) filter by {param_id, min?, max?} in search_products's filters, with min/max in the facet's own units as given by values[].value (e.g. millimetres for a monitor diagonal, inches for a TV diagonal; live-verified 2026-10-06). filterable: false groups are informational only. Call this before using producer_ids/filters — never guess ids. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| category_id | Yes | Category id to read attribute filters for (from `list_categories` or `search_products`'s results). |
Output Schema
| Name | Required | Description |
|---|---|---|
| brands | Yes | |
| groups | Yes | |
| category_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover read-only/idempotent/open-world; the description adds substantial behavior: that not every filterable facet is honoured and search_products errors rather than silently returning unfiltered results, that slider groups filter by {param_id, min?, max?} in the facet's own units, and that filterable:false groups are informational only. This is exactly the beyond-annotations context the dimension rewards.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose and every sentence carries operational information (mapping to search_products, error behavior, slider semantics). It is a single dense run-on paragraph with heavy parentheticals, so structure could be tighter, but there is little waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a facet-listing tool with an output schema (so return shape needn't be re-explained), the description covers purpose, ordering relative to search_products, edge-case error handling, and unit conventions. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter (category_id) and schema description coverage is 100%, so the schema already carries the parameter definition. The description adds no syntax or format detail for category_id itself, matching the baseline-3 rule for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the brands and attribute filters (facets) Alza defines for a category') and immediately scopes the output ('real ids and product counts'). It is clearly distinguishable from siblings like search_products and get_product, which it names as consumers of its output.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Call this before using producer_ids/filters — never guess ids,' giving the trigger condition, and provides a fallback path when a facet is rejected ('drop that filter and compare candidates with get_product's params instead'). This is when-to-use, when-not, and named alternatives in one place.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_toolsetsList available toolsetsARead-onlyIdempotent
List every toolset this server groups its tools into: id, title, description, member tool names, and whether it's currently enabled. Only catalog and auth are enabled by default to keep the visible tool list small — use set_toolset to turn on the group a task actually needs (e.g. basket_and_checkout before placing an order). Call this first if you're unsure which toolset covers what you need. Read-only, no network call.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| toolsets | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint, so the 'Read-only, no network call' sentence is partly redundant. However, the description adds genuinely non-structured behavior: only `catalog` and `auth` are enabled by default and the visible tool list is intentionally gated, which explains why other tools may be missing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with what is returned, then the default-enabled constraint, then the routing advice. Each sentence carries distinct, actionable information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values needn't be explained, yet the description still summarizes them briefly. Combined with the default-enablement disclosure and the set_toolset routing, an agent has everything needed to call this correctly and act on the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is no parameter semantics to document and the baseline is 4. The description correctly spends no effort on arguments and instead covers return fields and server state.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List every toolset this server groups its tools into') and enumerates the returned fields (id, title, description, member tool names, enabled flag). It is unambiguously distinct from sibling set_toolset, which it names directly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent when to call it ('Call this first if you're unsure which toolset covers what you need') and routes to the alternative ('use set_toolset to turn on the group a task actually needs'), even giving a concrete example (basket_and_checkout before placing an order).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prepare_mutationPrepare an Alza mutationARead-only
Start a two-step mutation by returning a one-time confirmation token bound to exactly one action. This call itself sends nothing to Alza. Use it before the high-impact typed mutations — register (action register), address_upsert (address_create or address_edit), address_delete (address_delete), pay_after_order (after_order_payment), web_place_order (web_place_order), web_pay_after_order (web_after_order_payment), cancel_order (cancel_order), review_submit (review_submit), subscription_activate (subscription_activate), subscription_update_installment (subscription_update_installment), upload_attachment (attachment_upload), watchdog_set (watchdog_set), watchdog_delete (watchdog_delete) — and before any low-risk mutate_list action (create, rename, delete, add, remove, move, set_country, set_isic, add_gift, add_order_service, send_feedback, submit_discussion, rate_discussion, coupon_add, coupon_remove, basket_update, basket_unlock, gdpr_export). Pass the returned token as confirmation_token on the matching call; the token is single-use and only valid for the exact action you prepared. Do not use for read-only tools, and not for add_to_cart (which is a low-risk cart write that needs no token).
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Which mutation you are about to perform; the token will only be accepted by that action's tool. |
Output Schema
| Name | Required | Description |
|---|---|---|
| action | Yes | |
| confirmationToken | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=false, and the description is consistent with them ('sends nothing to Alza'). It goes further by disclosing token semantics not captured anywhere else: single-use, bound to the exact prepared action, and passed back as confirmation_token on the matching call. No contradiction with any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose, side-effect disclaimer and token-flow instructions are front-loaded in the first sentences, and the exclusions close the paragraph. The long enumeration of low-risk mutate_list actions largely restates enum values already present in the schema, which is the one place the text does not fully earn its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value documentation is unnecessary, and the description covers purpose, side effects, token lifecycle and exclusions well. The only completeness gap is the five unlisted enum actions, which an agent could reasonably wonder about when deciding whether to call this first.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum already lists every valid action, so the baseline is 3. The description adds real meaning by pairing actions with the tool that will consume the token, but it omits five enum values (change_password, two_factor_set, phone_change, email_change, delete_account), leaving it unclear whether those actions also require preparation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise verb+resource ('Start a two-step mutation by returning a one-time confirmation token bound to exactly one action') and immediately clarifies scope with 'This call itself sends nothing to Alza.' It is unmistakably distinct from the read siblings (search_products, get_product) and from the mutation tools it fronts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance (before every high-impact typed mutation and every low-risk mutate_list action) plus two explicit when-not cases: read-only tools and add_to_cart. The action-to-tool mapping removes nearly all inference from routing decisions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recommend_alternativesRecommend alternative productsARead-onlyIdempotent
Find alternatives to one product ("something like this but cheaper / better / same brand"). Candidate pool = Alza's own alternatives list for the product (mobile API, keyed by the numeric commodity id taken from the product URL); if that list is empty, or nothing in it survives the mode filter, falls back to a same-category search_products (poolSource says which was used). Heuristics: cheaper = strictly lower price, cheapest first; same-brand = brand match (source brand vs. the candidate's name), then rating desc, price asc; better-specs = rating >= source rating (unrated dropped), then rating desc, price asc — it ranks by customer rating, it does not compare spec tables, so shortlist then compare with get_product params. The source product is always excluded. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Alza product code of the product to find alternatives for, e.g. 'RI054b5'. Same as the `code` from `search_products`. | |
| mode | No | Ranking mode. 'cheaper': strictly lower price than the source, cheapest first. 'same-brand': same brand as the source, best rating first then lower price. 'better-specs': rated at least as high as the source (unrated excluded), best rating first then lower price — a rating heuristic, NOT a spec-table comparison. Omit to get Alza's own alternatives order. | |
| limit | No | Maximum number of alternatives to return. Default 5, max 20. |
Output Schema
| Name | Required | Description |
|---|---|---|
| mode | No | |
| source | Yes | Scraped product detail (JSON-LD sourced; stable fields listed, rest passthrough). |
| poolSource | Yes | |
| alternatives | Yes | |
| candidatesConsidered | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotentHint, openWorldHint), but the description goes well beyond: it discloses the candidate pool source, the fallback mechanism, the `poolSource` output field, deterministic ranking rules per mode, and the guarantee that the source product is always excluded.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose and the pool/fallback logic before the heuristics, and every clause carries information. It is dense to the point of being a single run-on paragraph, which slightly hurts scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with an output schema and full annotation coverage, the description supplies everything an agent needs: what the pool is, how fallback works, how each mode ranks, and the exclusion rule. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents `code`, `mode`, and `limit` in detail. The description adds marginal value by clarifying that the underlying pool is keyed by a numeric commodity id taken from the product URL while the parameter is the Alza code, but this is largely restatement of enum semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Find alternatives to one product') and immediately scopes it with the user-intent framing ('something like this but cheaper / better / same brand'). It is clearly distinguishable from siblings like search_products, compare_products, and get_product.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative paths explicitly: falls back to same-category search_products when the Alza list is empty, and instructs the agent to shortlist here then compare with get_product `params`. The mode-selection conditions ('cheaper' / 'same-brand' / 'better-specs') are each defined with their trigger.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_productsSearch Alza productsARead-onlyIdempotent
Search the Alza.cz catalog by keyword. Use this for product discovery — finding what's available, comparing options, or starting research. Returns a list with product code, name, price, stock (from the card's purchase CTA), and rating. To get full details for one product, follow up with get_product. Sorting: Alza's search page ignores server-side sort, so price-asc / price-desc / rating scan up to ~72 top-ranked candidates (3 pages) and sort them client-side — candidatesScanned reports how many were scanned; for an absolute price floor also pass max_price. in_stock: true keeps only products with a live purchase CTA. Brand/attribute filtering: call list_category_filters({category_id}) for real ids, then pass producer_ids and/or filters with category_id. This switches to Alza's own filtered category page, so query is ignored and results match what the website shows; a filter Alza doesn't honour returns an error rather than unfiltered results. Slider facets (screen size, refresh rate, brightness, weight, …) filter by {param_id, min?, max?} range; the applied ranges come back in appliedRanges. min_screen_inches/max_screen_inches use the category's real diagonal slider when category_id is given, and a product-name size heuristic otherwise. For attributes Alza has no facet for, compare shortlisted candidates with get_product's params. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-indexed result page (follows Alza's rendered pagination). Only pages Alza actually renders are reachable — a page beyond the rendered set returns no results rather than repeating page 1. Paginating this way does not change which page the sweep scans (sorting still scans from page 1). | |
| sort | No | Sort order, default 'relevance'. Alza's search page ignores server-side sort parameters, so price (asc/desc) and rating sorts gather candidates from up to 3 result pages (~72 items — the top of Alza's relevance ranking) and sort them client-side; the response's `candidatesScanned` says how many candidates were scanned. 'newest' is best-effort (Alza's newest-sort is client-side JS, so it returns relevance order). For an absolute price floor, also pass `max_price` and/or narrow `category_id`. | |
| limit | No | Maximum number of results to return. Default 20, max 50. | |
| query | Yes | Search keywords. Required. Example: 'iPhone 15 Pro', 'PlayStation 5', 'gaming mouse Logitech'. | |
| filters | No | Attribute filter selections from `list_category_filters` (`filterable: true` groups only; never guess ids). Requires `category_id`. Combines with `producer_ids`. Two forms: `{param_id, value_id}` for Checkbox groups (`filterMode: "value"`), and `{param_id, min?, max?}` for Slider groups (`filterMode: "range"` — screen size, refresh rate, brightness, weight, port counts, …; at least one bound; units are the group's `values[].value`; live-verified 2026-10-06). The ranges Alza actually applied come back in `appliedRanges`. If Alza doesn't honour a filter, the call errors instead of returning unfiltered results. | |
| in_stock | No | If true, keep only products purchasable right now (card shows a 'Do košíku'/'Vybrat variantu' purchase CTA; cards with a 'Hlídat' watch button are excluded). Derived from the search card's CTA — for real delivery dates use `get_product`. | |
| max_price | No | Maximum price in the locale's currency. | |
| min_price | No | Minimum price in the locale's currency. | |
| category_id | No | Restrict the search to a specific category id. Use list_categories to discover ids. | |
| producer_ids | No | Filter by brand id(s) — get real ids from `list_category_filters`'s `brands`. Requires `category_id`. Works in every category. | |
| max_screen_inches | No | Maximum screen diagonal in inches. See `min_screen_inches` for when this is a server-side filter vs a name-based fallback. | |
| min_screen_inches | No | Minimum screen diagonal in inches, for display products (monitors, TVs, laptops). With `category_id` (and a category that has a diagonal slider) this is Alza's own server-side diagonal filter (live-verified 2026-10-06), snapped to the slider's real sizes and reported in `appliedRanges`; like `filters`, that switches to the category-browse page, so `query` is not applied. Without `category_id` it falls back to parsing the product name's leading size (e.g. '40" MSI MAG401QR' → 40), excluding products whose name doesn't start with a size. |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| query | Yes | |
| total | Yes | |
| pageSize | Yes | |
| products | Yes | |
| appliedRanges | No | |
| candidatesScanned | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnly/openWorld/idempotent), yet the description goes well beyond them: Alza ignores server-side sort so price/rating sorts scan ~72 candidates client-side, unhonoured filters error rather than silently returning unfiltered results, `in_stock` is derived from the card CTA (excluding 'Hlídat'), and `min_screen_inches` is a server-side slider only with `category_id`, otherwise a name heuristic. These are exactly the non-obvious behaviors an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose, return shape, and the `get_product` handoff are front-loaded before the dense caveat block, so the critical routing information comes first. It is long and repeats several parameter-level details already present in the schema (sort client-side behavior, screen-inch fallback), which costs some efficiency but not clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter, quirk-heavy tool with an output schema, the description covers everything an agent must know to call it correctly: required `query`, when filters override it, what `candidatesScanned`/`appliedRanges` mean, and which filters are real versus name-based heuristics. Return values are only summarized, appropriately, since the output schema carries them.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema cannot: filters/screen-size switching to Alza's own category page and thereby nullifying `query`, and the interaction between `sort`, `candidatesScanned`, and `max_price` for a price floor. Some text (sort, screen inches) duplicates the schema descriptions, which caps it below 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Search the Alza.cz catalog by keyword') and immediately scopes intent to product discovery. It explicitly routes to the sibling `get_product` for single-item detail, so an agent can separate it from `compare_products`, `get_deals`, and `list_categories` without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('finding what's available, comparing options, or starting research'), a named follow-up path (`get_product`), and prerequisites for the filtered mode (`list_category_filters` first, then `producer_ids`/`filters` with `category_id`). It even states a when-not condition: `query` is ignored once a filter switches to the category-browse page.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_toolsetEnable or disable a toolsetAIdempotent
Enable or disable every tool in one toolset (or all for every toolset) at once, so only the tools relevant to the current task are visible. Call list_toolsets first to see the available ids. Changing this fires the standard MCP tools-list-changed notification. No Alza-side effect — this only changes which tools this MCP server currently exposes to you.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Toolset id from `list_toolsets`, or "all". | |
| enabled | Yes | true to enable, false to disable. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| enabled | Yes | |
| tools_affected | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it discloses the tools-list-changed notification side effect, clarifies there is no Alza-side mutation, and explains the blast radius (affects which tools this MCP server exposes to the agent). This is exactly the context an agent needs before a non-read-only toggle.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the operation and scope front-loaded, followed by prerequisite and side-effect notes. No filler and nothing repeated from the title or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and the description covers the two things an agent must know: the prerequisite lookup and the notification/side-effect behavior. Nothing material is missing for a two-parameter toggle.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, including the enum values and the 'all' sentinel, so the schema already carries the parameter meaning. The description echoes the 'all' option but adds no syntax or format detail beyond it, matching the baseline for fully documented schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (enable/disable) and resource (every tool in a toolset, or all toolsets) with the exact scope of the operation. It is immediately distinguishable from the sibling list_toolsets, which is only referenced as a prerequisite.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear usage motive (make only task-relevant tools visible) and an explicit prerequisite (call list_toolsets first to get valid ids). It does not spell out when *not* to use it or an alternative route, so it falls just short of the top band.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
17 tool updates
v0.4.0- First observed
account_status - First observed
auth_discovery - First observed
auth_exchange - First observed
auth_start - First observed
autocomplete - First observed
compare_products - First observed
find_pickup_points - First observed
get_deals - First observed
get_product - First observed
get_product_reviews - First observed
list_categories - First observed
list_category_filters - First observed
list_toolsets - First observed
prepare_mutation - First observed
recommend_alternatives - First observed
search_products - First observed
set_toolset
TDQS
Scored across 17 tools
Every tool targets a distinct purpose: search/discovery (search_products, autocomplete, list_categories, list_category_filters, get_deals), product detail (get_product, compare_products, get_product_reviews, recommend_alternatives), fulfillment (find_pickup_points), auth (auth_discovery, auth_start, auth_exchange, account_status), mutation gating (prepare_mutation), and meta toolset management (list_toolsets, set_toolset). The descriptions explicitly cross-reference each other (e.g. use compare_products instead of repeated get_product) so boundaries are clear.
Predominantly consistent verb_noun snake_case (search_products, get_product, list_categories, compare_products, prepare_mutation). Minor deviations: find_pickup_points vs list_* for exploration, and auth_* / account_status / list_toolsets shift the noun order slightly. Still highly predictable.
17 tools is borderline-heavy but defensible given the breadth (catalog, auth, cart/order, mutations, meta). The toolset-grouping mechanism exists precisely because the flat list is too large by default, which signals the count is on the high side.
Catalog-side coverage is strong (search, autocomplete, detail, compare, reviews, alternatives, deals, filters, categories, pickup). However the descriptions reference many tools that aren't visible in this set (cart, profile, order, add_to_cart, mutate_list, register, review_submit, watchdogs), so within the stated surface there are referenced-but-not-shown operations—a minor completeness gap for the catalog domain, largely mitigated by read-only coherent coverage.
Maintenance
Related MCP Connectors
AI-agent product catalog: search, lookup & purchase routing over verified merchant data.
Web search, browser automation, scraping, crawling and CAPTCHA solving for AI agents.
Independent directory of agentic AI tools — search, compare & recommend via MCP. Read-only.
Search ~8.5M products from 2,500+ Central European e-shops. Semantic, keyword, GTIN lookup.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to automate web browsers through Playwright, providing capabilities for navigation, content extraction, form filling, screenshot capture, and JavaScript execution. Supports multiple browser engines with comprehensive error handling and security features.1-
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to control web browsers through Playwright automation, providing 50+ tools for navigation, interaction, testing, accessibility audits, and visual testing across Chromium, Firefox, and WebKit.17 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI models to find the best online deals by browsing and interacting with multiple shopping platforms like Amazon and eBay across various regions. It uses Playwright to automate searches and retrieve product information from compatible e-commerce and deal-tracking websites.11 npm3MIT
- AlicenseAqualityCmaintenanceEnables AI agents to search for products, manage shopping carts, and place grocery orders on Instacart using browser automation. It includes comprehensive tools for store discovery, product searching, and secure checkout with explicit user confirmation.1149 npm10MIT