Checking a Polish company means opening the court register, finding the right entity among similar names, and copying its number, board and address into whatever you were doing. We built krs-mcp so an AI assistant can do that lookup itself. krs-mcp is a free, MIT-licensed MCP server that lets Claude, Codex and other clients search the KRS by name, KRS, NIP or REGON and read official extracts, with no API key. Below: what the KRS holds and who needs it, the four tools, setup in three clients, one worked lookup, the caveat about name search, and the decisions behind the code.
The KRS is the register every Polish company check starts from
The National Court Register, Krajowy Rejestr Sądowy, is Poland’s official company register. As Wikipedia’s entry on the KRS summarizes it, the register was “Launched in 2001” and “is jointly administered by the Ministry of Justice and the regional courts”. Companies, associations, foundations and other organizations sit in it, each under a 10-digit KRS number, alongside the tax number (NIP) and the statistical number (REGON) that invoices and contracts actually carry.
Access is free. The Ministry of Justice’s KRS page goes as far as warning businesses about private registers that sell copies of KRS data, and reminds them that access to the register itself costs nothing.
The lookup itself is the chore. A finance team checks a new supplier’s NIP against its registered name before the first payment. A legal team needs to know who may sign for a counterparty and whether one board member can sign alone. Sales wants the registered address and line of business before a first call. Know-your-business (KYB) onboarding needs all of it, with a record of where it came from. The same extract answers all of them, once someone has turned the name they typed into the right KRS number.
krs-mcp gives an assistant four read-only tools
krs-mcp is an MCP server. The protocol lets a client such as Claude Code or Claude Desktop discover a server’s tools and call them through ordinary tool calling, so the model decides when to look something up and the server does the fetching. The README lists four tools:
| Tool | Input | Returns |
|---|---|---|
search_companies |
a name or partial name, or krs, nip, regon |
matching entities with padded 10-digit KRS numbers, city, registry, bankruptcy flag |
get_company |
krs, optional registry P or S |
the current extract (odpis aktualny) as markdown |
get_company_full |
same | the full extract (odpis pełny) as JSON, with every historical entry |
get_board |
same | only the board, supervisory bodies and proxies |
All four are read-only, need no account or configuration, and run on Node.js 22.12 or later. Each tool also declares readOnlyHint: true in its MCP annotations, so a client can tell before calling that nothing on the other side changes. The registry has two sections the search covers by default: P for entrepreneurs and S for associations.
Setup takes one config entry in Claude Desktop, Claude Code or Codex
The server runs locally over the stdio transport. The MCP transports specification defines it plainly: “The client launches the MCP server as a subprocess.” There is nothing to host and no port to open. npx downloads the published package from npm on first run.
For Claude Desktop, add the README’s entry to claude_desktop_config.json, which the MCP guide to local servers locates under ~/Library/Application Support/Claude/ on macOS:
{
"mcpServers": {
"krs-mcp": {
"command": "npx",
"args": ["-y", "krs-mcp"]
}
}
}
Claude Code and Codex each take one command. The syntax follows Claude Code’s MCP documentation and the Codex MCP guide, where everything after -- is the command that starts the server:
claude mcp add --transport stdio krs-mcp -- npx -y krs-mcp
codex mcp add krs-mcp -- npx -y krs-mcp
Restart the client, and the four tools appear in its tool list.
A lookup is two calls: search for the number, then read the extract
The extract API only answers to a KRS number, so the first call resolves the name. Ask the assistant for Soba Labs’ registered details and the model calls the search tool first:
{ "name": "search_companies", "arguments": { "query": "soba labs" } }
The result is one line per match: the padded KRS number, the registered name, the city and the registry. The model picks 0001245101 and calls get_company with it. The server fetches the official current extract, here the public Soba Labs record on the Ministry’s API, and returns markdown that opens like this:
# ODPIS AKTUALNY
As of: 15.06.2026
## Basic information
...
KRS: 0001245101
NIP: 8971973376
REGON: 54494280900000
...
Board, share capital and the PKD activity codes follow under their own headings. Because the model reads registry fields rather than recalling a company from training, a hallucinated NIP or a board member from three years ago has nowhere to come from. If only the people matter, get_board returns that section alone.
Name search rides on an undocumented endpoint, so a daily check watches it
The two calls above hit two different services. Extracts come from the Ministry’s official, keyless API at api-krs.ms.gov.pl. That API has no name search, so search_companies uses the endpoint behind the Ministry’s public search website, wyszukiwarka-krs.ms.gov.pl. The README is direct about what that means: the endpoint “is not part of the documented KRS Open API and may change or reject programmatic access without notice.”
A GitHub Actions workflow runs every day at 05:27 UTC, searches for one known public record (our own, KRS 0001245101) and fetches its extract. A changed response contract or an access failure opens or updates a single incident issue; a temporary upstream outage fails the run without opening one. A 403 from search means the Ministry changed its access controls, and the repo’s rules say that needs human review: “Maintainers and repair agents must not bypass new access controls automatically.” If search ever breaks for good, get_company, get_company_full and get_board keep working for anyone who already has the number.
The rule: when the search service starts refusing requests, krs-mcp reports the
403and a person decides what happens next. Code that slips past a new access control is the one fix we will not ship.
Inside the server, every HTTP request gets 15 seconds, the tool-side share of the agent’s own timeout budget. Network errors, HTTP 429 and server errors are retried twice, after 250 ms and then 750 ms, and any other 4xx returns at once. We kept this tool-side layer of the retry policy short on purpose, because an assistant waiting on a tool is a person waiting on an answer. A 500 ms pacer also spaces requests to each service, which matters when a model decides to look up twenty suppliers in a row.
Every result carries its provenance, because the reuse terms require it
Each successful tool result comes back as two text blocks. The first is the answer. The second is a small JSON block:
{
"source": "https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/...",
"sourceProducedAt": null,
"retrievedAt": "2026-11-11T10:30:00.000Z",
"processing": "Current extract formatted as Markdown by krs-mcp."
}
The Ministry’s conditions for reusing public-sector information include the duty to state the source, the time the information was produced and obtained, and any processing applied to it. The Ministry’s services do not supply a separate production time today, so the server writes null rather than inventing one. For a KYB file, this is the traceability record you would have to keep by hand otherwise.
Why we built it read-only, with markdown for current extracts and JSON for full ones
Four decisions shaped the code. The first three came from watching how an assistant actually uses a tool.
Read-only is the whole scope. The KRS accepts filings, but an assistant that could file changes to a court register would be the wrong tool for a lookup job. Four read tools and no write path keep the server inside least privilege by construction, so no call ever needs a human to approve it. The server also sends requests only to the Ministry’s KRS services, keeps no telemetry and stores no responses.
Markdown for reading, JSON for history. The raw extract is nested Polish JSON built for machines. get_company walks it by path and prints compact markdown sections, which spends fewer tokens and reads like a document. The full extract is different: it keeps every historical entry, and flattening it would throw away exactly the history someone asked for. So get_company_full passes the JSON through untouched, so a script can still parse it. It is also large. For our own young record, the full JSON is already 12,982 bytes against 6,553 for the current extract’s JSON, about 2x, so the tool description tells the model to prefer get_company and spare its context window.
Forgive the model’s guesses. Client models often guess name or the Polish nazwa where the search tool expects query, so the input schema accepts both as aliases, treats empty strings as absent, and asks for at least one criterion instead of failing on a wrong field name.
The live check against our own public record is the fourth decision, made for the long run. The test suite runs on synthetic fixtures, which can show the parser works but never that the Ministry still answers the same way. We chose our own record for the real request because it is public and ours, so the check never touches anyone else’s data.
krs-mcp is on npm and in the official MCP Registry
The package is on npm under the same name, first released as 0.1.0 on 11 November 2026, and listed in the official MCP Registry as io.github.soba-labs/krs-mcp, an npm package over stdio. The source, tests and issue tracker live in the GitHub repository above, and the project sits with our other public tools on our open-source page.
If you plan to run it inside a larger agent, two of our posts cover the surrounding work: MCP built into LangChain for wiring servers into an agent, and MCP security in production for what to check before any MCP server gets near real data.
The takeaway
Looking up a Polish company is a two-step job, name to KRS number and number to extract, and krs-mcp hands both steps to your assistant with four read-only tools, no key and a provenance block on every answer. The extract side rests on the Ministry’s documented API; the name search does not, and a daily check against our own record tells us the day that changes. Add one config entry, ask about a company, and read the source URL that comes back with the answer.
Sources
- krs-mcp on GitHub
- National Court Register (Wikipedia)
- National Court Register page, Ministry of Justice
- MCP specification: transports
- krs-mcp on npm
- Connect to local MCP servers (MCP documentation)
- Connect Claude Code to tools via MCP
- Codex MCP guide
- Soba Labs current extract, KRS Open API
- KRS search website, Ministry of Justice
- Reuse of public-sector information, Ministry of Justice
- krs-mcp in the official MCP Registry