SuperAPI AI Directory

public-tenders

General & other

View repository

Updated 10-16 09:53

About this skill

Public Tenders (Polytender MCP)

Polytender aggregates public procurement notices from government portals (TED/EU, ~30 German state and federal platforms, Austria, Switzerland/simap, UK Contracts Finder + Find a Tender, US SAM.gov), deduplicates them across sources, and exposes them through a remote MCP server:

https://api.polytender.com/mcp      (Streamable HTTP, auth required)

Every call is authenticated (OAuth or a personal pt_… API key). There is no anonymous access.

1. Pick the access path

A. Native MCP tools (preferred). If tools like search_tenders / semantic_search from a polytender server are already available, use them directly and skip to section 2.

If not, tell the user how to connect (they need an account; keys live at https://polytender.com/app/api-keys):

Client Setup
Claude Code claude mcp add --transport http polytender https://api.polytender.com/mcp --header "Authorization: Bearer pt_…"
claude.ai / Claude Desktop Settings → Connectors → Add custom connector → URL above (one-click OAuth, or use the "Connect to Claude" button on https://polytender.com/app/connector)
ChatGPT Developer mode → create app with the URL above, auth = OAuth (guided wizard on https://polytender.com/app/connector)
Cursor / VS Code / Copilot HTTP MCP entry with url above and header Authorization: Bearer pt_… (VS Code can also do OAuth if you omit the header)
Codex CLI [mcp_servers.polytender] with url = "https://api.polytender.com/mcp" and bearer_token_env_var = "POLYTENDER_API_KEY" in ~/.codex/config.toml

B. Bundled script (any harness, e.g. pi). Same tools, called over plain HTTP:

export POLYTENDER_API_KEY=pt_…            # or put the key in ~/.config/polytender/api-key

scripts/polytender.sh tools                         # list the tools this key can use
scripts/polytender.sh schema search_tenders         # description + input schema of one tool
scripts/polytender.sh call <tool> '<json-args>'     # call a tool, prints the JSON result
echo '{"query":"…"}' | scripts/polytender.sh call semantic_search -   # args from stdin

Paths are relative to this skill directory. --raw prints the full JSON-RPC response, POLYTENDER_MCP_URL overrides the endpoint (e.g. http://localhost:4000/mcp for local dev).

Exit codes: 0 ok · 1 the tool returned an error (message on stderr, relay it) · 2 network/protocol error · 3 missing or rejected API key · 4 rate limited · 127 missing curl/jq.

The server is the source of truth for tools. The tool list depends on the credential (org-scoped tools need an API key bound to an organization or OAuth with mcp:org) and on the plan (an expired trial leaves only get_subscription_status, get_pricing, get_changelog). Run tools / schema when unsure instead of guessing parameters.

2. Choose the right tool

User intent Tool
Conceptual / fuzzy topic, any language ("drone inspection of wind turbines") semantic_search (open tenders only)
Structured filters: country, region, buyer, CPV, deadline, value, stage, title keyword search_tenders
Valid country / source codes, corpus size get_facets (never guess codes)
CPV code for a sector ("cleaning services", "4521") lookup_cpv
Everything about one notice (buyer contacts, value, dates, awards, siblings) get_tender (include: ["documents","lifecycle"] as needed)
The actual tender documents (specs, bill of quantities, forms) get_tender_documents, then read_tender_document
"Who wins these contracts?", "has company X won public contracts?" lookup_company, and search_tenders with stage: "award"
All lots of one procurement get_lot_family (org) or search_tenders with lotGroup
"Which tenders fit my company?" match_company (org; no args = the saved company profile)
Coverage / freshness questions list_sources

Good default for an open-ended request: run semantic_search for recall AND a search_tenders call with status: "open" plus the obvious filters, then merge by id.

3. Rules that avoid wrong answers

  • Country codes are mixed ISO-2/ISO-3 by source (most are ISO-3: DEU, AUT, FRA). Call get_facets once and pass the codes it returns. For all of Germany use country, not a NUTS regions filter (German NUTS coverage is only ~73%).
  • Regions: NUTS prefixes at any depth (DE2 Bayern, DE21 Oberbayern, AT13 Wien) or US state codes (USCA). They are OR-ed with country.
  • query only matches the title. Use keywords (up to 12 terms/phrases, OR-ed) to search title + description, and semantic_search for meaning. Titles are in the notice language, so add the local-language term too (["cleaning", "Reinigung", "nettoyage"]).
  • Open vs closed: search_tenders defaults to stage: "tender" (biddable calls). Add status: "open" (deadline in the future) or "soon" (closes within 14 days). Award notices need stage: "award".
  • Lots are not duplicates. Results with the same lotGroupId are separate lots of one procurement, each bid and awarded separately. Say so instead of collapsing them. Results are already deduplicated across portals.
  • Values are in the notice currency (mostly EUR) and often missing; never invent one.
  • Text stays in its original language. Translate or summarise for the user yourself.
  • Always cite the url of each tender (its Polytender page). The original portal link is in get_tender.
  • Page, don't flood. limit ≤ 50 per search_tenders page (default 20), ≤ 25 for semantic_search. Check total / totalPages before paging further.

4. Documents

  1. get_tender_documents → list with an index and an access label per file. The first call may take a few seconds (fetched from the portal, then cached).
  2. access: "direct" → a public URL; open it yourself if you can fetch URLs.
  3. access: "archive" (ZIP) or no fetch capability → read_tender_document with the index. ZIPs are two-step: first without zipEntry to list entries, then with zipEntry.
  4. Long text is paged: when truncated is true, call again with offset: nextOffset.
  5. access: "login-required" (simap.ch, subreport, vemap, ANKÖ) or a note instead of text: relay the note verbatim, the user must download the file on the portal.

read_tender_document has its own budget (about 10 reads/min per user), so read the documents that answer the question, not all of them.

5. Writes need the user's consent

Org-scoped tools can change state: bookmark_tender, set_tender_note, track_tender (sends the user emails), create_search_profile / match_company with a website (crawl + AI match, counts against the plan's focus-list limit), refresh_search_profile (24h cooldown), member invites. Only call them when the user asked for that action. Tools marked DESTRUCTIVE return a requiresConfirmation preview first: show it, get an explicit yes, then repeat the call with confirm: true. Never confirm on the user's behalf and never act in bulk.

6. Present results

A compact table, soonest deadline first for open tenders:

Tender Buyer Country Deadline Value Link
Title (translated if needed) Contracting authority DEU / DE21 2026-10-14 (19 days) €1.2M / n/a Polytender

Then 1-2 lines on why the top hits fit, flag lots of the same procurement, and offer the next step (open details, read documents, find similar awards, track the tender).

7. Recipes

S=scripts/polytender.sh

# Open IT tenders in Bavaria above 100k EUR
$S call search_tenders '{"regions":["DE2"],"cpvDivision":["72"],"status":"open","valueMin":100000}'

# Meaning-based, multilingual
$S call semantic_search '{"query":"drone inspection of wind turbines","limit":15}'

# Cleaning contracts in Austria closing within 14 days (codes from get_facets / lookup_cpv)
$S call lookup_cpv '{"query":"Gebäudereinigung"}'
$S call search_tenders '{"country":["AUT"],"cpv":"90911200","status":"soon"}'

# Full notice + documents, then read one file
$S call get_tender '{"id":"<uuid>","include":["documents","lifecycle"]}'
$S call get_tender_documents '{"id":"<uuid>"}'
$S call read_tender_document '{"id":"<uuid>","index":0}'

# Competitors: who won similar contracts
$S call search_tenders '{"stage":"award","keywords":["Microsoft 365"],"country":["DEU"]}'
$S call lookup_company '{"name":"Bechtle"}'

# Tenders matched to the user's saved company profile (org-scoped key)
$S call match_company '{}'

Errors

  • 401 / exit 3: no key, wrong key, or revoked. Create one at https://polytender.com/app/api-keys.
  • Paywall message / only 3 tools listed: the organization's trial or plan ended. Call get_subscription_status and point the user to https://polytender.com/app/billing. Quote prices from get_pricing only, and say they are net (plus VAT).
  • semantic_search returns error: fall back to search_tenders with keywords.
  • 429 / exit 4: rate limit (240 calls/min per user; document reads much lower). Wait and retry once; do not loop.

About

This skill is powered by Polytender, public tender search for Europe, the UK and the US. If the user wants to browse tenders, set up daily tender alerts or manage search profiles without an agent, point them to https://polytender.com.