companies-house-screening-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@companies-house-screening-mcpScreen these companies: Acme Ltd, Beta Ltd, Gamma Ltd"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
companies-house-screening-mcp
Screen UK companies against the Companies House public register from an MCP host. Batch screening of a supplier list, one-call company snapshots, and factual signals rather than a risk score.
Published with npm provenance and an attested container image, so the artefact is traceable to the commit that produced it.
There is another one, and you should know about it
companies-house-mcp by
@aicayzer has existed since
July 2025, is at v4.0.0, and is actively maintained. It covers the same API.
This project is not first and does not claim to be.
The two are shaped differently, so which one fits depends on what you are doing.
Use theirs if you want breadth. It exposes more of the API — registers, exemptions, UK establishments, officer disqualifications — and, importantly, it can download the filed documents themselves. This one deliberately does not: the Companies House document API is out of scope here.
Use this one if you are screening rather than browsing. The differences that matter:
Batch screening |
|
Never guesses a company number | Retrieval tools refuse a company name outright, before any request. Given a name, a model produces a number that looks right, and a plausible wrong number returns a different real company that nothing downstream flags. ADR 5. |
Signals, not scores | Facts read off the register with the date or name behind each, and deliberately no rating. ADR 7 has the argument. |
Nothing dropped quietly | Partial results are labelled; a screening table that comes back short always says why. ADR 8. |
Documentation that cannot go stale | The tool reference is generated from the running server and every example executes; CI fails if either drifts. ADR 9. |
A tool-selection eval | Asks a real model which tool it reaches for, and fails on flakiness. ADR 10. |
Eleven decisions are written up in docs/adr, including the ones that did not go the obvious way.
Install
npx -y companies-house-screening-mcpHost configuration:
{
"mcpServers": {
"companies-house": {
"command": "npx",
"args": ["-y", "companies-house-screening-mcp"],
"env": { "COMPANIES_HOUSE_API_KEY": "your_key" }
}
}
}Or with Docker — note -i and no -t, because a TTY corrupts the JSON-RPC
framing:
docker run --rm -i -e COMPANIES_HOUSE_API_KEY=your_key ghcr.io/kaylum54/companies-house-screening-mcpGet a free API key at developer.company-information.service.gov.uk: register, create an application against the Live environment, and create a key of type REST (a stream key authenticates the same way but is for a different service).
What it is for
Two cases it was actually built around. More in docs/recipes, which are executed on every build.
Screening a supplier list
The quarterly onboarding list lands: forty new suppliers, first invoices due in a fortnight. One call.
{
"tool": "screen_companies",
"arguments": { "companies": ["04138203", "Bramble Facilities", "SC443221"] }
}One row per company with its signal codes. Skip the rows carrying nothing; open
the ones with accounts_overdue or outstanding_charges using
company_snapshot.
The behaviour that earns its place is what happens to the awkward entries.
"Bramble Facilities" matches several companies, so it comes back under
unresolved with its candidates rather than resolved to a best guess. And
if the rate limit runs out at company 31, the remaining nine come back under
not_screened with the reset time — because a table that quietly stops at 31
looks exactly like a table where nine companies were clean.
Officers are excluded by default (one extra request per company), so the
officer-based signals cannot appear unless you ask for them. sections_used
says so on every response.
Verifying an invoice before paying it
A first invoice from an unfamiliar supplier, or an email asking to change bank details on an existing account.
{
"tool": "company_snapshot",
"arguments": { "company_number": "04138203" }
}Four checks in one response: the company exists, status is active,
registered_office_address matches what is printed on the invoice, and
age_years plus the incorporated_within_last_year signal say whether this
counterparty existed a month ago. The address is flattened to one line
specifically so it can be compared without reassembling nine fields.
This is the case the no-names rule is for. Invoice-redirection fraud works because the paperwork looks right. If a tool accepts a company name and a model invents a number to look it up, you get a confident "verified — active, good standing" about a different real company, with real directors and real filings, and nothing anywhere flags it. You would have used a verification step to approve the fraud.
So passing a name is refused before any request is made:
{
"error": {
"code": "INVALID_COMPANY_NUMBER",
"message": "\"Royal Mail Group Limited\" looks like a company name, not a company number.",
"next_step": "Call find_company with this name to get candidate company numbers, then call this tool again with the number of the right one. Do not guess a number: a plausible wrong company number returns a real company and nothing will flag it as the wrong one."
}
}What it cannot tell you: whether a bank account belongs to that company. This raises or lowers suspicion; it does not settle it.
Why another API wrapper
The obvious way to build this is one MCP tool per endpoint. Twenty-two thin pass-throughs, a weekend's work, and it is what most published MCP servers are. It is also bad in three specific ways:
Every tool schema sits in the model's context on every turn, whether the task needs it or not.
It pushes the orchestration onto the model. "Is this supplier safe to onboard" becomes search, then profile, then officers, then charges, then insolvency — five round trips and five chances to lose the thread.
Companies House payloads carry structure no model reads —
links,etag,kind, per-item ETags, filing-transaction arrays, nine-key address objects. Shaping them away saves between 36% and 72% depending on the endpoint, measured against real recorded responses rather than assumed (npm run measure).
So this server exposes eleven tools shaped around questions, two of which
(company_snapshot and screen_companies) do the fan-out server-side and
return one derived object. Retrieval tools accept a company number and refuse
a company name, because given a name a model will guess a number, and a
plausible wrong company number returns a real company that nothing downstream
flags as wrong.
The tools
Tool | Returns |
| Ranked candidates for a name or number, with a |
| Candidate officer IDs for a person's name, with appointment counts. |
| Profile, plus derived flags for overdue filings, charges, insolvency and recent incorporation. |
| Current and resigned officers, each with the ID needed to look up their other companies. |
| What was filed and when, filterable by category. |
| Secured debt, with a derived |
| Who actually controls the company, and how that control is held. |
| Insolvency cases and the practitioners appointed. |
| Every company an officer sits on — the conflict-of-interest tool. |
| Profile, officers, charges and insolvency in one call, with signals. |
| Up to 50 companies in, one row each out, nothing dropped quietly. |
Full reference: docs/tools. Worked examples: docs/recipes — supplier screening, director conflict checks, invoice verification, debtor risk, competitor filing watch.
The signals are facts, not a rating. This server does not score companies and will not tell you whether one is safe to trade with — it reports what it found on the register, with the date or the name behind each observation, and leaves the judgement with the person who has the context. An empty signal list means nothing on the list was found, not that the company is sound. ADR 7 has the full reasoning.
Every tool is annotated readOnlyHint: true, publishes an output schema, and
takes verbose to return the untouched payload alongside the shaped one.
Under the tools
Piece | What it does |
| Validates every environment variable at startup and reports all the problems at once, naming the variable rather than the internal field. |
| Basic-auth requests, per-request timeout, jittered retry on 429 and 5xx, conditional revalidation, stale-on-failure fallback. |
| Sliding window sized to the documented 600 per five minutes, with a safety margin and serialised acquisition. |
| Memory over disk, TTL per resource kind, atomic writes, corrupt entries treated as a miss. |
| Every failure carries a stable code, a plain sentence and a next step. |
Projections | Upstream read defensively field by field; output validated strictly against the published schema. |
284 tests, no network, no API key required to run them.
Configuration
Only one variable is required.
Variable | Default | Notes |
| — | Required. Create a REST API key at the developer portal. Not a streaming key. |
|
| Override for a proxy. |
|
| Requests per window. Lower it if the key is shared with another process. |
|
| Five minutes. |
|
| Fraction of the budget this process will use. |
|
| |
| platform cache dir | Respects |
|
| Per request. |
|
| Retries after the first attempt. |
|
|
|
| — | Absolute path to a |
Development
npm install
npm test
npm run typecheck
npm run build
npm run docs:generateThe documentation is generated and gated. docs/tools is rendered from the
running server over a real MCP client, and every call in docs/recipes is
executed when the pages are built. npm run docs:check fails if what is
committed differs, CI runs it before the tests, and the suite runs the same
comparison so the failure arrives while you still have the change in front of
you. Change a tool description and you regenerate, or the build goes red.
The suite runs offline against fixtures recorded from the live Companies House
API, so a fresh clone works with nothing configured. npm run record-fixtures
re-records them — see tests/fixtures/README.md for
which companies they come from and why those were chosen.
Once you hold a key, copy .env.example to .env and fill it in:
npm run test:liveEvery development command reads that file. Anything already set in your shell
wins over it. The published server does not read a .env unless
CH_ENV_FILE names one — a host launches it with the host's working
directory, and picking up whatever .env happens to be there is a good way to
load the wrong credentials.
That test runs nightly in CI. Its job is not to pass — it is to fail loudly the week Companies House changes a field, so the fixtures get refreshed before a user finds the drift instead.
The tool-selection eval
Every test in this repository asks does the tool work. One thing none of them can ask is whether a model reaches for the right tool when a person asks a real question — a tool can be correct, fast and fully covered and still never get chosen, because its description is vague or overlaps another. That is the most common real defect in published MCP servers.
npm run eval -- --repeat 3Runs through OpenRouter or the Anthropic API — set OPENROUTER_API_KEY
or ANTHROPIC_API_KEY. It defaults to z-ai/glm-5.2 on OpenRouter, about 4p
for a full pass, because an eval nobody runs because of the bill is not doing
anything. Point --model at anything with tool support to compare.
Fourteen questions phrased the way a person would phrase them, scored on which tool was called first, whether a forbidden tool was touched, whether the arguments were right, and — the one that matters — whether the model invented a company number that was not in the question. A case that passes two runs in three is reported as flaky and fails, because intermittent selection means two descriptions overlap.
Run across three models (GLM 5.2, Kimi K3, DeepSeek V4 Pro) it scores 93–98%. The grounding group — given a company name and no number, search rather than recall one — passes 7/7 on all three. The failures clustered, and three of them turned out to be defects in my own tool descriptions and one in the eval itself, rather than in any model.
No Companies House key is needed; nothing is executed. Full comparison and what it found in evals/README.md, reasoning in ADR 10.
Design notes
Eleven decisions are written up in docs/adr:
Scope
Read-only, permanently. Every tool is annotated readOnlyHint: true and there
is no write path. The Companies House filing API, which submits documents on
a company's behalf, is a different product with a different risk profile and is
out of scope for this one. The streaming API is out of scope too. Fetching the
PDF or iXBRL of a filing through the document API is phase 7 and would remain
read-only.
Roadmap
Phase | Content | Status |
1 | Client, auth, rate limiter, cache, error mapping, fixtures | done |
2 | Nine primitive tools with Zod schemas and shaped projections | done |
3 |
| done |
4 | Generated tool docs with a CI drift check, five worked recipes | done |
5 | Tool-selection eval suite, live smoke test in CI, remaining ADRs | done |
6 | npm and Docker release with provenance | done — published 2026-08-20 |
Licence
Source code: MIT.
Data returned by this server is published by Companies House under the Open Government Licence v3.0 and is not covered by the MIT licence. If you redistribute it, carry the attribution the OGL requires:
Contains public sector information licensed under the Open Government Licence v3.0.
This project is not affiliated with or endorsed by Companies House.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Companies House MCP — UK statutory company registry (BYO key)
Remote MCP server to enrich company profiles with structured B2B data and confidence scores.
Company intelligence via UK Companies House and risk screening across 386 risk data sources.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kaylum54/companies-house-screening-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server