Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 2x 5x 5x 2x 1x 1x 1x 1x 1x 1x 2x 14x 8x 8x 8x 1x 7x 6x 8x 2x 4x 5x 5x 5x 4x 1x | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import * as z from "zod/v4";
import type { KvkClient } from "../kvk-client.js";
import type { SearchResult } from "../types.js";
import { toTextResult, toErrorResult } from "../tool-result.js";
const formatSearchResultAddress = (result: SearchResult): string | null => {
const adres = result.adres;
if (!adres) return null;
if (adres.binnenlandsAdres) {
const a = adres.binnenlandsAdres;
const street = [a.straatnaam, a.huisnummer !== undefined ? String(a.huisnummer) : null, a.huisletter]
.filter(Boolean)
.join(" ");
return [street, a.postcode, a.plaats].filter(Boolean).join(", ");
}
Eif (adres.buitenlandsAdres) {
const a = adres.buitenlandsAdres;
return [a.straatHuisnummer, a.postcodeWoonplaats, a.land].filter(Boolean).join(", ");
}
return null;
};
export const registerSearchTools = (server: McpServer, client: KvkClient): void => {
server.registerTool(
"search_companies",
{
title: "Search KVK Business Register",
description:
"Search the KVK (Kamer van Koophandel) business register by company name, KVK number, RSIN, vestigingsnummer, street name, city, postcode, or type. " +
"Returns a list of matching companies with basic information including KVK number, name, address, and active status. " +
"At least one search parameter must be provided. Uses whole-text matching — partial names will not return results.",
annotations: { readOnlyHint: true, openWorldHint: true },
inputSchema: z.object({
naam: z.string().optional().describe("Company or establishment name to search for. Uses whole-text matching."),
kvkNummer: z.string().regex(/^\d{8}$/).optional().describe("KVK number (8 digits)."),
rsin: z.string().regex(/^\d{9}$/).optional().describe("RSIN number (9 digits) — Legal Entities and Partnerships Identification Number."),
vestigingsnummer: z.string().regex(/^\d{12}$/).optional().describe("Vestigingsnummer / location number (12 digits)."),
straatnaam: z.string().optional().describe("Street name."),
plaats: z.string().optional().describe("City name."),
postcode: z.string().regex(/^[0-9]{4}[a-zA-Z]{2}$/).optional().describe("Postal code (e.g. 1234AB). Only in combination with huisnummer."),
huisnummer: z.number().int().min(1).optional().describe("House number. Only in combination with postcode."),
huisletter: z.string().max(1).optional().describe("House letter suffix. Only in combination with huisnummer."),
postbusnummer: z.number().int().min(1).optional().describe("PO box number. Only in combination with postcode."),
type: z.array(z.enum(["hoofdvestiging", "nevenvestiging", "rechtspersoon"])).optional().describe("Type of entity: hoofdvestiging (main location), nevenvestiging (branch), or rechtspersoon (legal entity)."),
inclusiefInactieveRegistraties: z.boolean().optional().describe("Include inactive registrations. Default is false."),
pagina: z.number().int().min(1).max(1000).default(1).describe("Page number (starts at 1, max 1000)."),
resultatenPerPagina: z.number().int().min(1).max(100).default(10).describe("Results per page (max 100)."),
}),
},
async ({ naam, kvkNummer, rsin, vestigingsnummer, straatnaam, plaats, postcode, huisnummer, huisletter, postbusnummer, type, inclusiefInactieveRegistraties, pagina, resultatenPerPagina }) => {
try {
const hasParam = naam ?? kvkNummer ?? rsin ?? vestigingsnummer ?? straatnaam ?? plaats ?? postcode ?? type;
if (!hasParam && huisnummer === undefined && postbusnummer === undefined) {
return toErrorResult(new Error("At least one search parameter must be provided (naam, kvkNummer, rsin, vestigingsnummer, straatnaam, plaats, postcode, or type)."));
}
const response = await client.search({
naam,
kvkNummer,
rsin,
vestigingsnummer,
straatnaam,
plaats,
postcode,
huisnummer,
huisletter,
postbusnummer,
type,
inclusiefInactieveRegistraties,
pagina,
resultatenPerPagina,
});
const results = response.resultaten ?? [];
if (results.length === 0) {
return toTextResult("No companies found matching the search criteria.");
}
const lines = results.map((r) => {
const address = formatSearchResultAddress(r);
const parts = [
`KVK: ${r.kvkNummer}`,
r.naam,
address,
r.type ? `(${r.type})` : null,
r.actief === "Nee" ? "[INACTIEF]" : null,
].filter(Boolean);
return ` - ${parts.join(", ")}`;
});
return toTextResult(
[
`Found ${response.totaal} result${response.totaal !== 1 ? "s" : ""} (page ${response.pagina}, ${response.resultatenPerPagina} per page)`,
...lines,
].join("\n"),
{
totaal: response.totaal,
pagina: response.pagina,
resultatenPerPagina: response.resultatenPerPagina,
resultaten: results,
} as Record<string, unknown>,
);
} catch (error) {
return toErrorResult(error);
}
},
);
};
|