MCP server for Torob: search, compare and price-check across shops. Read-only, no API key. 14 tools, hosted on Cloudflare Workers.
概览
Search from any MCP client (Cline, Cursor, Claude Desktop...): , every seller's offer on one product, price history, Torob's own product guides, shop profiles, today's deals. - and answers measured in kilobytes instead of the ~70KB search page they came from. https://torob-mcp.mmdju3.workers.dev/mcp (Streamable HTTP, stateless) - opening the bare address in a browser shows the site. GET /mcp gives a browser the connect page and every other client a 405 - the answer the MCP transport asks for, so no client reads the page as a stream that opened and ended. It runs on Cloudflare, and Torob challenges some addresses outright, so it is and . For real use, run the main version on your own machine (below): no limit of ours, and its calls come from your own connection. See Status. Any MCP client, . Cline / Cursor / Claude Desktop (mcp.json style): No client at hand?
README
Torob MCP - Price comparison intelligence for AI agents
Real Torob knowledge for AI agents. Search Iran’s price-comparison engine from any MCP client (Cline, Cursor, Claude Desktop…): prices in Toman, every seller’s offer on one product, price history, Torob’s own product guides, shop profiles, today’s deals. Read-only, no API key, no login - and answers measured in kilobytes instead of the ~70KB search page they came from.
Live endpoint: https://torob-mcp.mmdju3.workers.dev/mcp (Streamable HTTP, stateless) - opening the bare address in a browser shows the site. GET /mcp gives a browser the connect page and every other client a 405 - the answer the MCP transport asks for, so no client reads the page as a stream that opened and ended.
[!IMPORTANT] The hosted copy is not the main version - it is a quick-test demo. It runs on Cloudflare, and Torob challenges some addresses outright, so it is rate limited (20
/mcpcalls a minute per client IP) and paused from time to time. For real use, run the main version on your own machine (below): no limit of ours, and its calls come from your own connection. See Status.
نسخه فارسی · Examples · Tool reference · Changelog
Connect in 30 seconds
Any MCP client, one URL. Cline / Cursor / Claude Desktop (mcp.json style):
{
"mcpServers": {
"torob": { "url": "https://torob-mcp.mmdju3.workers.dev/mcp" }
}
}
Then just talk: “ارزونترین آیفون ۱۳ کجاست؟”, “هدفون زیر ۱۰ میلیون”, “این گوشی رو کجا بخرم بهتره؟”, “چی تخفیف خورده؟”.
No client at hand? The whole protocol is one POST - a real call:
curl -sS https://torob-mcp.mmdju3.workers.dev/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_products","arguments":{"query":"هدفون بی سیم","limit":2}}}'
And the answer that comes back inside that one message, trimmed to its bones:
{
"query": "هدفون بی سیم",
"total_matches": 412,
"total_matches_note": "Torob's own count is approximate - it changes between identical requests - so page with has_next_page rather than trusting the number.",
"price_range_toman": { "min": 98000, "max": 2500000 },
"products": [
{
"prk": "prk-a1b2c3d4-0000-4000-8000-000000000001",
"name_fa": "هدفون بیسیم مدل P47",
"price_toman": 238970,
"available": true,
"shop_name": "فروشگاه لوازم دیجیتال",
"url": "https://torob.com/p/prk-a1b2c3d4-0000-4000-8000-000000000001/",
"details_url": "https://api.torob.com/v4/base-product/details/?search_id=s1&prk=prk-a1b2c3d4-0000-4000-8000-000000000001"
},
{
"prk": "prk-a1b2c3d4-0000-4000-8000-000000000002",
"name_fa": "هدفون بلوتوثی مدل P9",
"price_toman": 410000,
"available": true,
"shop_name": "پخش عمومی صوت",
"url": "https://torob.com/p/prk-a1b2c3d4-0000-4000-8000-000000000002/"
}
],
"attribution": "Data comes from Torob's public web API. Prices, stock and seller offers change constantly - always confirm on torob.com before buying."
}
Agents running in a browser work too - the endpoint answers CORS preflights (OPTIONS /mcp).
Or run it on your own machine
The same fifteen tools as a local process - no endpoint of ours, no rate limit of ours. What a run learns goes in ~/.torob-mcp/state.json: the product names and links it found, and the wall it is waiting out. A restart keeps both; deleting that file starts you over.
One line - the first run is slower because npm builds it, and git must be installed, since one dependency (fa-text-utils) is fetched from a git repository:
{
"mcpServers": {
"torob": { "command": "npx", "args": ["-y", "github:mmdju/torob-mcp"] }
}
}
Or from a clone, if you would rather run code you can read:
git clone https://github.com/mmdju/torob-mcp.git
cd torob-mcp && npm install # the prepare script builds dist/
{
"mcpServers": {
"torob": { "command": "node", "args": ["/path/to/torob-mcp/dist/index.js"] }
}
}
15 tools, grouped by the question
Find it
| Tool | What it answers |
|---|---|
torob_suggest |
Vague wording to the search terms Torob itself suggests |
search_products |
“Show me X”, price checks - filters, sorting, paging, price window |
search_by_image |
“What is this?” - products matched to a picture link, no upload |
browse_categories |
Walk Torob’s category tree, with each category’s product count |
list_locations |
Province and city ids, plus the cities shoppers pick most |
Know one product
| Tool | What it answers |
|---|---|
product_details |
One product plus every seller’s offer - online and in person - with the spec tables and the full price window |
product_guide |
“Should I buy this?” - Torob’s own write-up of one model: what it is, its strengths and weaknesses, what buyers said |
price_history |
“Is now a good time to buy?” - Torob’s own price chart, month by month, and when it last moved |
Decide
| Tool | What it answers |
|---|---|
similar_products |
“That one is too expensive, what else?” |
compare_products |
“Which of these?” - only what actually differs, plus the price spread |
find_best_value |
“Best X under Y Toman” - ranked by what your budget actually reaches |
Shops and sellers
| Tool | What it answers |
|---|---|
shop_profile |
“Is this seller any good?” - Torob’s own notes, seal, score, delivery terms, and the shop’s catalogue |
find_shops |
Find a shop by name or city, when the user names a store rather than a product |
What is hot
| Tool | What it answers |
|---|---|
torob_trends |
What shoppers are searching right now, each with a sample product |
special_offers |
Today’s featured deals, kept separate from any product’s seller list |
Every tool is read-only (readOnlyHint: true) and needs no credentials. Nothing here can order, message or contact a shop.
Using it well
- All prices are in Toman (1 Toman = 10 Rial). Prices, stock and shop grades move constantly - always link the product URL so the user can confirm before buying.
- A search card is one price - the cheapest offer.
product_detailsis the call that lists every seller, and itsprice_spread_tomanis the whole reason a price-comparison source exists. It also returns the shops that sell in person, with each shelf price’s age - so say how old it is. - A seller’s
urlis the shop’s page on torob.com. The buy button’s link is a trackedapi.torob.comredirect that travels separately asbuy_url- keepurlin the answer, reach forbuy_urlonly when the user is about to click through. price_historyis the honesty check on a price. Compare today’s cheapest offer with what Torob charts; the series labels are Torob’s own, so quote them rather than inventing a trend.price_toman: nullmeans not available, never 0 - and 0 is never free: Torob’s own “not for sale” comes back asavailable: false.price_unreliable: trueis Torob saying the price cannot be trusted: pass the warning on, do not present it as a bargain.- A shop grade needs its vote count.
shop_score: 5withshop_votes: 0is normal and means “no votes yet”, not “five-star shop”. - An empty result is not proof a product does not exist. The response carries
query_noteand Torob’s ownsuggested_queries- retry with one of those instead of telling the user it is unavailable. - An unknown filter slug or value is refused with the real ones (
available_filterscarries every group’s accepted values). Torob ignores what it does not know and answers unfiltered, which is exactly why this server refuses first. - Torob answers a client that calls too fast with a bot challenge instead of data. The server reports it plainly, never solves or evades it, and holds the rest of a burst for the whole cooldown. Details in SECURITY.md.
- Results are capped (default 10, each tool’s own maximum in docs/tools.md) to protect agent context, and Persian wording is folded (Arabic yeh/kaf, mixed digits, ZWNJ) when names are compared - the query reaches Torob exactly as typed.
- examples/sample-calls.md has eleven copy-paste flows, docs/tools.md every parameter and filter slug, and docs/card.d.ts the response types.
How it works
How a question becomes an answer. No user data is stored anywhere in this path.
flowchart LR
subgraph you [Your machine]
agent[AI agentCline / Cursor / Claude]
end
subgraph cf [Cloudflare Workers]
worker[torob-mcpstateless, no database]
end
tr[(Torob public web APIapi.torob.com)]
agent -->|POST /mcpStreamable HTTP, no key| worker
worker -->|HTTPS + polite pacingreads only| tr
tr -->|large JSON, seller lists| worker
worker -->|small recordstoman, offers, URL| agent
- Stateless. Every request stands alone - no sessions, no accounts, nothing to log in to.
- Read-only. All 15 tools carry
readOnlyHint. Nothing can change, delete or order anything. - Projected, not passed through. A Torob search page is roughly 70KB of ranking metadata and experiment plumbing; every tool returns a compact record instead, with the seller list as a first-class
offers[]array. - No user data. What the server does keep: a short-lived response cache and a small map of product ids it handed out.
- Rate-aware by necessity. Upstream calls run one at a time with a 1.5s gap, and a challenge closes a gate the whole server shares instead of opening a retry storm.
- Undocumented upstream. Torob’s API can change without notice, which is exactly why the verify script exists.
Trust, verified
Don’t take my word for it - check the live server yourself:
node scripts/verify-live.mjs # needs Node.js 18+, nothing to install
It drives the real endpoint the way an MCP client does and compares the version the live service reports against the newest release here, so a deployment that lags these docs cannot stay quiet. The same script runs hourly in CI (); a bot challenge is reported without failing the run, because it is upstream’s answer to a fast caller rather than a broken deploy. The unit suite - the badge that means the code is healthy - runs on every push (
). Full path, including why a product id is not an address upstream: docs/architecture.md. Copy-paste client: examples/python.py.
Run it yourself
npm install # two runtime dependencies: the MCP SDK and a small Persian text helper
npm test # builds, then runs every test in the repo
npm run dev # the same Worker the live service runs, on your machine
npm run probe # re-checks every upstream endpoint this server reads
Nothing to configure: no account, no key, no database, no bindings. npm run build && npx wrangler deploy puts your own copy on your own Cloudflare account.
Status
There are two versions, and the one to use is the one on your machine.
- The main version is this repository, run by you:
npx -y github:mmdju/torob-mcp, or a clone plusnode dist/index.js. It has no limit of ours, and its calls come from your own connection. - A quick-test copy is hosted for a first look at
https://torob-mcp.mmdju3.workers.dev/mcp- free, read-only, keyless, at most 20/mcpcalls a minute per client IP (HTTP 429 withretry-afterpast that), and Torob pauses it from time to time. Both stops say so in their own text and name the main version, so nobody is left thinking the tools are broken.
Why it gets paused: Torob answers some addresses with a challenge page instead of data, and what it judges is the address, not what a client sends (measured 2026-10-09: every request shape answered from one connection, while a Cloudflare Worker drew the challenge page on its third call of the minute - a Worker’s subrequests carry Cloudflare’s Cf-Worker header, which cannot be removed). An address can also be blocked outright with nothing wrong at all (measured 2026-10-12: one request answered in full, the next 40 seconds later challenged, still challenged after five idle minutes) - so some addresses are simply refused, and no header or setting here fixes that. If your own copy keeps being challenged, the only thing you can change is where the calls come from: run it locally, or point TOROB_API_BASE at a relay you control (every upstream call, and every details_url it hands out, goes through that base) - worth trying, not a guarantee. The measurements are in the changelog, the behaviour in SECURITY.md.
Data source
Torob’s public web API (undocumented, may change without notice). This project is not affiliated with or endorsed by Torob.
License
MIT - see LICENSE. Security notes in SECURITY.md. Persian version in README_FA.md.
If this is useful, a star helps other builders find it.
安装
npx -y github:mmdju/torob-mcp配置
{
"mcpServers": {
"torob": { "url": "https://torob-mcp.mmdju3.workers.dev/mcp" }
}
}