@registrum/mcp 1.2.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -12
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +43 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +86 -135
- package/dist/server.js.map +1 -1
- package/package.json +15 -6
package/README.md
CHANGED
|
@@ -2,14 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
**UK company data in your AI agent — without building Companies House plumbing.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
[](https://registry.modelcontextprotocol.io)
|
|
6
|
+
[](https://www.npmjs.com/package/@registrum/mcp)
|
|
7
|
+
[](https://status.registrum.co.uk)
|
|
8
|
+
[](https://api.registrum.co.uk/docs)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
No Companies House developer account, no rate-limit handling, no iXBRL parsing.
|
|
11
|
+
Works in Claude Desktop, Claude Code, Cursor, and any MCP-compatible client.
|
|
12
|
+
|
|
13
|
+
## Try it now — no signup, no key, no install
|
|
14
|
+
|
|
15
|
+
Point your client at the hosted endpoint and every tool answers with real data:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
https://registrum.co.uk/api/mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"registrum": {
|
|
25
|
+
"url": "https://registrum.co.uk/api/mcp"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
11
29
|
```
|
|
12
30
|
|
|
31
|
+
That is the whole setup. Ask it *"Who ultimately owns Rolls-Royce Holdings?"* and
|
|
32
|
+
it will trace the ownership chain.
|
|
33
|
+
|
|
34
|
+
The free anonymous tier is generous on the everyday tools and deliberately
|
|
35
|
+
small on the expensive ones — a couple of ownership-chain traces and financial
|
|
36
|
+
statements per day, which is enough to see exactly what comes back before you
|
|
37
|
+
decide anything. When you reach a cap, the tool tells you so and points at a
|
|
38
|
+
free key; nothing silently degrades.
|
|
39
|
+
|
|
13
40
|
---
|
|
14
41
|
|
|
15
42
|
## Why this instead of the Companies House API directly
|
|
@@ -19,7 +46,7 @@ call it yourself. What you then own is the plumbing:
|
|
|
19
46
|
|
|
20
47
|
| Doing it yourself | With Registrum |
|
|
21
48
|
|---|---|
|
|
22
|
-
| Register for a CH developer key, manage OAuth |
|
|
49
|
+
| Register for a CH developer key, manage OAuth | Nothing, or one `REGISTRUM_API_KEY` |
|
|
23
50
|
| 600 requests/5min, and you handle the 429s | Server-side throttling on a higher negotiated budget |
|
|
24
51
|
| Accounts arrive as iXBRL documents you must parse | `get_financials` returns turnover, net assets, profit/loss as numbers |
|
|
25
52
|
| PSC control types are raw codes | Decoded to plain English |
|
|
@@ -32,7 +59,10 @@ the raw response. This one does the enrichment.
|
|
|
32
59
|
|
|
33
60
|
---
|
|
34
61
|
|
|
35
|
-
##
|
|
62
|
+
## Running it with your own key
|
|
63
|
+
|
|
64
|
+
Use a key when you want the anonymous caps lifted, or when you would rather run
|
|
65
|
+
the server locally than call ours.
|
|
36
66
|
|
|
37
67
|
**Claude Desktop** — `~/.claude/claude_desktop_config.json`
|
|
38
68
|
**Cursor** — `.cursor/mcp.json` (per project) or `~/.cursor/mcp.json` (global)
|
|
@@ -49,7 +79,7 @@ the raw response. This one does the enrichment.
|
|
|
49
79
|
}
|
|
50
80
|
```
|
|
51
81
|
|
|
52
|
-
[Get a free key](https://registrum.co.uk/?utm_source=mcp&utm_campaign=readme) —
|
|
82
|
+
[Get a free key](https://registrum.co.uk/?utm_source=mcp&utm_campaign=readme) — no card.
|
|
53
83
|
|
|
54
84
|
---
|
|
55
85
|
|
|
@@ -96,8 +126,8 @@ as non-compliant.
|
|
|
96
126
|
|
|
97
127
|
## Plans
|
|
98
128
|
|
|
99
|
-
|
|
100
|
-
PSC chain traversal and the ECCTA compliance endpoint.
|
|
129
|
+
The anonymous endpoint needs no account at all. A free key raises the caps, and
|
|
130
|
+
paid tiers add volume, PSC chain traversal and the ECCTA compliance endpoint.
|
|
101
131
|
|
|
102
132
|
Prices and quotas are served live from [`GET /v1/plans`](https://api.registrum.co.uk/v1/plans) —
|
|
103
133
|
that endpoint is the source of truth, so this README does not duplicate the
|
|
@@ -110,7 +140,9 @@ numbers and cannot go stale against them. Human-readable version at
|
|
|
110
140
|
|
|
111
141
|
- Company numbers are zero-padded 8-character strings: `00445790`, `SC000268`.
|
|
112
142
|
- Responses are JSON, cached server-side (24h profiles/directors, 7d financials).
|
|
113
|
-
-
|
|
114
|
-
|
|
143
|
+
- The hosted endpoint is stateless and anonymous: it stores no account, and rate
|
|
144
|
+
limiting is keyed on a hash of the calling IP rather than anything about you.
|
|
145
|
+
- The npm package sends `User-Agent: @registrum/mcp/<version>` so we can see
|
|
146
|
+
which features developers actually use. No telemetry runs on your machine.
|
|
115
147
|
|
|
116
148
|
[API reference](https://api.registrum.co.uk/docs) · [Issues](https://github.com/vdmeu/registrum-mcp/issues) · [support@registrum.co.uk](mailto:support@registrum.co.uk)
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
3
3
|
import { createServer } from "./server.js";
|
|
4
4
|
const apiKey = process.env.REGISTRUM_API_KEY ?? "";
|
|
5
5
|
if (!apiKey) {
|
|
6
6
|
process.stderr.write("Warning: REGISTRUM_API_KEY is not set. Tool calls will fail until you set it.\n" +
|
|
7
7
|
"Get a free key at https://registrum.co.uk/?utm_source=mcp&utm_campaign=server\n");
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// A factory rather than a single instance: serveStdio builds one server per
|
|
10
|
+
// connection, and pins it to the protocol era the client handshook with, so
|
|
11
|
+
// the same tool definitions serve both 2025-era and 2026-07-28 clients.
|
|
12
|
+
serveStdio(() => createServer(apiKey));
|
|
12
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC;AACnD,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iFAAiF;QACjF,iFAAiF,CAClF,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,UAAU,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
export declare const API_BASE = "https://api.registrum.co.uk/v1";
|
|
3
3
|
export declare function callApi(path: string, apiKey: string, baseUrl?: string): Promise<unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Every tool this server exposes, in registration order.
|
|
6
|
+
*
|
|
7
|
+
* Exported because more than one surface has to agree on it: the stdio package,
|
|
8
|
+
* the hosted anonymous endpoint, and the drift test that asserts the two match.
|
|
9
|
+
* The MCP README advertised five tools while the server registered eight for
|
|
10
|
+
* five months - a list nobody could enumerate programmatically is a list that
|
|
11
|
+
* goes stale.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TOOL_NAMES: readonly ["search_company", "get_company", "get_financials", "get_directors", "get_compliance", "get_psc", "get_psc_chain", "get_network"];
|
|
14
|
+
export type ToolName = (typeof TOOL_NAMES)[number];
|
|
15
|
+
/**
|
|
16
|
+
* Called before a tool touches the API. Return a string to refuse the call with
|
|
17
|
+
* that message; return null to allow it.
|
|
18
|
+
*
|
|
19
|
+
* This is how the hosted anonymous endpoint enforces per-tool daily caps
|
|
20
|
+
* without the tool definitions knowing anything about IPs, Supabase or quotas.
|
|
21
|
+
* The stdio package passes no quota at all, so a user with their own API key is
|
|
22
|
+
* unaffected - their limits are enforced upstream against their own key.
|
|
23
|
+
*
|
|
24
|
+
* The refusal is deliberately returned as tool *text* rather than thrown: the
|
|
25
|
+
* calling model reads it and relays the upgrade path to the user in its own
|
|
26
|
+
* words, which makes the cap a conversion surface instead of an error.
|
|
27
|
+
*/
|
|
28
|
+
export type QuotaCheck = (tool: ToolName) => Promise<string | null>;
|
|
29
|
+
export interface RegisterToolsOptions {
|
|
30
|
+
apiKey: string;
|
|
31
|
+
baseUrl?: string;
|
|
32
|
+
quota?: QuotaCheck;
|
|
33
|
+
}
|
|
34
|
+
export declare const SERVER_INSTRUCTIONS: string;
|
|
35
|
+
/**
|
|
36
|
+
* Registers all eight tools on a server instance.
|
|
37
|
+
*
|
|
38
|
+
* Split out of createServer so the hosted HTTP endpoint can reuse the exact
|
|
39
|
+
* same tool definitions. `createMcpHandler` takes a factory callback rather
|
|
40
|
+
* than a server instance, so without this seam the only way to host these tools
|
|
41
|
+
* would be to copy them into the website repo - which is precisely the
|
|
42
|
+
* copy-drifts-from-source failure this codebase keeps paying for.
|
|
43
|
+
*/
|
|
44
|
+
export declare function registerTools(server: McpServer, options: RegisterToolsOptions): McpServer;
|
|
45
|
+
/** A stdio-ready server for a single user's own API key. */
|
|
4
46
|
export declare function createServer(apiKey: string, baseUrl?: string): McpServer;
|
|
5
47
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAIzD,eAAO,MAAM,QAAQ,mCAAmC,CAAC;AAEzD,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAiB,GACzB,OAAO,CAAC,OAAO,CAAC,CAclB;AAeD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,4IASb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,QAOsE,CAAC;AAUvG;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,oBAAoB,GAAG,SAAS,CA2LzF;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAiB,GAAG,SAAS,CAMlF"}
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { VERSION, USER_AGENT } from "./version.js";
|
|
4
4
|
export const API_BASE = "https://api.registrum.co.uk/v1";
|
|
@@ -26,24 +26,66 @@ function err(message) {
|
|
|
26
26
|
content: [{ type: "text", text: message }],
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Every tool this server exposes, in registration order.
|
|
31
|
+
*
|
|
32
|
+
* Exported because more than one surface has to agree on it: the stdio package,
|
|
33
|
+
* the hosted anonymous endpoint, and the drift test that asserts the two match.
|
|
34
|
+
* The MCP README advertised five tools while the server registered eight for
|
|
35
|
+
* five months - a list nobody could enumerate programmatically is a list that
|
|
36
|
+
* goes stale.
|
|
37
|
+
*/
|
|
38
|
+
export const TOOL_NAMES = [
|
|
39
|
+
"search_company",
|
|
40
|
+
"get_company",
|
|
41
|
+
"get_financials",
|
|
42
|
+
"get_directors",
|
|
43
|
+
"get_compliance",
|
|
44
|
+
"get_psc",
|
|
45
|
+
"get_psc_chain",
|
|
46
|
+
"get_network",
|
|
47
|
+
];
|
|
48
|
+
export const SERVER_INSTRUCTIONS = "Use these tools to look up UK companies registered at Companies House. " +
|
|
49
|
+
"Company numbers are zero-padded 8-digit strings (e.g. '00445790' for Tesco PLC). " +
|
|
50
|
+
"When a user gives you a company name, use search_company first to find the number, " +
|
|
51
|
+
"then use get_company, get_financials, get_directors, get_psc, get_psc_chain, get_compliance, or get_network as needed. " +
|
|
52
|
+
"Use get_psc for a flat view of who controls a company. Use get_compliance for ECCTA identity-verification status - note 'pending' means the deadline has not passed and is not a failure. " +
|
|
53
|
+
"Use get_psc_chain to trace corporate ownership upward and find the ultimate beneficial owners (UBOs) " +
|
|
54
|
+
"- it follows corporate entity PSCs recursively until reaching natural persons or foreign entities.";
|
|
55
|
+
const companyNumber = z
|
|
56
|
+
.string()
|
|
57
|
+
.regex(/^[A-Z0-9]{1,8}$/, "Must be 1-8 alphanumeric characters")
|
|
58
|
+
.describe("Companies House company number, e.g. '00445790' for Tesco PLC. " +
|
|
59
|
+
"Numeric-only numbers should be zero-padded to 8 digits.");
|
|
60
|
+
/**
|
|
61
|
+
* Registers all eight tools on a server instance.
|
|
62
|
+
*
|
|
63
|
+
* Split out of createServer so the hosted HTTP endpoint can reuse the exact
|
|
64
|
+
* same tool definitions. `createMcpHandler` takes a factory callback rather
|
|
65
|
+
* than a server instance, so without this seam the only way to host these tools
|
|
66
|
+
* would be to copy them into the website repo - which is precisely the
|
|
67
|
+
* copy-drifts-from-source failure this codebase keeps paying for.
|
|
68
|
+
*/
|
|
69
|
+
export function registerTools(server, options) {
|
|
70
|
+
const { apiKey, baseUrl = API_BASE, quota } = options;
|
|
71
|
+
/** Runs the quota gate, then the call. Every tool body goes through this. */
|
|
72
|
+
const run = async (tool, path) => {
|
|
73
|
+
try {
|
|
74
|
+
const denial = await quota?.(tool);
|
|
75
|
+
if (denial)
|
|
76
|
+
return err(denial);
|
|
77
|
+
return text(await callApi(path, apiKey, baseUrl));
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return err(String(e));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
41
83
|
server.registerTool("search_company", {
|
|
42
84
|
title: "Search for companies",
|
|
43
85
|
description: "Search for UK companies by name. Returns a list of matching companies with " +
|
|
44
86
|
"their company number, status, type, and registered address. Use this first " +
|
|
45
87
|
"when you only have a company name and need its company number.",
|
|
46
|
-
inputSchema: {
|
|
88
|
+
inputSchema: z.object({
|
|
47
89
|
query: z.string().min(1).describe("Company name or keywords to search for"),
|
|
48
90
|
limit: z
|
|
49
91
|
.number()
|
|
@@ -52,18 +94,12 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
52
94
|
.max(20)
|
|
53
95
|
.optional()
|
|
54
96
|
.describe("Maximum number of results to return (default 10)"),
|
|
55
|
-
},
|
|
97
|
+
}),
|
|
56
98
|
}, async ({ query, limit }) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const data = await api(`/search?${params}`);
|
|
62
|
-
return text(data);
|
|
63
|
-
}
|
|
64
|
-
catch (e) {
|
|
65
|
-
return err(String(e));
|
|
66
|
-
}
|
|
99
|
+
const params = new URLSearchParams({ q: query });
|
|
100
|
+
if (limit)
|
|
101
|
+
params.set("limit", String(limit));
|
|
102
|
+
return run("search_company", `/search?${params}`);
|
|
67
103
|
});
|
|
68
104
|
server.registerTool("get_company", {
|
|
69
105
|
title: "Get company profile",
|
|
@@ -72,22 +108,8 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
72
108
|
"with descriptions, accounts status, confirmation statement status, and derived " +
|
|
73
109
|
"fields like company_age_years and accounts.overdue that are not available from " +
|
|
74
110
|
"the raw Companies House API.",
|
|
75
|
-
inputSchema: {
|
|
76
|
-
|
|
77
|
-
.string()
|
|
78
|
-
.regex(/^[A-Z0-9]{1,8}$/, "Must be 1–8 alphanumeric characters")
|
|
79
|
-
.describe("Companies House company number, e.g. '00445790' for Tesco PLC. " +
|
|
80
|
-
"Numeric-only numbers should be zero-padded to 8 digits."),
|
|
81
|
-
},
|
|
82
|
-
}, async ({ company_number }) => {
|
|
83
|
-
try {
|
|
84
|
-
const data = await api(`/company/${company_number}`);
|
|
85
|
-
return text(data);
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
return err(String(e));
|
|
89
|
-
}
|
|
90
|
-
});
|
|
111
|
+
inputSchema: z.object({ company_number: companyNumber }),
|
|
112
|
+
}, async ({ company_number }) => run("get_company", `/company/${company_number}`));
|
|
91
113
|
server.registerTool("get_financials", {
|
|
92
114
|
title: "Get company financials",
|
|
93
115
|
description: "Get structured financial data for a UK company, parsed from its iXBRL accounts " +
|
|
@@ -97,42 +119,16 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
97
119
|
"Also includes accounts type (full/abbreviated/micro/dormant) and a data_quality " +
|
|
98
120
|
"block indicating which fields were extracted and which were absent from the filing. " +
|
|
99
121
|
"Cached for 7 days.",
|
|
100
|
-
inputSchema: {
|
|
101
|
-
|
|
102
|
-
.string()
|
|
103
|
-
.regex(/^[A-Z0-9]{1,8}$/)
|
|
104
|
-
.describe("Companies House company number, e.g. '00445790' for Tesco PLC"),
|
|
105
|
-
},
|
|
106
|
-
}, async ({ company_number }) => {
|
|
107
|
-
try {
|
|
108
|
-
const data = await api(`/company/${company_number}/financials`);
|
|
109
|
-
return text(data);
|
|
110
|
-
}
|
|
111
|
-
catch (e) {
|
|
112
|
-
return err(String(e));
|
|
113
|
-
}
|
|
114
|
-
});
|
|
122
|
+
inputSchema: z.object({ company_number: companyNumber }),
|
|
123
|
+
}, async ({ company_number }) => run("get_financials", `/company/${company_number}/financials`));
|
|
115
124
|
server.registerTool("get_directors", {
|
|
116
125
|
title: "Get company directors",
|
|
117
126
|
description: "Get the current and past directors for a UK company, including each director's " +
|
|
118
127
|
"name, role, appointment date, resignation date (if applicable), nationality, " +
|
|
119
128
|
"country of residence, and a list of other companies they serve or have served as " +
|
|
120
129
|
"director. This gives you a full picture of a director's corporate history in one call.",
|
|
121
|
-
inputSchema: {
|
|
122
|
-
|
|
123
|
-
.string()
|
|
124
|
-
.regex(/^[A-Z0-9]{1,8}$/)
|
|
125
|
-
.describe("Companies House company number, e.g. '00445790' for Tesco PLC"),
|
|
126
|
-
},
|
|
127
|
-
}, async ({ company_number }) => {
|
|
128
|
-
try {
|
|
129
|
-
const data = await api(`/company/${company_number}/directors`);
|
|
130
|
-
return text(data);
|
|
131
|
-
}
|
|
132
|
-
catch (e) {
|
|
133
|
-
return err(String(e));
|
|
134
|
-
}
|
|
135
|
-
});
|
|
130
|
+
inputSchema: z.object({ company_number: companyNumber }),
|
|
131
|
+
}, async ({ company_number }) => run("get_directors", `/company/${company_number}/directors`));
|
|
136
132
|
server.registerTool("get_compliance", {
|
|
137
133
|
title: "Check ECCTA identity-verification compliance",
|
|
138
134
|
description: "Check a UK company's ECCTA identity-verification status - who has verified their " +
|
|
@@ -146,21 +142,8 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
146
142
|
"IMPORTANT: 'pending' means the deadline has not yet passed - it is NOT a failure and " +
|
|
147
143
|
"must not be reported as one. Only 'overdue' means a deadline was missed. " +
|
|
148
144
|
"Requires a Pro plan or above. Cached for 24 hours.",
|
|
149
|
-
inputSchema: {
|
|
150
|
-
|
|
151
|
-
.string()
|
|
152
|
-
.regex(/^[A-Z0-9]{1,8}$/)
|
|
153
|
-
.describe("Companies House company number, e.g. '00445790' for Tesco PLC"),
|
|
154
|
-
},
|
|
155
|
-
}, async ({ company_number }) => {
|
|
156
|
-
try {
|
|
157
|
-
const data = await api(`/company/${company_number}/compliance`);
|
|
158
|
-
return text(data);
|
|
159
|
-
}
|
|
160
|
-
catch (e) {
|
|
161
|
-
return err(String(e));
|
|
162
|
-
}
|
|
163
|
-
});
|
|
145
|
+
inputSchema: z.object({ company_number: companyNumber }),
|
|
146
|
+
}, async ({ company_number }) => run("get_compliance", `/company/${company_number}/compliance`));
|
|
164
147
|
server.registerTool("get_psc", {
|
|
165
148
|
title: "Get persons with significant control",
|
|
166
149
|
description: "Get the PSC (Persons with Significant Control) register for a UK company. " +
|
|
@@ -170,36 +153,20 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
170
153
|
"instead of raw codes). Corporate entity PSCs include their company number for " +
|
|
171
154
|
"ownership chain traversal. Also detects PSC exemptions for listed PLCs. " +
|
|
172
155
|
"Cached for 24 hours.",
|
|
173
|
-
inputSchema: {
|
|
174
|
-
|
|
175
|
-
.string()
|
|
176
|
-
.regex(/^[A-Z0-9]{1,8}$/)
|
|
177
|
-
.describe("Companies House company number, e.g. '00445790' for Tesco PLC"),
|
|
178
|
-
},
|
|
179
|
-
}, async ({ company_number }) => {
|
|
180
|
-
try {
|
|
181
|
-
const data = await api(`/company/${company_number}/psc`);
|
|
182
|
-
return text(data);
|
|
183
|
-
}
|
|
184
|
-
catch (e) {
|
|
185
|
-
return err(String(e));
|
|
186
|
-
}
|
|
187
|
-
});
|
|
156
|
+
inputSchema: z.object({ company_number: companyNumber }),
|
|
157
|
+
}, async ({ company_number }) => run("get_psc", `/company/${company_number}/psc`));
|
|
188
158
|
server.registerTool("get_psc_chain", {
|
|
189
159
|
title: "Resolve PSC ownership chain to find ultimate beneficial owners",
|
|
190
160
|
description: "Trace the full ownership chain for a UK company by recursively following corporate " +
|
|
191
|
-
"entity PSCs. Returns a tree showing who ultimately controls the company
|
|
192
|
-
"(UBOs), foreign entities, or legal persons
|
|
161
|
+
"entity PSCs. Returns a tree showing who ultimately controls the company - natural persons " +
|
|
162
|
+
"(UBOs), foreign entities, or legal persons - along with why each branch terminated. " +
|
|
193
163
|
"Each node has a terminal_reason: natural_person, foreign_entity, legal_person, " +
|
|
194
164
|
"super_secure, depth_limit, not_found, cycle_detected, or psc_exempt. " +
|
|
195
165
|
"chain_metadata reports how many companies were resolved and the total API credit cost. " +
|
|
196
166
|
"Use this for KYB (Know Your Business) checks, AML screening, or any task requiring " +
|
|
197
167
|
"beneficial ownership beyond the immediate PSC layer.",
|
|
198
|
-
inputSchema: {
|
|
199
|
-
company_number:
|
|
200
|
-
.string()
|
|
201
|
-
.regex(/^[A-Z0-9]{1,8}$/)
|
|
202
|
-
.describe("Companies House company number, e.g. '12345678'"),
|
|
168
|
+
inputSchema: z.object({
|
|
169
|
+
company_number: companyNumber,
|
|
203
170
|
max_depth: z
|
|
204
171
|
.number()
|
|
205
172
|
.int()
|
|
@@ -208,17 +175,8 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
208
175
|
.optional()
|
|
209
176
|
.describe("Maximum chain depth to traverse (1-10, default 5). " +
|
|
210
177
|
"Each level costs 1 upstream API call per corporate entity found."),
|
|
211
|
-
},
|
|
212
|
-
}, async ({ company_number, max_depth }) => {
|
|
213
|
-
try {
|
|
214
|
-
const params = max_depth ? `?max_depth=${max_depth}` : "";
|
|
215
|
-
const data = await api(`/company/${company_number}/psc/chain${params}`);
|
|
216
|
-
return text(data);
|
|
217
|
-
}
|
|
218
|
-
catch (e) {
|
|
219
|
-
return err(String(e));
|
|
220
|
-
}
|
|
221
|
-
});
|
|
178
|
+
}),
|
|
179
|
+
}, async ({ company_number, max_depth }) => run("get_psc_chain", `/company/${company_number}/psc/chain${max_depth ? `?max_depth=${max_depth}` : ""}`));
|
|
222
180
|
server.registerTool("get_network", {
|
|
223
181
|
title: "Get director network",
|
|
224
182
|
description: "Map the corporate network connected to a UK company via shared directors. " +
|
|
@@ -226,11 +184,8 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
226
184
|
"depth. Each connected company includes its name, number, status, and the directors " +
|
|
227
185
|
"it shares with the focal company. Useful for identifying corporate group structures, " +
|
|
228
186
|
"related party relationships, and director interlocks.",
|
|
229
|
-
inputSchema: {
|
|
230
|
-
company_number:
|
|
231
|
-
.string()
|
|
232
|
-
.regex(/^[A-Z0-9]{1,8}$/)
|
|
233
|
-
.describe("Companies House company number, e.g. '00445790' for Tesco PLC"),
|
|
187
|
+
inputSchema: z.object({
|
|
188
|
+
company_number: companyNumber,
|
|
234
189
|
depth: z
|
|
235
190
|
.number()
|
|
236
191
|
.int()
|
|
@@ -239,17 +194,13 @@ export function createServer(apiKey, baseUrl = API_BASE) {
|
|
|
239
194
|
.optional()
|
|
240
195
|
.describe("Traversal depth: 1 = direct connections only, 2 = connections of connections (default 1). " +
|
|
241
196
|
"Depth 2 can return many results for large companies."),
|
|
242
|
-
},
|
|
243
|
-
}, async ({ company_number, depth }) => {
|
|
244
|
-
try {
|
|
245
|
-
const params = depth ? `?depth=${depth}` : "";
|
|
246
|
-
const data = await api(`/company/${company_number}/network${params}`);
|
|
247
|
-
return text(data);
|
|
248
|
-
}
|
|
249
|
-
catch (e) {
|
|
250
|
-
return err(String(e));
|
|
251
|
-
}
|
|
252
|
-
});
|
|
197
|
+
}),
|
|
198
|
+
}, async ({ company_number, depth }) => run("get_network", `/company/${company_number}/network${depth ? `?depth=${depth}` : ""}`));
|
|
253
199
|
return server;
|
|
254
200
|
}
|
|
201
|
+
/** A stdio-ready server for a single user's own API key. */
|
|
202
|
+
export function createServer(apiKey, baseUrl = API_BASE) {
|
|
203
|
+
const server = new McpServer({ name: "registrum", version: VERSION }, { capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS });
|
|
204
|
+
return registerTools(server, { apiKey, baseUrl });
|
|
205
|
+
}
|
|
255
206
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@registrum/mcp",
|
|
3
3
|
"mcpName": "io.github.vdmeu/registrum-mcp",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.1",
|
|
5
5
|
"description": "MCP server for the Registrum Companies House API — search UK companies, get financials, directors, beneficial ownership (PSC), and director networks",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -23,11 +23,19 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"author": "Eugene Merwe-Chartier",
|
|
25
25
|
"type": "module",
|
|
26
|
-
"main": "dist/
|
|
27
|
-
"module": "dist/
|
|
26
|
+
"main": "dist/server.js",
|
|
27
|
+
"module": "dist/server.js",
|
|
28
|
+
"types": "dist/server.d.ts",
|
|
28
29
|
"bin": {
|
|
29
30
|
"registrum-mcp": "dist/index.js"
|
|
30
31
|
},
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/server.d.ts",
|
|
35
|
+
"default": "./dist/server.js"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
31
39
|
"files": [
|
|
32
40
|
"dist"
|
|
33
41
|
],
|
|
@@ -40,17 +48,18 @@
|
|
|
40
48
|
"version": "node scripts/sync-server-json.mjs && git add server.json"
|
|
41
49
|
},
|
|
42
50
|
"dependencies": {
|
|
43
|
-
"@modelcontextprotocol/
|
|
44
|
-
"zod": "^
|
|
51
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
52
|
+
"zod": "^4.4.3"
|
|
45
53
|
},
|
|
46
54
|
"devDependencies": {
|
|
55
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
47
56
|
"@types/node": "^20",
|
|
48
57
|
"esbuild": "^0.27.3",
|
|
49
58
|
"typescript": "^5",
|
|
50
59
|
"vitest": "^3"
|
|
51
60
|
},
|
|
52
61
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
62
|
+
"node": ">=20"
|
|
54
63
|
},
|
|
55
64
|
"bugs": {
|
|
56
65
|
"url": "https://github.com/vdmeu/registrum-mcp/issues"
|