torob-mcp
by siamak
README.md
# torob-mcp
**An MCP server for [torob.com](https://torob.com), Iran's price-comparison engine.**
Ask your assistant what something costs in Iran, who sells it cheapest, whether the price is good
right now, and whether the shop can be trusted.
[](https://github.com/siamak/torob-mcp/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/torob-mcp)
[](LICENSE)
[فارسی](README.fa.md)
> **Unofficial.** This project is not affiliated with, endorsed by, or connected to Torob in any
> way. It is an independent client for the undocumented JSON endpoints torob.com's own website
> uses. Those can change or stop working without notice. All prices come from Torob and are
> reported as-is.
---
## What it does
Fifteen tools covering search, prices, sellers, price history, physical shops and shop trust
signals. Prices are normalized to **Toman**. Persian, English and Finglish queries all work —
`ayfon 13` finds what you'd expect.
```
You: What's the cheapest iPhone 13 in Iran right now, and is it a good time to buy?
→ search_torob(query: "iPhone 13")
→ product_price_chart(product_id: …) verdict: "great" — at the 25th percentile of 12 weeks
→ product_sellers(product_id: …) cheapest reliable seller, ★5, 300-500 orders in 90 days
```
## Install
Needs **Node 22+**.
### Claude Code
```bash
claude mcp add torob -- npx -y torob-mcp
```
### Claude Desktop
`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS,
`%APPDATA%\Claude\claude_desktop_config.json` on Windows:
```json
{
"mcpServers": {
"torob": {
"command": "npx",
"args": ["-y", "torob-mcp"]
}
}
}
```
### Cursor
`.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` globally:
```json
{
"mcpServers": {
"torob": {
"command": "npx",
"args": ["-y", "torob-mcp"]
}
}
}
```
### Try it without installing
```bash
npx -y torob-mcp --help
npx -y @modelcontextprotocol/inspector npx -y torob-mcp
```
## Tools
| Tool | What it answers |
| --------------------- | --------------------------------------------------------------------------------- |
| `search_torob` | "What does X cost?" — query plus category, brand, price range, condition and sort |
| `torob_suggest` | Turns a vague, misspelled or Finglish query into phrases that work |
| `product_details` | Price range, seller count, specs, category path |
| `product_sellers` | Who sells it, cheapest-reliable-first, with trust signals |
| `product_price_chart` | ~a year of weekly prices, plus a verdict: great / fair / high |
| `product_variants` | Storage, RAM and region siblings with their own prices |
| `similar_products` | Torob's own "similar" list |
| `product_stores` | Physical shops stocking it, filterable by city |
| `shop_profile` | Shop city, score, enamad trust seal, time on Torob |
| `browse_category` | Browse a whole category rather than searching |
| `search_filters` | Discover category ids, brand ids and the price span |
| `compare_products` | 2–5 products side by side, showing only what differs |
| `find_best_value` | "Best X under Y Toman", ranked |
| `get_products_batch` | Cards for up to 10 ids at once |
| `product_url` | A shareable torob.com link (costs no request) |
## Example prompts
- «قیمت گوشی سامسونگ A55 چنده؟» — what does the Samsung A55 cost?
- "Best wireless headphones under 2 million Toman"
- "Is 113 million Toman a good price for a used iPhone 13 Pro right now?"
- "Which shops in Shiraz have this in stock?"
- "Compare the 128GB and 256GB versions"
- "Is this seller trustworthy? They're much cheaper than everyone else."
A good habit: ask it to link the product URL, because prices move constantly.
## Remote mode
```bash
torob-mcp --http # 127.0.0.1:3000
torob-mcp --http --port 8080
torob-mcp --http --host 0.0.0.0 # requires TOROB_AUTH_TOKEN
```
- Binds **loopback by default**.
- A non-loopback bind **requires** `TOROB_AUTH_TOKEN` (16+ chars) unless you pass `--insecure`.
- Origin and Host are validated (DNS-rebinding protection); no wildcard CORS.
- `GET /healthz` makes no upstream call. `POST /mcp` is the transport.
## Configuration
Everything is optional and validated at startup. See [`.env.example`](.env.example).
| Variable | Default | What it does |
| ----------------------- | --------------------------- | -------------------------------------------------------------- |
| `TOROB_LOG_LEVEL` | `info` | Queries are logged at `debug` only, never `info`. stderr only. |
| `TOROB_USER_AGENT` | `torob-mcp/<v> (+repo url)` | Sent on every request. Keep it honest. |
| `TOROB_CONCURRENCY` | `3` | Simultaneous upstream requests (1–4). |
| `TOROB_RATE_PER_SEC` | `4` | Token bucket against Torob. |
| `TOROB_TTL_SEARCH_S` | `300` | Search cache. Product `900`, price chart `21600`. |
| `TOROB_AUTH_TOKEN` | — | Bearer token for HTTP mode. Required on non-local binds. |
| `TOROB_ALLOWED_ORIGINS` | — | Comma-separated. Empty rejects all browser Origins. |
## Deployment
> **⚠️ Torob blocks many cloud egress IPs.** This is the single most likely reason a deployment
> fails. Cloudflare Workers and datacenter ranges (AWS, GCP, Azure, Hetzner, DigitalOcean, …) are
> frequently blocked or served a challenge page instead of JSON. The server reports this as a
> `Blocked` error rather than hanging.
>
> **An Iranian VPS, or a home connection in Iran, is the reliable option.** Test first with
> `pnpm test:live` from the target host.
Full Docker, Iranian VPS, Fly.io and Railway recipes: [`docs/DEPLOY.md`](docs/DEPLOY.md).
Workers egress probe (Phase 5.0 gate): [`docs/WORKERS_EGRESS.md`](docs/WORKERS_EGRESS.md).
Workers deploy guide: [`docs/DEPLOY_WORKERS.md`](docs/DEPLOY_WORKERS.md).
## Security model
Designed on the assumption that it runs on **your** machine and its output lands in an LLM's
context window. Full detail in [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md).
- **Host allowlist.** `torob.com` and `api.torob.com` only, re-checked on **every redirect hop**.
Product ids are validated as UUIDs before a URL is ever built.
- **Prompt-injection hygiene.** Merchant-written titles and notes are third-party text. Control
characters, bidi overrides and zero-width characters (except ZWNJ, meaningful in Persian) are
stripped; fields are truncated. **This reduces the risk, it does not eliminate it** — the real
protection is that this server is entirely read-only.
- **Sponsored placements are labelled** `sponsored: true`, never hidden.
- **Shop profiles are an allowlist.** ~15 of Torob's ~72 shop fields are emitted; the rest
(billing internals, personal contact) are dropped.
- **Supply chain.** Four runtime dependencies, lockfile committed, install scripts disabled, npm
provenance via OIDC, Actions pinned to SHAs, CodeQL + gitleaks + Scorecard + Trivy, SBOM per
release, cosign-signed image.
## What is sent to Torob
Only what a query needs: your **search text** (normalized), the **ids and filters** you asked for,
page and size, our User-Agent, and unavoidably your **IP address**. One exception carries a derived
`deliver_city` header: `product_stores` with a `city` argument.
**No telemetry, no analytics, no disk persistence, no cookie jar.** Full detail in
[`docs/PRIVACY.md`](docs/PRIVACY.md).
## Development
```bash
pnpm install --frozen-lockfile
pnpm check # oxlint + oxfmt + typecheck
pnpm test # offline and deterministic
pnpm test:live # opt-in, hits the real API
pnpm build
pnpm inspect # MCP Inspector, interactive
pnpm inspect:cli # Inspector CLI checks
```
Monorepo: `packages/core` is runtime-agnostic (no `node:*`; everything injected through `Runtime`);
`apps/node` is the published package. See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and
[`CONTRIBUTING.md`](CONTRIBUTING.md).
## Docs
[Wiki](wiki/Home.md) ·
[Index](docs/README.md) ·
[Endpoints](docs/ENDPOINTS.md) ·
[Architecture](docs/ARCHITECTURE.md) ·
[Threat model](docs/THREAT_MODEL.md) ·
[Privacy](docs/PRIVACY.md) ·
[Deploy](docs/DEPLOY.md) ·
[Dependencies](docs/DEPENDENCIES.md) ·
[Security policy](SECURITY.md) ·
[Contributing](CONTRIBUTING.md)
User-facing guides live in [`wiki/`](wiki/Home.md) (Getting started, Tools, Recipes, Deploy).
Deep technical reference stays in [`docs/`](docs/README.md). Publish to the GitHub Wiki tab with
`pnpm wiki:publish` after the first wiki page exists on GitHub.
## License
MIT © Siamak Mokhtari
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues