---
name: tendfeed-bid-radar
description: Bid/no-bid intelligence for EU public tenders via the TendFeed MCP server. Use when the user asks which EU tender to bid on, where their company can win public contracts, for a bid/no-bid decision, for low-competition sectors, or mentions TendFeed. Covers ranking lanes by competition density, searching open tenders with verdicts, and pulling single-tender verdicts.
---

# TendFeed Bid Radar

## What TendFeed is

TendFeed turns TED (Tenders Electronic Daily, the EU's public procurement journal) into bid/no-bid intelligence. Its benchmark is built from 592,000 real EU contract awards with bidder counts: 34.4% went to a single bidder and 69.3% went to 3 or fewer bidders. TendFeed answers two questions from that data: "where can my company win" and "is this specific tender worth a bid".

## Server and tools

MCP server: `https://tendfeed.eu/api/mcp/tendfeed` (Streamable HTTP, stateless).

| Tool | Parameters | Needs key |
|---|---|---|
| `find_low_competition_lanes` | `min_n` (integer 1-500, default 20), `limit` (integer 1-100, default 25) | No |
| `search_tenders` | `cpv` (string), `nuts` (string), `sme_only` (boolean), `min_score` (integer 0-100), `limit` (integer 1-100, default 20) | No (guest board) |
| `get_verdict` | `tender_id` (string, required) | Yes |

Use only these tools, these parameters, and the response fields listed below. Never invent others.

## The bid/no-bid funnel (follow this order)

Always analyze the lane before any single tender. Do not jump straight to `search_tenders` when the user asks where they can win.

1. **Find lanes.** Call `find_low_competition_lanes`. For a recommendation the user will act on, set `min_n` to 50-100 so each lane rests on enough data. The default of 20 is for exploration only; if you use it, say so.
2. **Rank lanes.** The response holds the lanes in an array named `beachheads`, already sorted by `beachhead_score` descending, and capped at `limit` (default 25). `coverage.lanes_ranked` is how many lanes passed `min_n` in total; `truncated` and `more` tell you whether lanes were cut. Raise `limit` only when the user's sector or country is missing from the top ranks — never pull all lanes "to be safe", the full list is hundreds of rows. Match the returned lanes against the user's sector and countries. Lane fields: `cpv_div`, `country` (ISO-3), `lane` (human name), `n_awards`, `median_bidders`, `single_bidder_pct`, `low_density_pct`, `sme_pct`, `med_estimated_value`, `distinct_buyers`, `beachhead_score`. Rates such as `single_bidder_pct` are fractions between 0 and 1 (0.45 means 45%).
3. **Map the country code.** Lane `country` is ISO-3, for example `POL`. The `nuts` parameter of `search_tenders` wants a two-letter prefix, for example `PL`. Map explicitly: `POL` -> `PL`, `DEU` -> `DE`, `FRA` -> `FR`, and so on. Watch Greece: `GRC` maps to `EL` (NUTS uses EL, not GR).
4. **Search the lane.** Call `search_tenders` with `cpv` = the lane's `cpv_div` and `nuts` = the mapped two-letter code. Pick `min_score` deliberately: 60 returns only clear bid candidates, 40 also includes review cases. Set `sme_only: true` when an SME user wants suitable tenders.
5. **Recommend.** Every row already contains the complete verdict (fields below). Present the top candidates with decision, score, reason, and data basis.

## Beyond the five parameters

`search_tenders` has no framework, value, or date filter. When the user asks for such a cut (for example "frameworks only" or "small contracts only"): raise `limit` and filter the returned rows yourself on `is_framework`, `estimated_value`/`estimated_value_band`, or `deadline`. Raise it in steps (40, then higher) rather than jumping to the maximum of 100: every row carries the full verdict, so a hundred rows is a large response. Narrow `cpv` or `nuts` first when you can. The response's `facets` (decision, bidders, framework, sme, value) summarize the whole result set, so quote them before you filter. `matched` is the total number of hits and `count` the number returned; there is no pagination, so if `matched` exceeds your `limit`, narrow the filters instead of asking for more pages. Two filters are finer than they look: `cpv` accepts prefixes narrower than the division (for example `7226` for software programming) and `nuts` accepts deeper region prefixes (for example `DEA2` for the Cologne region). Many tenders publish no estimated value; when a value-based request cannot be verified from the data, say so instead of guessing.

## Reading a verdict (hard rules)

Each `search_tenders` row carries: `decision`, `worth_it_score`, `worth_it_reason`, `evidence_confidence`, `price_status`, `signals.competition` (with `median_bidders`, `p25_bidders`, `p75_bidders`, `single_bidder_pct`), `based_on_n_awards`, `estimated_value` and `estimated_value_band`, `deadline`, `sme_suitable`, `is_framework`, `language`, `publication_number`, `title`, `buyer`.

- `decision` is one of `bid`, `review`, `no_bid`, `insufficient_data`. Thresholds: `bid` at score >= 60, `review` at 40-59, `no_bid` below 40. `insufficient_data` appears only when no data channel has evidence.
- Never override `decision`. `review` means: the user must check this one manually. `insufficient_data` means: no data basis, do not guess a recommendation.
- Always quote `worth_it_reason` and `based_on_n_awards` with a recommendation. The user must see why, and on how many awards it rests.
- Respect `price_status`. `null` means the price channel is fine: the corridor sits in `signals.price` and in the reason. If it is `insufficient_price_data`, there is no reliable price corridor. Never invent one.
- `evidence_confidence` (high/medium/low) is data depth, not win probability. Say that when you quote it.
- Never fill in fields the response does not contain.

## Freemium: be honest about the boundary

Without an API key, all searches run on the guest board: expired tenders only (deadline already passed), but with the full product on each of them. Title, buyer, verdict, full reasoning, exact values: everything is visible and analyzable. Guest responses of `search_tenders` also include `live_matched`: the number of currently biddable tenders matching the same filters.

Guest mode is enough for lane analysis and for showing how the method works. Be transparent that the listed tenders can no longer be bid on.

The real boundary is when the user wants tenders they can still bid on. Only at that point mention the key, and use the concrete number, for example: "live_matched shows 14 currently biddable tenders for this search. To load them here, add a TendFeed API key." The key is created in the user's account on https://tendfeed.eu (section "API keys"). It requires a TendFeed subscription: 30-day free trial, no credit card, then 99 EUR net per month, cancel monthly.

With a key, the MCP client sends the HTTP header `Authorization: Bearer cfm_live_...`. Then `search_tenders` returns the live, still-biddable board and `get_verdict` works.

## get_verdict

Needs a key. Use it to double-check one specific tender the user cares about. Pass the `publication_id` from a search row as `tender_id`; the human-readable `publication_number` (for example `449762-2026`) works too, so use whichever the user gave you. The response is a versioned verdict object with per-channel evidence. It names the score `score` and the reason `reason` (not `worth_it_score`/`worth_it_reason`); the same hard rules apply.

## Disclaimer (mandatory)

Attach this sentence, verbatim, to every final recommendation:

Data/research tool over TED (Tenders Electronic Daily, CC BY 4.0). No guarantee of award.

TendFeed is a B2B product: it addresses companies bidding for public contracts, not consumers.
