MCP tools
Tenders and search
MCP tools for searching and reading tenders, their summaries and price history, plus favorites, saved searches and export.
These tools find and read the tenders the platform collects from Etimad and NUPCO, together with the manual tenders your own team added, which only your organization sees. A tender's ID, which comes from the listing and search tools here, is what the bid, item, quotation and price tools on the other pages take. To add or edit a manual tender, see Manual tenders, items and participation.
q to tenders_list. It is found under any spelling (NPT0002/26, NPT0002-26, NPT 0002/26) and even after its deadline has passed. The syntax of q is the same in tenders_list and tenders_search, and Searching tenders explains it.last_offer_date) is when offers close, and its publication date (submission_date) is when it was published. tenders_list and export_tenders_csv filter by the deadline, while tenders_search filters by the publication date.| Tool | What it does |
|---|---|
| tenders_list | List tenders |
| tenders_search | Search tenders |
| agencies_list | List agencies |
| tenders_get | Get a tender |
| tenders_summary | Get a tender's AI summary |
| tenders_distribution | Item distribution |
| tenders_historical_prices | Past awards for a tender's items |
| tenders_sourcing_history | Past supplier prices for a tender's items |
| tenders_submission_history | Your history with a tender's items |
| favorites_list | List favorites |
| favorites_add | Add a favorite |
| favorites_remove | Remove a favorite |
| saved_searches_list | List saved searches |
| saved_searches_create | Save a search |
| saved_searches_delete | Delete a saved search |
| export_tenders_csv | Export tenders as CSV |
Finding tenders
Four ways in: a listing with filters, a full-text search, a quick lookup for what you already follow, and the list of agencies for building filters.
tenders_list
Lists tenders from Etimad, NUPCO and your team's manual tenders, newest published first, each with a one-line AI headline when one exists. This is the tool for finding a tender by its tender or reference number: pass the number as q.
Read-only · scope tenders.read · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
source | string | No | Only tenders from this source: etimad, nupco, or manual for the tenders your team added. |
agency | string | No | Part of the government agency's name; capitals do not matter. |
q | string | No | What to look for in the tender's name, agency, tender and reference numbers and item text. A keyword of 8 or more digits (a NUPCO material code) also matches the items' material codes. A comma means OR, + or an uppercase AND means AND, and a space is literal, so gloves nitrile looks for that exact phrase: put + between words you want together. The structured syntax works too: the fields agency:, name:, number:, reference:, source:, type:, status: and code: (an exact material code), plus NOT, - to exclude, quoted phrases, parentheses, and item:(...) to require a whole condition within one item, for example agency:health item:(suture AND absorbable) -pediatric. Invalid syntax answers 400 with a readable message, and an unfinished query runs on the part that is complete. |
type | string | No | The tender type: open, direct, limited, contest or badail (NUPCO Badail pay-on-account requests). The stored Arabic value is accepted too. |
date_from | string | No | The lower bound on the offer deadline (last_offer_date), as YYYY-MM-DD. |
date_to | string | No | The upper bound on the offer deadline, as YYYY-MM-DD; the whole of that day counts. |
created_within | integer | No | Only tenders that reached the platform within this many past days, from 1 to 3,650. |
expired | boolean | No | Send true to include tenders whose deadline has passed. Without it, a listing that uses q, product_id, a date filter or created_within shows open tenders only. |
past_only | boolean | No | Send true to see only tenders whose deadline has passed. |
product_id | integer | No | A catalog product's ID, from `products_list`. Only tenders that ask for this product: those with a live item carrying one of the product's linked NUPCO codes. |
{
"q": "syringe + sterile",
"source": "nupco",
"created_within": 7,
"page_size": 10
}Returns: A page of tenders with total, total_pages, depth_limited and max_rows. Each tender carries its details, summary_headline_en and summary_headline_ar (empty until an AI summary exists), and item_match set to item when the match came through its items.
- The bare listing, and one filtered only by
source,agencyortype, includes tenders whose deadline has passed. A search withq,product_idor a date filter shows open tenders only unless you sendexpired. Two things are exempt: a tender or reference number sent on its own is always found, and your team's manual tenders always show, even without a deadline. - When you send both
qandproduct_id, the tenders matching the product are added to those matchingq;qdoes not narrow the product's results. - For a search that matches expired tenders without
expired, or that filters by publication date, use `tenders_search`. - Paging stops at a depth set by your organization's subscription, and before its first payment pages are smaller and the depth shallower. When a result holds more,
depth_limitedistrue,max_rowsgives how many results paging reaches, andtotal_pagescounts only reachable pages. To see others, narrowqor the filters rather than paging further. - Tender and price-history searches share a per-minute budget across the whole organization, and a smaller one per connection. When either is reached, the tool answers
rate_limitedwith the seconds to wait. Walking one search page after page quickly slows responses for a while (slowed_down).
tenders_search
Searches tenders and their items by name, agency, tender and reference numbers and item text. Unlike `tenders_list`, it has no open-tenders default, so expired tenders match too, and its date filters bound the publication date rather than the deadline.
Read-only · scope search · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Yes | What to look for: a tender or reference number, plain keywords (a comma means OR, + or an uppercase AND means AND, a space is literal), or the structured syntax with fields, NOT, -, quotes, parentheses and item:(...), for example agency:health item:(suture AND absorbable) -pediatric. A keyword of 8 or more digits (a NUPCO material code) also matches item material codes. See Searching tenders. |
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
source | string | No | Only tenders from this source: etimad, nupco, or manual for the tenders your team added. |
agency | string | No | Part of the government agency's name. |
type | string | No | The tender type: open, direct, limited, contest or badail (NUPCO Badail pay-on-account requests). The stored Arabic value is accepted too. |
date_from | string | No | The lower bound on the publication date (submission_date), as YYYY-MM-DD. |
date_to | string | No | The upper bound on the publication date, as YYYY-MM-DD. |
{
"q": "agency:health item:(suture AND absorbable) -pediatric",
"source": "nupco",
"date_from": "2026-09-01"
}Returns: A page of tenders with total, total_pages, depth_limited and max_rows, newest published first. Each carries item_match, true when the match came through its items.
- Tenders of every deadline appear here, expired and open. To keep only open ones, or to filter by the offer deadline, use
tenders_list. - The results do not carry the one-line AI headline that
tenders_listadds. - Paging stops at a depth set by your organization's subscription, and before its first payment pages are smaller and the depth shallower. When a result holds more,
depth_limitedistrue,max_rowsgives how many results paging reaches, andtotal_pagescounts only reachable pages. To see others, narrowqor the filters rather than paging further. - Tender and price-history searches share a per-minute budget across the whole organization, and a smaller one per connection. When either is reached, the tool answers
rate_limitedwith the seconds to wait. Walking one search page after page quickly slows responses for a while (slowed_down).
agencies_list
Lists every distinct government agency name found on the tenders you can see, in Arabic alphabetical order. Use it to see which agencies exist before filtering by agency.
Read-only · scope search · all roles · Pro and Enterprise plans
No parameters.
Returns: agencies: a list of agency names.
- The list includes the agencies of your team's manual tenders and nothing from other organizations' manual tenders.
Reading a tender
tenders_get
Returns one tender in full: its details and all of its items, with a link to its page in the app. For a short description of what the tender buys, use `tenders_summary` instead.
Read-only · scope tenders.read · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The tender's ID, from tenders_list or tenders_search. |
Returns: The tender's fields (such as tender_name, agency_name, tender_number, reference_number, last_offer_date and submission_date), its items and its web_url.
- The tool returns every item however many there are, so the reply can be long for a large tender. Each item has
item_number,item_name,description,quantityandunit, andmaterial_codeon NUPCO tenders. An item with a value indeleted_atwas removed from the tender. - A tender that does not exist, or another organization's manual tender, answers 404.
tenders_summary
Returns the AI-written summary of a collected tender: a one-sentence headline, a short summary, a few highlights, and the items grouped into product families with a count each (twenty syringe variants read as one entry, "Syringes", counted 20). The first request for a tender writes and stores its summary, and everyone after it reads the stored one.
Changes data · scope tenders.read · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The tender's ID. |
Returns: tender_id, status and summary. The summary holds headline_en and headline_ar, summary_en and summary_ar, highlights_en and highlights_ar, item_groups (each with name_en, name_ar and item_count), generated_at and items_meta.
statusisreadywhensummaryis filled.generatingmeans another request is writing the summary: call again after a few seconds.unavailablemeans there is no summary and none could be made now, withsummaryempty; the reply does not say why.- When no summary exists yet, the call writes it now, which can take up to a minute. Each key may start 60 new summaries an hour; past that the call answers 429.
- Only tenders collected from Etimad and NUPCO have summaries. Your team's manual tenders always answer
unavailable. - The text is written from the tender record and NUPCO's earlier award results for the same item codes, in Arabic and English together, so present it as an AI summary. The viewer, technical and viewer (no pricing) roles cannot call this tool, though they still see the one-line headline in
tenders_list.
tenders_distribution
Returns how a tender's items are distributed across locations: for each item, the plant and delivery address it goes to and the quantity for each. Items with no distribution rows are left out.
Read-only · scope tenders.read · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The tender's ID. |
Returns: distributions: a list with one entry per item, holding tender_item_id and its rows, each with plant, delivery_address and quantity.
- A tender that does not exist, or another organization's manual tender, answers 404.
Price history for a tender's items
Three views of what is already known about a tender's items before you price them: what NUPCO awarded earlier, the supplier prices your organization recorded, and what your organization submitted. All three tools belong to the Pro and Enterprise plans. The app shows the same information, explained in Price History.
tenders_historical_prices
Matches a tender's items to earlier NUPCO award results by material code. For each code it lists who won, from which manufacturer and at what unit price, cheapest first.
Read-only · scope tenders.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The tender's ID. |
Returns: prices: one entry per material code, with its rows. Each row has unit_price, currency, uom, vendor_name, manufacturer, country, catalog_no, source_tender_number, award_date, awarded_to_us and price_hidden, plus price_hidden_reason on a row hidden until the subscription is paid. Beside it, prices_hidden is unpaid when prices are hidden for that reason, else null.
- A row whose award announcement published no price has an empty
unit_price(null). That means the price was not published, never zero, and these rows come last. awarded_to_usistruewhen the winning vendor's name matches your organization's name or its legal name in Arabic or English.- Until your organization's first subscription payment clears, rows may come back without prices: a row hidden for that reason has
unit_pricenull,price_hiddenset totrueandprice_hidden_reasonset tounpaid, and rows are ordered by vendor, not price. The price exists and is hidden; it is not an unpublished one. - A row with
price_hiddenset totrueand noprice_hidden_reasonhas a price your organization does not see, ranked with the rows that have no price. The price exists and is hidden; it is not an unpublished one. - Each read counts against your organization's per-minute budget for searches of tenders and price history, shared by every key, connector and the web app. When it runs out the tool answers 429
rate_limitedwith the seconds to wait. - Only items with a material code are matched, so a tender whose items have none returns an empty
priceslist. To search the whole record by code or vendor, use `historical_prices_list`.
tenders_sourcing_history
Shows the supplier prices your organization recorded earlier for the same items on its other tenders and RFQs, matched to this tender's items by catalog product or NUPCO code.
Read-only · scope tenders.read · roles: owner, admin, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The tender's ID. |
Returns: items: one entry per item that has history, holding tender_item_id and its quotes. Each quote has matchBasis (product or code), unitPriceUsd, unitPriceSar, currency with quotedUnitPrice, vendorName, leadTimeDays, catalogNo, source and submittedAt, along with the RFQ or tender it came from.
- At most 50 quotes per item, newest first, and quotes without a price are dropped.
matchBasisisproductwhen the quote prices the item's catalog product, andcodewhen it shares the item's NUPCO code. - This tender's own quotes are left out: only your other tenders and RFQs count. The history is evidence, not a current cost, so it changes nothing in this tender's costs.
- An item with neither a NUPCO code nor a selected product has no history. Supplier prices are cost data, so the sales and technical roles do not see them.
tenders_submission_history
Shows your organization's own history with a tender's products, matched by material code: the NUPCO awards that went to you and the prices you quoted before.
Read-only · scope tenders.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The tender's ID. |
Returns: submissions: one entry per material code that has history, holding awarded (each with unit_price, currency, uom, source_tender_number, award_date and price_hidden, plus price_hidden_reason on an award hidden until the subscription is paid) and quotes (each with unit_price, currency, source, reference, customer_name, status, quote_date and tender_id). Beside it, prices_hidden, as on tenders_historical_prices.
awardedholds only the awards whose winning vendor's name matches your organization's name or legal name, newest first. Itsunit_pricecan be empty when the award was published without a price.- Award prices here follow the same rules as
tenders_historical_prices, so renaming the organization reveals no price it cannot see. Until your organization's first subscription payment clears,prices_hiddenmay beunpaid, and an award hidden for that reason comes back withunit_pricenull,price_hiddenset totrueandprice_hidden_reasonset tounpaid. An award withprice_hiddenset totrueand noprice_hidden_reasonhas a price your organization does not see. The price exists and is hidden; it is not an unpublished one. quotesare your organization's own prices, so none of them is hidden.quotescome from two places: quotes you imported earlier (sourceisimport) and the item prices you set on your other tenders in ProcDuck (sourceissystem, in SAR, with atender_id). This tender's own prices are left out.- Only items with a material code are matched, so a tender whose items have none returns an empty
submissionslist.
Favorites
A favorite is a tender your organization follows, with an optional note. Favorites belong to the whole organization, not one member, and they count with bids toward your plan's limit on active tenders: 10 on Starter, 100 on Pro, unlimited on Enterprise.
favorites_list
Lists the tenders your organization has favorited, newest first, each with the tender record and the note saved with it. Every member sees the same list.
Read-only · scope favorites.read · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
Returns: A page of favorites with total and total_pages. Each has tender_id, notes, created_at and the tender itself in tenders.
favorites_add
Adds a tender to your organization's favorites, with an optional note. Adding a tender that is already a favorite replaces its note only, and leaving notes out clears it, so repeating the call never creates a duplicate.
Changes data · scope favorites.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | Yes | The tender's ID, from tenders_list or tenders_search. |
notes | string | No | A note saved with the favorite, up to 2,000 characters. Send null or leave it out for no note. |
{
"tender_id": 48213,
"notes": "Follow up with the procurement desk before the briefing"
}Returns: The favorite as saved, with tender_id, notes and created_at.
- Favorites count with bids toward the active-tenders limit, and a tender in both counts once. A new favorite past the limit is refused with 402 and
plan_limit_reached, naming thelimitandcurrent. Changing the note of an existing favorite is never refused. - A tender you cannot see answers 404.
favorites_remove
Removes a tender from your organization's favorites, and the note saved with it goes too. Removing a tender that is not a favorite is not an error.
Deletes or overwrites data · scope favorites.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | Yes | The ID of the tender to remove. |
Returns: A confirmation of the removal.
Saved searches
A saved search stores a query and filters under a name so your team can run them again from the search page. It does not notify you of new matches; for that, create a keyword alert.
saved_searches_list
Lists your organization's saved searches, newest first. Every member sees the same list.
Read-only · scope search · all roles · Pro and Enterprise plans
No parameters.
Returns: saved_searches: a list in which each search has id, name, query, agency, tender_type, source, date_from, date_to and created_at.
saved_searches_create
Saves a search, its query and filters, under a name, and it appears in your organization's list of saved searches.
Changes data · scope searches.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The saved search's name, 1 to 200 characters. |
query | string | No | The search text, in the syntax that q takes in tenders_list, up to 500 characters. Send null for none. |
agency | string | No | The agency filter, up to 300 characters. |
tender_type | string | No | The tender type filter: open, direct, limited, contest or badail. |
source | string | No | The source filter: etimad, nupco or manual. |
date_from | string | No | The earliest offer deadline, as YYYY-MM-DD. |
date_to | string | No | The latest offer deadline, as YYYY-MM-DD. |
{
"name": "Ministry of Health sutures",
"query": "item:(suture AND absorbable)",
"agency": "Health",
"source": "nupco"
}Returns: The saved search, in saved_search.
- When a member opens the saved search on the search page,
date_tobecomes the nearest "expires in" choice of 7 days, 30 days or a year, and it is ignored once that date has passed.date_fromis stored but the page does not apply it. - Everything you send is saved as given, without a check that the query is valid.
saved_searches_delete
Deletes a saved search from your organization's list. This cannot be undone.
Deletes or overwrites data · scope searches.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The saved search's ID, from saved_searches_list. |
Returns: A confirmation of the deletion.
- An ID that does not belong to your organization deletes nothing and is not an error.
Exporting
export_tenders_csv
Exports up to 1,000 tenders as CSV text, or those tenders' items when you send items as true. The column headings are in Arabic and the text is UTF-8 with a BOM, so Excel opens it correctly.
Read-only · scope export · roles: owner, admin · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | No | Plain keywords to look for in the tender's name, agency, tender and reference numbers and item text: a comma means OR, + or an uppercase AND means AND. This tool does not understand the structured syntax of tenders_search (such as agency:), so narrow with the other parameters instead. |
source | string | No | Only tenders from this source: etimad, nupco, or manual for the tenders your team added. |
agency | string | No | Part of the government agency's name. |
type | string | No | The tender type: open, direct, limited, contest or badail (NUPCO Badail pay-on-account requests). The stored Arabic value is accepted too. |
date_from | string | No | The lower bound on the offer deadline, as YYYY-MM-DD. |
date_to | string | No | The upper bound on the offer deadline, as YYYY-MM-DD; the whole of that day counts. |
created_within | integer | No | Only tenders that reached the platform within this many past days, from 1 to 3,650. |
expired | boolean | No | Send true to include tenders whose deadline has passed. Without it, only open tenders are exported. |
items | boolean | No | Send true for one row per tender item instead of one row per tender. |
{
"q": "gloves",
"source": "nupco",
"created_within": 30,
"items": true
}Returns: CSV text whose first line is the column headings. Each tender is a row with its source, name, agency, type, reference number, publication date, offer deadline, purchase fee, remaining days and link. With items, each item is a row with its tender's name and agency, the item number, name, description, quantity, unit and estimated price.
- Only the owner and admin roles may export, and the
exportscope is separate fromtenders.read. - At most 1,000 tenders are exported and there is no paging: narrow the results with the filters. With
items, the items of those tenders are exported, so the file can still be long. - The date filters here bound the offer deadline, as in
tenders_listand unliketenders_search, and only open tenders are exported unless you sendexpired. - Every export is recorded in the team activity log under the member who created the key, where the owner sees it in team_activity_list.