{
  "markdown": "# ihalent\n\n**Turkish public-tender award intelligence: who won, for how much, at what discount, against how many bidders.**\n\n[![CI](https://github.com/gulmezeren2-byte/ihalent/actions/workflows/ci.yml/badge.svg)](https://github.com/gulmezeren2-byte/ihalent/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/ihalent)](https://pypi.org/project/ihalent/)\n[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](pyproject.toml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n\n> Built on top of **[saidsurucu/ihale-mcp](https://github.com/saidsurucu/ihale-mcp)** — ihale-mcp reaches EKAP; ihalent structures and analyzes what it returns.\n\n<!-- mcp-name: io.github.gulmezeren2-byte/ihalent -->\n\nEvery public tender in Turkey ends with a *Sonuç İlanı* — a result notice that states the\nestimate, the contract price, the winner, and how many firms bid. The notices are public.\nThey are also one at a time, unstructured, and impossible to reason across: you cannot ask\nEKAP \"what has this company won in the last two years\" or \"how far below estimate does this\nauthority actually award.\" ihalent turns those notices into structured records and answers\nexactly those questions — firm histories, discount (kırım) distributions, and competition\nmetrics — with every number traceable back to the notice it came from.\n\n![ihalent overview, winner concentration and procurement red flags over nine real awards from the December-2025 / January-2026 window](docs/demo.svg)\n\nThat is real output over nine real construction awards from the December-2025 / January-2026 window (in\n[`examples/`](examples/)). The `-2.96%` at the bottom of the range is not a bug: Istanbul\nUniversity awarded a 615-million-lira campus job at **2.96% above** its own estimate,\nthrough an emergency \"pazarlık\" procedure — a contract signed over the public estimate,\nwhich is precisely the kind of thing that should be easy to see and currently is not.\n\n## Why this exists\n\nI'm an industrial engineer and I work in construction. When our firm weighs a public\ntender, the questions that decide the bid are all about other people's history: how far\nbelow estimate does this authority award, how many firms usually show up, what has a given\ncompetitor been winning and at what price. All of that is public — it is sitting in tens of\nthousands of result notices on EKAP — and none of it is queryable. So everyone rebuilds a\nprivate, partial version of it by hand, in spreadsheets, badly.\n\nThere is already an excellent tool for *reaching* this data:\n[saidsurucu/ihale-mcp](https://github.com/saidsurucu/ihale-mcp) solved authenticated EKAP\naccess and hands you the notices. ihalent is the layer above it — the one that turns a pile\nof notices into an answer. It does not re-scrape EKAP; it structures what you have collected\nand does the analytics that don't exist yet.\n\n## What it answers\n\n**A firm's history** — every award across the dataset, folded across spelling variants:\n\n```\n$ ihalent firm examples/sample-awards.jsonl \"ÖZDEN YEL\"\nÖZDEN YEL\n  wins: 1   joint ventures: 1   total contract value: 19.709.997,40 TL\nDiscount (kırım) — ÖZDEN YEL         \n┌────────┬────────┬────────┬────────┐\n│ mean   │ median │ min    │ max    │\n├────────┼────────┼────────┼────────┤\n│ 33.87% │ 33.87% │ 33.87% │ 33.87% │\n└────────┴────────┴────────┴────────┘\nbased on all 1 awards.\n  top awarding authorities:\n      1x  DSİ 14. Bölge Müdürlüğü\n```\n\n**Where the discounts are** — grouped and sorted, highest first:\n\n```\n$ ihalent discounts examples/sample-awards.jsonl --by authority\nMean discount by authority                                     \n┌──────────────────────────────────┬────────┬────────┬────────┐\n│ authority                        │   mean │ median │ awards │\n├──────────────────────────────────┼────────┼────────┼────────┤\n│ DSİ 14. Bölge Müdürlüğü          │ 33.87% │ 33.87% │    1/1 │\n│ Ziraat Bankası Genel Müdürlüğü   │ 31.41% │ 31.41% │    1/1 │\n│ İstanbul YİKOB                   │ 26.98% │ 26.98% │    1/1 │\n│ İSKİ Genel Müdürlüğü             │ 26.66% │ 26.66% │    1/1 │\n│ İTÜ Rektörlüğü                   │ 22.68% │ 22.68% │    1/1 │\n│ Esenler Belediyesi               │ 21.99% │ 21.99% │    1/1 │\n│ Ağrı İl Özel İdaresi             │ 19.99% │ 19.99% │    1/1 │\n│ Kağıthane Belediyesi             │ 17.41% │ 17.41% │    1/1 │\n│ İstanbul Üniversitesi-Cerrahpaşa │ -2.96% │ -2.96% │    1/1 │\n└──────────────────────────────────┴────────┴────────┴────────┘\nawards column: used / considered (excluded lack an estimate).\n```\n\n**Competition** — how many valid bids show up, and how often exactly one does (a single-bid\naward is a flag procurement watchdogs care about). It's in the overview above.\n\n## The one rule\n\n**A number is never shown without the ground it stands on.** A mean discount always comes\nwith \"over how many awards, and how many were dropped for a missing estimate.\" A firm's\ntotal contract value says so when some of its wins had no published price, so you read it as\na floor, not the full figure. A discount is `None`, never `0`, when the estimate wasn't\npublished — ihalent does not invent the numbers the government didn't print. Half the value\nof a tool like this is refusing to guess.\n\n## Install\n\n```\npip install ihalent           # add [mcp] for the MCP server: pip install \"ihalent[mcp]\"\n```\n\nOr from source: `pip install git+https://github.com/gulmezeren2-byte/ihalent`.\n\n## The workflow\n\nihalent reads a JSONL file of awards. You produce it by collecting result notices — the\neasy path is ihale-mcp — and letting ihalent structure them:\n\n```\n# 1. collect: with ihale-mcp connected to your agent, save what its\n#    get_tender_announcements returns (one or many tenders) to bundle.json\n\n# 2. structure:\nihalent ingest bundle.json -o awards.jsonl\n\n# 3. ask:\nihalent overview  awards.jsonl\nihalent firm      awards.jsonl \"ACME İNŞAAT\"\nihalent discounts awards.jsonl --by province --min 5\n```\n\nEvery command takes `--json` for pipelines and agents. Or skip collection and try the\nbundled example directly:\n\n```\ngit clone https://github.com/gulmezeren2-byte/ihalent && cd ihalent\npython examples/build_sample.py       # parses the real notices in examples/notices/\nihalent overview examples/sample-awards.jsonl\n```\n\n## Commands\n\n| command | what it does |\n|---|---|\n| `ihalent overview AWARDS` | value, discount, competition and the data gaps of a dataset |\n| `ihalent firm AWARDS \"NAME\"` | one company's wins, total value, discount, and where it wins |\n| `ihalent discounts AWARDS --by X` | mean/median discount grouped by authority, province or tender_type |\n| `ihalent single-bid AWARDS` | awards with a single valid bid — no real competition (a watchdog flag) |\n| `ihalent concentration AWARDS [--authority X]` | winner concentration (HHI) — do the same few firms win everything? |\n| `ihalent flags AWARDS` | per-award red flags: single bid, near-estimate price, no estimate, high drop-off |\n| `ihalent parse NOTICE.md` | one result notice → structured JSON |\n| `ihalent ingest BUNDLE.json` | collected ihale-mcp/EKAP output → awards JSONL |\n\n## Using it with AI agents\n\nThe result notice is unstructured text; the interesting questions are aggregate. That is an\nawkward fit for an agent working notice-by-notice, and a natural fit for a tool: `--json`\noutput with stable fields, an exit code that means something, and a firm-name match that\nfolds spelling variants so an agent doesn't have to.\n\nThere is a native **MCP server** (`pip install 'ihalent[mcp]'`) that exposes the analytics\nas tools — `overview`, `firm`, `discounts`, `concentration`, `flags`, `parse_notice`,\n`ingest_bundle` — over a dataset you point it at:\n\n```\nIHALENT_AWARDS=awards.jsonl ihalent-mcp\n```\n\nNo local Python? The [`Dockerfile`](Dockerfile) builds the same server:\n`docker build -t ihalent . && docker run --rm -i -e IHALENT_AWARDS=/data/awards.jsonl -v \"$PWD:/data:ro\" ihalent`.\n\nPair it with ihale-mcp and an agent can collect notices and reason across them in one\nsession: ihale-mcp fetches, ihalent structures and aggregates. The Python API\n(`ihalent.ingest_bundle`, `ihalent.analytics.firm_profile`) is three calls deep if you'd\nrather script it.\n\n## Scope, honestly\n\n- **This is analytics, not a scraper.** Collection is ihale-mcp's job (it does it well);\n  ihalent deliberately owns the layer above and stays a pure function of the data you give\n  it — no network, no signing keys, nothing that breaks when EKAP rotates a header.\n- **Company-name folding is conservative on purpose.** It merges legal-form suffixes and\n  Turkish spelling variants, and it would rather show two rows for one firm than one row for\n  two — so `firm` reports how many distinct spellings a query matched, and warns you if that\n  is more than one.\n- **The parser tracks one document: the result notice.** Bid-level detail (who else bid, at\n  what price) is not in the notice and so is not here. Discount is the estimate-to-contract\n  gap, which the notice does carry.\n- **Award values are nominal lira, as printed.** Comparing 2023 and 2026 contracts is your\n  analysis to make with the dates in hand; ihalent does not silently inflation-adjust.\n\n## What ihalent is not\n\nIt is not a replacement for [ihale-mcp](https://github.com/saidsurucu/ihale-mcp) — it sits\non top of it. It is not a live dashboard or a paid tender-alert service; it is a library and\na CLI you point at data you control. And it does not redistribute a dataset: it ships the\nhandful of real notices in `examples/` and the code to structure your own.\n\n## A note on the data\n\nPublic-tender results are public information, published by the state for public scrutiny.\nihalent structures what EKAP already discloses. Company names that appear are legal entities\nin their public commercial capacity, not private individuals.\n\n## How this project is built\n\nI designed the model and the analytics and I review every line; I use AI agents (Claude\nCode) heavily for implementation speed, and the commit trailers say so. The contract is the\ntests — 80 of them, built on the exact JSON shapes EKAP and ihale-mcp emit, including a\nresult notice with a negative discount and a cancelled tender. They don't care who typed\nthem.\n\n## Related\n\n- **[acikpoz](https://github.com/gulmezeren2-byte/acikpoz)** — the other half of the Turkish public-procurement picture. `ihalent` reads the tender result notices that say who won and at what discount; `acikpoz` reads the unit-price (birim fiyat) catalogs that say what the work *should* have cost. Same discipline: every figure traceable to its source, nothing invented.\n\nMore tools by [Eren Gülmez](https://github.com/gulmezeren2-byte?tab=repositories).\n\n## License\n\n[MIT](LICENSE) — Mehmet Eren Gülmez\n",
  "bytes": 10704,
  "sha": "bda77ad4d73a8b74d76ae2a95aec64485c99768e46bbd58e302589232e016125",
  "repo_slug": "gulmezeren2-byte/ihalent",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_gulmezeren2_byte_ihalent_ccafa0a3/readme"
}