UK Due Diligence
# uk-due-diligence-mcp
<!-- mcp-name: io.github.paulieb89/uk-due-diligence-mcp -->
**Official-source UK due-diligence data for AI agents.**
Search Companies House, Charity Commission, The Gazette and HMLR price-paid data, plus screen names against the UK (FCDO), OFAC, EU and UN sanctions lists. Exposes atomic MCP tools for company ownership, officers, cross-company appointment history, secured charges, insolvency notices and related registry evidence — the consuming agent decides how to investigate, not the server.
Every data source is a legally-mandated register with a free official API. Zero paywalls.
[](https://pypi.org/project/uk-due-diligence-mcp/)
[](https://safeskill.dev/scan/paulieb89-uk-due-diligence-mcp)
[](https://glama.ai/mcp/connectors/io.github.paulieb89/uk-due-diligence-mcp)
[](https://smithery.ai/servers/bouch/uk-due-diligence)
[](https://vscode.dev/redirect/mcp/install?name=uk-due-diligence&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fuk-due-diligence-mcp.fly.dev%2Fmcp%22%7D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=uk-due-diligence&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fuk-due-diligence-mcp.fly.dev%2Fmcp%22%7D&quality=insiders)
[](https://cursor.com/en/install-mcp?name=uk-due-diligence&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vdWstZHVlLWRpbGlnZW5jZS1tY3AuZmx5LmRldi9tY3AifQ==)
---
## Data sources
| Register | API | Auth | Coverage |
|----------|-----|------|----------|
| Companies House | `api.company-information.service.gov.uk` | API key (free) | UK-wide |
| Charity Commission | `api.charitycommission.gov.uk` | API key (free) | England & Wales |
| HMLR Land Registry | `landregistry.data.gov.uk` (SPARQL) | None | England & Wales |
| The Gazette | `thegazette.co.uk` (Linked Data) | None (read) | UK-wide |
| UK (FCDO) / OFAC / EU / UN sanctions | consolidated list files | None | International |
---
## Quick start
### Hosted (no install)
```json
{
"mcpServers": {
"uk-due-diligence": {
"type": "http",
"url": "https://uk-due-diligence-mcp.fly.dev/mcp"
}
}
}
```
### Local (uvx)
```json
{
"mcpServers": {
"uk-due-diligence": {
"type": "stdio",
"command": "uvx",
"args": ["uk-due-diligence-mcp"]
}
}
}
```
See [Configuration](#configuration) for the environment variables it needs.
---
## Tools
**Companies House**
| Tool | Description |
|------|-------------|
| `company_search` | Search by name/keyword, filter by status/type |
| `company_profile` | Status, filing compliance, registered address, `has_charges` summary |
| `company_officers` | Directors/secretaries; each carries an `officer_id` |
| `company_psc` | Beneficial owners, PSC chain, overseas-corporate-PSC flag |
| `officer_appointments` | Full appointment history for a person by `officer_id` — including dissolved or insolvent companies not named anywhere else |
| `company_charges` | Complete secured-charge history: status, dates, secured parties, what each charge covers |
| `disqualified_search` | Search disqualified directors by name |
| `disqualified_profile` | Full disqualification record: period, Act, associated companies |
**Charity Commission**
| Tool | Description |
|------|-------------|
| `charity_search` | Search by name, filter by registration status |
| `charity_profile` | Full record: trustees, income/expenditure, governing document |
**HMLR Land Registry**
| Tool | Description |
|------|-------------|
| `land_title_search` | Price Paid Index sale transactions by postcode — not title ownership (see [Limitations](#limitations)) |
**The Gazette**
| Tool | Description |
|------|-------------|
| `gazette_insolvency` | Corporate insolvency notices across the Gazette's notice-code taxonomy (codes 2401-2465) |
| `gazette_notice` | Full legal wording of a specific notice |
**Sanctions**
| Tool | Description |
|------|-------------|
| `sanctions_screen` | Screen a name against the UK (FCDO), OFAC, EU and UN consolidated lists |
The UK list is the FCDO UK Sanctions List XML and is reported under the label `OFSI (UK)`. The parser refuses an incomplete export: a file with fewer than 5,000 designations, a drop of more than 10% against the previous load in the same process, a duplicate `UniqueID`, or a missing `DateGenerated` fails the UK list, which is then reported in `lists_unavailable` with `is_partial` true. The floor and drop threshold are constants in `sanctions.py`.
**Cross-register**
| Tool | Description |
|------|-------------|
| `search` | Fan-out search across all registers — returns IDs (for ChatGPT deep research) |
| `fetch` | Fetch a structured record by ID returned from `search` |
---
## Examples
```
Resolve "TAS Engineering" to a company number and check whether it has any outstanding charges.
```
→ `company_search` to find the company number, then `company_charges` for the full secured-debt picture (not just the `has_charges` summary).
```
Has Gareth Davies (director of company 06333469) been connected to any other companies, including ones that no longer exist?
```
→ `company_officers` to get his `officer_id`, then `officer_appointments` to surface every company he's held an appointment at, current or historic.
```
Is "MEL Precision Limited" in the middle of insolvency proceedings, and what does that actually mean legally?
```
→ `gazette_insolvency` to find the notices, then `gazette_notice` to read the full legal wording before drawing conclusions from the notice label alone.
```
Screen "Acme Trading Ltd" and its officers against sanctions lists.
```
→ `company_officers` for the officer names, then `sanctions_screen` against the company name and each officer.
---
## Limitations
Things worth knowing before trusting output:
- **`land_title_search` returns Price Paid transactions, not ownership.** It does not return current proprietor/title data — HMLR's Price Paid Index only records historic sale transactions.
- **Sanctions screening is exact/alias matching, not compliance clearance.** A company/entity legal name matches reliably; person names with transliteration variants may not. An empty result is not a guarantee of clearance, and a hit on a common name may need disambiguation.
- **`has_charges: null` means the check could not be confidently completed** — a charges-endpoint outage, or a charge with an unrecognized status. Treat it as unresolved, not as "no charges."
- **`officer_appointments` returns historical relationships.** Companies House doesn't auto-resign a director when a company enters insolvency — check `company_status` on each appointment, not just `resigned_on`.
- **Official-source data can be incomplete or delayed.** These are the same registers a human would check, with the same latency and gaps.
---
## Configuration
| Variable | Required for | Where to get it |
|----------|--------------|------------------|
| `CH_API_KEY` | All Companies House tools | [developer.company-information.service.gov.uk](https://developer.company-information.service.gov.uk) — free |
| `CHARITY_API_KEY` | Charity Commission tools | [api-portal.charitycommission.gov.uk](https://api-portal.charitycommission.gov.uk) — free |
HMLR, The Gazette, and the sanctions lists require no credentials.
---
## Project structure
```
uk-due-diligence-mcp/
├── server.py # FastMCP init, tool/resource registration, transport config
├── companies_house.py # company_search/profile/officers/psc, officer_appointments, company_charges
├── disqualified.py # disqualified_search, disqualified_profile
├── charity.py # charity_search, charity_profile
├── land_registry.py # land_title_search (SPARQL Price Paid Index)
├── gazette.py # gazette_insolvency, gazette_notice
├── sanctions.py # sanctions_screen (UK/OFAC/EU/UN consolidated lists)
├── search_fetch.py # search, fetch (cross-register fan-out)
├── models.py # Pydantic v2 output models
├── http_client.py # Shared httpx clients, retry backoff, error formatting
├── fly.toml
├── Dockerfile
├── pyproject.toml
└── .env.example
```
---
## Licence
MIT
<!-- mcp-name: io.github.paulieb89/uk-due-diligence-mcp -->
TDQS
Scored across 18 tools
The register-specific tools (company_officers, company_psc, company_charges, charity_profile, etc.) are clearly distinct, but there are two competing paths to the same data: `search`→`fetch` duplicates `company_search`→`company_profile`, `charity_search`→`charity_profile`, `disqualified_search`→`disqualified_profile`, and `gazette_insolvency`→`gazette_notice`. An agent must decide whether to use the federated generic pair or the register-specific pair, which is a real ambiguity despite the descriptions explaining both.
Most names follow a predictable register_prefix + action pattern (company_search, company_psc, charity_profile, disqualified_profile, land_title_search, sanctions_screen). The two exceptions are the bare verbs `fetch` and `search`, which lack the resource noun and sit awkwardly beside their specific counterparts.
18 tools span six registers (Companies House, Charity Commission, disqualifications, Gazette, sanctions, Land Registry) and each tool maps to a real DD step, so the count is earned. It is on the heavy side and one pair (`search`/`fetch`) is arguably redundant with the specific search/profile tools.
Coverage is strong: search + profile + officers + PSC + charges + filing history + document metadata for Companies House, search/profile for charities and disqualifications, Gazette insolvency, sanctions screening, and Land Registry sales. Gaps are minor but real: no officer-name lookup (only officer ID from company_officers), Gazette limited to insolvency notices, and land_title_search only hits the Price Paid Index rather than actual title/ownership data despite its name.