brreg-mcp-server

brreg-mcp-server

MCP server for querying the Norwegian Brønnøysundregistrene business register, including companies, roles, subunits, and annual accounts, with built-in guards against common data traps like retired NACE codes and withheld employee counts.

Category
Visit Server

README

brreg-mcp-server

The Norwegian business register for AI agents — companies, roles, subunits and annual accounts — with the register's silent traps encoded as guards.

License: MIT Node

Brønnøysundregistrene's API is free, public and keyless. Fetching from it is easy — a 40-line script does it, faster than any MCP will. The problem is that the register lies quietly, and every trap returns a plausible number instead of an error:

naeringskode=96.02   →  totalElements: 0        # "no hairdressers in Oslo"?
naeringskode=96.210  →  totalElements: 1283     # the code was renumbered. No error. No warning.

That is the whole reason this exists.

✨ Features

  • Annual accounts (regnskapsregisteret) — no other public brreg MCP covers them.
  • Retired-NACE guard — a zero result arrives with a hint naming the current code and its live match count. The query is never silently rewritten.
  • filed_no_revenue_line — holding companies file driftsinntekter: {} (the key is absent, not zero, and not ""). A revenue >= X filter silently deletes every one of them; this status is how you notice.
  • employees_min/employees_max — brreg hides every headcount below 5 from the payload, so filtering on the antallAnsatte field silently drops the small companies. The range filter sees them (219 Oslo hairdressers have 1–4 staff). antallAnsatte: null means withheld, not unknown.
  • INNH resolution — sole proprietorships have no board and no daglig leder. Looking only for DAGL/LEDE makes ~78% of Norwegian small business look contactless.
  • VAT-sector warning — registrertIMvaregisteret costs ~a third of results in 96.x/93.x and deletes ~98% of genuine clinics under NACE 86.* (health is VAT-exempt by law).
  • valuta never assumed — Equinor files in USD.
  • Privacy by default — no personal names unless you ask; birth dates never, even if you do.
  • Contact data — epostadresse, mobil, telefon, hjemmeside: sparse (~1 in 3 units) but real. For a sole proprietorship they are a private individual's personal details, and the tool says so.
  • Bulk by array — one call for many orgnrs, per-item partial success.
  • No cache, no state, no telemetry — one outbound host, data.brreg.no.

📋 Available Tools

Tool Does Key params
get_units Look up companies/branches by orgnr; resolves main-vs-branch for you and returns unit_type, contact fields, and is_natural_person orgnrs
search_units Search by industry, municipality, name, org form, VAT or employee range; carries the guards nace, kommune, navn, org_form, registrertIMvaregisteret, employees_min, employees_max, strict_location, cap
get_roles Board, management and sole-proprietor owner. Structure only by default orgnrs, include_persons
get_financials Annual accounts, as a discriminated union you must read before the numbers orgnrs, statement_type

🚀 Quick Start

npm install && npm run build
npm run dev:mock          # real offline mode — no network, every guard live

--mock is not a stub: it swaps the socket, not the code path, and its dataset is the register's traps (a USD filer, a holding company with no revenue line, an ENK, a dissolved unit, a 410, a retired NACE code). Everything on this page reproduces offline.

⌨️ CLI

The same handlers the MCP registers, over argv — src/cli.ts dispatches the identical buildTools() array, so a finding from one surface holds for the other.

brreg get_units --orgnrs 923609016
brreg search_units --nace 96.02 --kommune 0301     # watch it catch the retired code
brreg get_financials --orgnrs 918035443
brreg reference                                     # field glossary + statutory sources
brreg self-test --mock                              # proves the install works offline

stdout is one JSON object (with elapsed_sec); diagnostics on stderr; usage errors exit 2, upstream failures exit 1. --orgnrs a --orgnrs b and --orgnrs a,b both work — bulk is the intended shape.

📚 Skill

skills/brreg/SKILL.md carries the domain knowledge an agent can't infer from a successful response — which silences are meaningful and which plausible number is wrong. The tools' guards always fire; the skill is what lets an agent go past the four tools when the question needs it.

🖥️ Claude Desktop

Download the .mcpb from Releases and double-click it. No terminal, no Node install, no API key — brreg needs no credentials.

<details> <summary>Claude Code / Cursor / any stdio client</summary>

dist/ is not committed, so build it once:

git clone https://github.com/nordio-ai/brreg-mcp-server
cd brreg-mcp-server && npm ci && npm run build

.mcp.json is committed and works when the client's working directory is the repo:

{ "mcpServers": { "brreg": { "command": "node", "args": ["./dist/stdio.js"] } } }

From anywhere else, give an absolute path to dist/stdio.js — the relative form above resolves against the client's cwd, not this file. </details>

💬 Try it

"Look up 923609016 and tell me what the accounts say."

"Find hairdressers in Oslo, NACE 96.02." — watch it catch the retired code

"Which of these 50 companies has a real operator and filed accounts?"

🔍 Read this before trusting a number

You see It means
total: 0 Maybe not "none exist." Check hints — the NACE code may be retired.
driftsinntekter: null + filed_no_revenue_line Accounts filed, no operating revenue line. A holding company. Not zero revenue.
status: not_applicable An ENK. Sole proprietorships never file. Not a red flag.
antallAnsatte: null Withheld, not unknown — brreg never shows a value below 5. Use employees_min/employees_max to filter; antallAnsatte_reported says whether a hidden count exists.
valuta: "USD" It happens. Never compare revenue across companies without reading it.
deleted vs gone vs not_found Dissolved · removed on legal grounds · never existed. Three different facts.

Read brreg://reference for the field glossary (with statutory sources), the retired-NACE table, and role codes.

📂 Paths & state

None. No config, no cache, no logs on disk, no state directory. Every read is live.

That is deliberate. brreg's documentation treats HTTP 410 as "en forespørsel om at eventuelle kopier/cacher også fjerner den aktuelle enheten" — an instruction to purge, not a status code. A cache is exactly what turns that into an obligation you can silently fail: the request that would reveal the erasure is the one a cache promises not to make. For an interactive connector caching buys almost nothing, so there isn't any — and a test fails if someone adds one.

🔄 Updating

Re-download the .mcpb from Releases. Version lives in package.json; see CHANGELOG.md.

Scope

Interactive discovery and targeted enrichment — tens to a couple of hundred results. Register-scale extraction (thousands of rows) is a non-goal: an MCP returns data through the model's context window, so a script will always win at that size. Use the API directly.

Also out of scope: kunngjøringer (no open REST API exists), fødselsnummer (needs Maskinporten), writes (brreg is read-only), and anything feeding a creditworthiness decision (that engages EU AI Act Annex III 5(b)).

Licence

MIT. Not affiliated with Brønnøysundregistrene.

Data © Brønnøysundregistrene, NLOD 2.0. NLOD is a copyright licence and explicitly excludes personal data — it is not a legal basis for processing the names in this register. If you bulk-harvest role data, you are the controller: you need your own basis (GDPR Art 6(1)(f)), an Art 14 notice at first contact, and an Art 21(2) suppression list. And note that ENK names are people — foretaksnavneloven §2-2 requires a sole proprietorship's registered name to contain the owner's surname, and ~74% of the register are ENKs.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured