prh-mcp-server 1.0.0

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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PRH MCP Server — Finnish company registry.
4
+ * FREE public API. No authentication required.
5
+ * Look up any Finnish company: business ID, name, address, business lines, company form.
6
+ */
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { z } from "zod";
10
+ import { PrhClient } from "./prh-client.js";
11
+ const server = new McpServer({ name: "prh-mcp-server", version: "1.0.0" });
12
+ const client = new PrhClient();
13
+ function json(data) { return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] }; }
14
+ server.tool("search_companies", "Search Finnish companies by name, business ID, location, company form, or business line. Free public data from PRH (Finnish Patent and Registration Office).", {
15
+ name: z.string().optional().describe("Company name (partial match works)"),
16
+ businessId: z.string().optional().describe("Y-tunnus / Business ID (e.g. 0112038-9)"),
17
+ location: z.string().optional().describe("City (e.g. Helsinki, Tampere, Espoo)"),
18
+ companyForm: z.string().optional().describe("Company form: OY, OYJ, KY, AY, TMI, etc."),
19
+ mainBusinessLine: z.string().optional().describe("Business line code or text"),
20
+ postCode: z.string().optional().describe("Postal code"),
21
+ page: z.string().optional().describe("Page number (starts at 0)"),
22
+ }, async (p) => json(await client.searchCompanies(p)));
23
+ server.tool("get_company_by_id", "Look up a specific Finnish company by its exact business ID (Y-tunnus). Returns full details including all names, addresses, business lines, and registration info.", {
24
+ businessId: z.string().describe("Y-tunnus (e.g. 0112038-9 for Nokia Oyj)"),
25
+ }, async ({ businessId }) => json(await client.searchCompanies({ businessId })));
26
+ server.tool("get_code_descriptions", "Get PRH codeset descriptions (company forms, register types, etc.).", {
27
+ code: z.string().describe("Code: YRMU (company forms), TOIMI (business lines), KONK (bankruptcy status), etc."),
28
+ lang: z.string().optional().describe("Language: fi, en, sv (default: en)"),
29
+ }, async ({ code, lang }) => json(await client.getDescriptions(code, lang)));
30
+ server.tool("get_post_codes", "Get Finnish postal code directory.", {
31
+ lang: z.string().optional().describe("Language: fi, en, sv (default: fi)"),
32
+ }, async ({ lang }) => json(await client.getPostCodes(lang)));
33
+ async function main() { await server.connect(new StdioServerTransport()); }
34
+ main().catch((e) => { console.error("Server error:", e); process.exit(1); });
@@ -0,0 +1,14 @@
1
+ export declare class PrhClient {
2
+ private request;
3
+ searchCompanies(params: {
4
+ name?: string;
5
+ businessId?: string;
6
+ location?: string;
7
+ companyForm?: string;
8
+ mainBusinessLine?: string;
9
+ postCode?: string;
10
+ page?: string;
11
+ }): Promise<unknown>;
12
+ getDescriptions(code: string, lang?: string): Promise<unknown>;
13
+ getPostCodes(lang?: string): Promise<unknown>;
14
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * PRH (Finnish Patent and Registration Office) public API client.
3
+ * API docs: https://avoindata.prh.fi/
4
+ * Auth: NONE — this is free public data, no API key needed.
5
+ */
6
+ const BASE_URL = "https://avoindata.prh.fi/opendata-ytj-api/v3";
7
+ export class PrhClient {
8
+ async request(url, params) {
9
+ const u = new URL(url);
10
+ if (params)
11
+ for (const [k, v] of Object.entries(params)) {
12
+ if (v)
13
+ u.searchParams.set(k, v);
14
+ }
15
+ const res = await fetch(u.toString(), { headers: { Accept: "application/json" } });
16
+ if (!res.ok)
17
+ throw new Error(`PRH API error ${res.status}: ${await res.text()}`);
18
+ return res.json();
19
+ }
20
+ async searchCompanies(params) {
21
+ return this.request(`${BASE_URL}/companies`, params);
22
+ }
23
+ async getDescriptions(code, lang = "en") {
24
+ return this.request(`${BASE_URL}/description`, { code, lang });
25
+ }
26
+ async getPostCodes(lang = "fi") {
27
+ return this.request(`${BASE_URL}/post_codes`, { lang });
28
+ }
29
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "prh-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for PRH (Finnish Patent and Registration Office) — Look up Finnish company data, board members, financial reports. Free public API, no auth required.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "prh-mcp-server": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/index.js"
12
+ },
13
+ "keywords": [
14
+ "mcp",
15
+ "prh",
16
+ "finland",
17
+ "finnish",
18
+ "company-registry",
19
+ "yritysrekisteri",
20
+ "kaupparekisteri",
21
+ "model-context-protocol"
22
+ ],
23
+ "author": "Viggo Johansson",
24
+ "license": "MIT",
25
+ "type": "module",
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.29.0",
28
+ "zod": "^4.3.6"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^25.5.0",
32
+ "typescript": "^6.0.2"
33
+ }
34
+ }
package/src/index.ts ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PRH MCP Server — Finnish company registry.
4
+ * FREE public API. No authentication required.
5
+ * Look up any Finnish company: business ID, name, address, business lines, company form.
6
+ */
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { z } from "zod";
10
+ import { PrhClient } from "./prh-client.js";
11
+
12
+ const server = new McpServer({ name: "prh-mcp-server", version: "1.0.0" });
13
+ const client = new PrhClient();
14
+ function json(data: unknown) { return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] }; }
15
+
16
+ server.tool("search_companies", "Search Finnish companies by name, business ID, location, company form, or business line. Free public data from PRH (Finnish Patent and Registration Office).", {
17
+ name: z.string().optional().describe("Company name (partial match works)"),
18
+ businessId: z.string().optional().describe("Y-tunnus / Business ID (e.g. 0112038-9)"),
19
+ location: z.string().optional().describe("City (e.g. Helsinki, Tampere, Espoo)"),
20
+ companyForm: z.string().optional().describe("Company form: OY, OYJ, KY, AY, TMI, etc."),
21
+ mainBusinessLine: z.string().optional().describe("Business line code or text"),
22
+ postCode: z.string().optional().describe("Postal code"),
23
+ page: z.string().optional().describe("Page number (starts at 0)"),
24
+ }, async (p) => json(await client.searchCompanies(p)));
25
+
26
+ server.tool("get_company_by_id", "Look up a specific Finnish company by its exact business ID (Y-tunnus). Returns full details including all names, addresses, business lines, and registration info.", {
27
+ businessId: z.string().describe("Y-tunnus (e.g. 0112038-9 for Nokia Oyj)"),
28
+ }, async ({ businessId }) => json(await client.searchCompanies({ businessId })));
29
+
30
+ server.tool("get_code_descriptions", "Get PRH codeset descriptions (company forms, register types, etc.).", {
31
+ code: z.string().describe("Code: YRMU (company forms), TOIMI (business lines), KONK (bankruptcy status), etc."),
32
+ lang: z.string().optional().describe("Language: fi, en, sv (default: en)"),
33
+ }, async ({ code, lang }) => json(await client.getDescriptions(code, lang)));
34
+
35
+ server.tool("get_post_codes", "Get Finnish postal code directory.", {
36
+ lang: z.string().optional().describe("Language: fi, en, sv (default: fi)"),
37
+ }, async ({ lang }) => json(await client.getPostCodes(lang)));
38
+
39
+ async function main() { await server.connect(new StdioServerTransport()); }
40
+ main().catch((e) => { console.error("Server error:", e); process.exit(1); });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * PRH (Finnish Patent and Registration Office) public API client.
3
+ * API docs: https://avoindata.prh.fi/
4
+ * Auth: NONE — this is free public data, no API key needed.
5
+ */
6
+ const BASE_URL = "https://avoindata.prh.fi/opendata-ytj-api/v3";
7
+
8
+ export class PrhClient {
9
+ private async request<T>(url: string, params?: Record<string, string>): Promise<T> {
10
+ const u = new URL(url);
11
+ if (params) for (const [k, v] of Object.entries(params)) { if (v) u.searchParams.set(k, v); }
12
+ const res = await fetch(u.toString(), { headers: { Accept: "application/json" } });
13
+ if (!res.ok) throw new Error(`PRH API error ${res.status}: ${await res.text()}`);
14
+ return res.json() as Promise<T>;
15
+ }
16
+
17
+ async searchCompanies(params: {
18
+ name?: string;
19
+ businessId?: string;
20
+ location?: string;
21
+ companyForm?: string;
22
+ mainBusinessLine?: string;
23
+ postCode?: string;
24
+ page?: string;
25
+ }) {
26
+ return this.request<unknown>(`${BASE_URL}/companies`, params);
27
+ }
28
+
29
+ async getDescriptions(code: string, lang: string = "en") {
30
+ return this.request<unknown>(`${BASE_URL}/description`, { code, lang });
31
+ }
32
+
33
+ async getPostCodes(lang: string = "fi") {
34
+ return this.request<unknown>(`${BASE_URL}/post_codes`, { lang });
35
+ }
36
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true,
12
+ "types": ["node"]
13
+ },
14
+ "include": ["src/**/*"]
15
+ }