# UK Public Procurement Monitor (Find a Tender+Contracts Finder) (`capitalist_developer/uk-procurement-unified-monitor`) Actor

Unified monitor and Streamable HTTP MCP server for UK public procurement notices across Find a Tender and Contracts Finder (OCDS 1.1.5).

- **URL**: https://apify.com/capitalist_developer/uk-procurement-unified-monitor.md
- **Developed by:** [Harsh](https://apify.com/capitalist_developer) (community)
- **Categories:** Business, Lead generation, MCP servers
- **Features:** Limited permissions, Single-tenant, Server interface
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 procurement notice delivereds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## UK Public Procurement Monitor (Find a Tender + Contracts Finder)

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-orange?logo=apify)](https://apify.com/capitalist_developer/uk-procurement-unified-monitor)
[![Node.js](https://img.shields.io/badge/Node.js-20_ESM-green?logo=node.js)](https://nodejs.org)
[![OCDS Standard](https://img.shields.io/badge/OCDS-1.1.5-blue)](https://standard.open-contracting.org/)
[![Model Context Protocol](https://img.shields.io/badge/MCP-Streamable_HTTP-purple)](https://modelcontextprotocol.io/)

A production-grade, unified procurement monitoring Actor and **Streamable HTTP Model Context Protocol (MCP) server** for UK public sector opportunities. Automatically ingests, normalizes, deduplicates, and filters tenders and contract awards from both **Find a Tender (FTS)** and **Contracts Finder (CF)** via native Open Contracting Data Standard (OCDS 1.1.5) APIs.

***

### ⚡ Model Context Protocol (MCP) Integration

Connect this Actor directly to **Claude Desktop**, **Cursor IDE**, **ChatGPT**, or custom AI agent frameworks to give your AI real-time search capabilities across UK government procurement.

#### MCP Endpoint URL

```text
https://mcp.apify.com/?tools=capitalist_developer/uk-procurement-unified-monitor
```

#### Claude Desktop Configuration (`claude_desktop_config.json`)

```json
{
    "mcpServers": {
        "uk-procurement": {
            "command": "npx",
            "args": [
                "-y",
                "mcp-remote-client",
                "https://mcp.apify.com/?tools=capitalist_developer/uk-procurement-unified-monitor"
            ]
        }
    }
}
```

#### Available MCP Tool: `search_uk_tenders`

AI assistants can directly query tenders using flexible parameters:

- `keywords`: Array of search terms (e.g. `["software", "cloud", "cybersecurity"]`)
- `cpvCodes`: CPV prefixes or full codes (e.g. `["72000000"]`)
- `portals`: `["find_a_tender", "contracts_finder"]`
- `stages`: `["tender", "planning", "award"]`
- `minValueGbp` / `maxValueGbp`: Contract budget thresholds
- `daysBack`: Number of lookback days (default `7`)
- `maxItems`: Number of notices to return

***

### 🚀 Key Features

- **Unified Ingestion:** Ingests from both primary UK portals into one consistent, flattened schema:
  - **Find a Tender Service (FTS):** High-value public tenders above GPA thresholds.
  - **Contracts Finder (CF):** Central government and wider public sector opportunities from £12,000+.
- **Zero Browser Overhead:** Pure Node.js 20 ESM engine built with `undici` and `zod`. Ultra-fast, lightweight (256MB RAM), and no browser or Playwright overhead.
- **Resilient & Self-Healing:** Automatic 3-retry exponential backoff handling HTTP 429 rate limits, 5xx server errors, and intermittent government API maintenance.
- **Stateful Incremental Runs:** Optional `onlyNewSinceLastRun` mode uses a dedicated Key-Value Store (`uk-tenders-seen-ids`) with a 50,000-ID FIFO cache to ensure you only receive fresh, unread tenders.
- **Pay-Per-Event (PPE) Support:** Supports granular pay-per-event pricing (`mcp-lookup` flat query fee and `tender-notice` per delivered notice) with immediate budget-cap enforcement.

***

### 🎯 High-Value Use Cases

1. **B2B GovTech & SaaS Sales Prospecting:**
   Identify commercial tender opportunities for software, cloud hosting, AI solutions, and managed IT services the minute they are published.
2. **Consulting & Defense Bidding Alerts:**
   Track frameworks, engineering tenders, and advisory RFPs across local councils, the NHS, and central ministries.
3. **Automated Pipeline Integrations:**
   Run on scheduled cron intervals (hourly or daily) with `onlyNewSinceLastRun=true` and push newly announced tenders directly to Slack, Teams, or your CRM via webhooks.
4. **Competitor & Award Analysis:**
   Inspect `awardedSuppliers` and historical contract values to monitor vendor incumbency and government spending patterns.

***

### 📋 Input Configuration

| Parameter             | Type             | Default                                 | Description                                                          |
| --------------------- | ---------------- | --------------------------------------- | -------------------------------------------------------------------- |
| `keywords`            | Array of strings | `[]`                                    | Keywords to filter titles and descriptions (case-insensitive).       |
| `cpvCodes`            | Array of strings | `[]`                                    | Common Procurement Vocabulary codes (e.g. `["72000000"]`).           |
| `portals`             | Array of strings | `["find_a_tender", "contracts_finder"]` | Procurement portals to search (`find_a_tender`, `contracts_finder`). |
| `stages`              | Array of strings | `["tender", "planning"]`                | Procurement stages (`tender`, `planning`, `award`).                  |
| `minValueGbp`         | Integer          | `null`                                  | Minimum contract value filter in GBP.                                |
| `maxValueGbp`         | Integer          | `null`                                  | Maximum contract value filter in GBP.                                |
| `daysBack`            | Integer          | `7`                                     | Number of days back to look for updated/published notices.           |
| `onlyNewSinceLastRun` | Boolean          | `false`                                 | When `true`, filters out tenders seen in previous runs.              |
| `maxItems`            | Integer          | `100`                                   | Maximum number of notices to return and save to dataset.             |

#### Sample `INPUT.json`

```json
{
    "keywords": ["software", "cloud"],
    "cpvCodes": ["48000000"],
    "portals": ["find_a_tender", "contracts_finder"],
    "stages": ["tender"],
    "daysBack": 7,
    "maxItems": 10,
    "onlyNewSinceLastRun": false
}
```

***

### 📊 Sample Output (Flattened Dataset Item)

```json
{
    "ocid": "ocds-b5fd17-c8cd22a2-0eaa-4706-83c7-2cc7e56bf485",
    "releaseId": "3d3f328e-bb59-4220-bd34-085bb4e72eef-921030",
    "sourcePortal": "contracts_finder",
    "title": "Joint Contract for Supply and support My Council Services Digital Transformation Platform for Waste & Environmental Services",
    "description": "Joint Contract for Supply and support My Council Services Digital Transformation Platform for Waste & Environmental Services under G-Cloud 14 Framework Agreement (RM1557.14) Lots 1 & 3",
    "buyerName": "Redditch Borough & Bromsgrove District Councils",
    "cpvCodes": ["48000000"],
    "stage": "tender",
    "valueAmount": null,
    "currency": "GBP",
    "publishedDate": "2026-11-09T13:12:19Z",
    "deadlineDate": "2025-03-28T12:00:00Z",
    "noticeUrl": "https://www.contractsfinder.service.gov.uk/Notice/3d3f328e-bb59-4220-bd34-085bb4e72eef",
    "documentPdfUrls": [],
    "awardedSuppliers": []
}
```

***

### 🛠️ Architecture & Core Components

```text
uk-procurement-unified-monitor/
├── .actor/
│   ├── actor.json           # Standby mode, MCP path (/mcp), 256MB-512MB RAM
│   ├── input_schema.json    # Zod/JSON input schema specification
│   ├── output_schema.json   # Unified table view definition
│   ├── dataset_schema.json  # Dataset overview transformation
│   └── pay_per_event.json   # Pay-per-event pricing (mcp-lookup, tender-notice)
├── src/
│   ├── core/
│   │   ├── lookup.js        # Undici OCDS 1.1.5 client with 3-retry exponential backoff
│   │   ├── normalize.js     # Normalizer & in-memory keywords/CPV/value filter
│   │   └── state.js         # Key-Value Store deduplication (50,000 ID capacity)
│   ├── mcp/
│   │   └── server.js        # Streamable HTTP MCP Server (/mcp) with Zod tool
│   └── main.js              # Standby MCP vs. Batch Runner router
├── Dockerfile               # Node 20-slim lean production container
└── package.json             # ESM project definition
```

***

### 🧪 Local Testing

Run Batch Mode with charging logs enabled:

```bash
ACTOR_USE_CHARGING_LOG_DATASET=1 apify run --input-file=test-input.json
```

Run Standby MCP Server locally:

```bash
STANDBY_MODE=1 node src/main.js
```

Then send a test JSON-RPC request to `http://localhost:4321/mcp`:

```bash
curl -X POST http://localhost:4321/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "search_uk_tenders",
      "arguments": {
        "keywords": ["software"],
        "daysBack": 7,
        "maxItems": 5
      }
    }
  }'
```

***

### 📄 License

ISC © Capitalist_Builder

# Actor input Schema

## `keywords` (type: `array`):

Keywords to filter titles and descriptions (e.g. \['software', 'cloud']). Case-insensitive matching.

## `cpvCodes` (type: `array`):

Common Procurement Vocabulary (CPV) codes to filter by (e.g. \['72000000']).

## `portals` (type: `array`):

UK portals to fetch notices from ('find_a_tender', 'contracts_finder').

## `stages` (type: `array`):

Procurement stages to monitor (e.g. 'tender', 'planning', 'award').

## `minValueGbp` (type: `integer`):

Filter out contracts with value below this threshold in GBP.

## `maxValueGbp` (type: `integer`):

Filter out contracts with value above this threshold in GBP.

## `daysBack` (type: `integer`):

Lookback window in days from today to search for notices.

## `onlyNewSinceLastRun` (type: `boolean`):

If enabled, deduplicates against previously seen OCIDs saved in Key-Value store 'uk-tenders-seen-ids'.

## `maxItems` (type: `integer`):

Maximum number of procurement notices to retrieve and output.

## Actor input object example

```json
{
  "keywords": [],
  "cpvCodes": [],
  "portals": [
    "find_a_tender",
    "contracts_finder"
  ],
  "stages": [
    "tender",
    "planning"
  ],
  "daysBack": 7,
  "onlyNewSinceLastRun": false,
  "maxItems": 100
}
```

# Actor output Schema

## `results` (type: `string`):

Clean table view of monitored notices

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("capitalist_developer/uk-procurement-unified-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("capitalist_developer/uk-procurement-unified-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call capitalist_developer/uk-procurement-unified-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,capitalist_developer/uk-procurement-unified-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/30AF1OdqgDQ8kpO4i/builds/fOmlQD83JnT2FCiV4/openapi.json
