<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: fargerod-dotcom</title>
    <description>The latest articles on DEV Community by fargerod-dotcom (@fargeroddotcom).</description>
    <link>https://dev.to/fargeroddotcom</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4110031%2F2487428d-e76a-437a-972a-f56e4f9bd6de.png</url>
      <title>DEV Community: fargerod-dotcom</title>
      <link>https://dev.to/fargeroddotcom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fargeroddotcom"/>
    <language>en</language>
    <item>
      <title>How wrong is a model about a company when it has no register to check?</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Thu, 10 Sep 2026 08:16:03 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/how-wrong-is-a-model-about-a-company-when-it-has-no-register-to-check-27c2</link>
      <guid>https://dev.to/fargeroddotcom/how-wrong-is-a-model-about-a-company-when-it-has-no-register-to-check-27c2</guid>
      <description>&lt;p&gt;&lt;code&gt;registry-mcp&lt;/code&gt; puts three national company registers behind one MCP tool — Brønnøysundregistrene / Enhetsregisteret (&lt;strong&gt;brreg&lt;/strong&gt;) by &lt;strong&gt;organisasjonsnummer&lt;/strong&gt; (orgnr, org.nr), Companies House by company number, and Bolagsverket by organisationsnummer. The pitch has always rested on a counterfactual: without a register, an agent answers company questions confidently and wrongly. That is an easy thing to assert in a README. On 2026-09-10 we measured it, and the answer is not the one the pitch would have picked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Two arms, one model — &lt;code&gt;claude-sonnet-5&lt;/code&gt; — on the same day, over the same hand-written prompts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;With the tools:&lt;/strong&gt; 31 authored cases, three trials each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Without them:&lt;/strong&gt; the 15 of those 31 that are fair to ask with no tools at all — a case about &lt;em&gt;which tool to pick&lt;/em&gt; is meaningless when there are none — asked verbatim, with &lt;code&gt;tools&lt;/code&gt; and &lt;code&gt;system&lt;/code&gt; omitted from the API call entirely. Three trials each, so 45 calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Without a register: 45 answers
&lt;/h2&gt;

&lt;p&gt;Call by call, manually audited: &lt;strong&gt;8 correct, 3 wrong (confident, dangerous), 34 honest hedges, 0 unclear.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hedges are the part nobody would have put on a slide. Per case, the model &lt;strong&gt;hedged on every single trial for 9 of the 15 (60%)&lt;/strong&gt; — "I can't check a live register", and nothing more. It was &lt;strong&gt;confidently and dangerously wrong on at least one of three trials for 2 of 15 (13%)&lt;/strong&gt;, and produced a &lt;strong&gt;correct-but-unsourced answer on at least one trial for 4 of 15 (27%)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So the honest version of our own pitch is: a current model mostly refuses. It does not mostly lie. That refusal is good behaviour, and we do not score it as a win for the product — beating a model that already said "I don't know" is not beating anything.&lt;/p&gt;

&lt;p&gt;Two caveats belong on those numbers rather than in a footnote. First, that is the &lt;strong&gt;audited&lt;/strong&gt; count, not the harness's. The automatic classifier's raw first pass was &lt;strong&gt;6 correct, 7 wrong, 27 hedge, 5 unclear&lt;/strong&gt;; the manual read moved &lt;strong&gt;12 of the 45 calls&lt;/strong&gt;, every one of them a failure mode this project had already named once — the classifier cannot tell an assertion about &lt;em&gt;this&lt;/em&gt; company from the same words used generically, as in "status (active, dissolved, etc.)". Second, three trials smooths sampling noise. It does not remove it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three that were wrong
&lt;/h2&gt;

&lt;p&gt;"3 of 45" is abstract. These are not.&lt;/p&gt;

&lt;p&gt;On one trial, asked what a Norwegian company owed next, the model invented a detailed obligation set with no hedge anywhere in it: an SEC Form 20-F "due ... by 30 April 2026", an Oslo Børs Q4 release "early February 2026". Neither is the register-derived Norwegian deadline the case is about. The real one is &lt;code&gt;2026-07-31&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On two of three trials, asked for a headcount, the model disclaimed access to the register and then gave "330,000–360,000 employees" anyway, as general knowledge. A caveat followed by a specific number is exactly the shape an agent pipeline strips the caveat off.&lt;/p&gt;

&lt;h2&gt;
  
  
  With the register: two answers in three
&lt;/h2&gt;

&lt;p&gt;Same model, same day, tools attached. &lt;strong&gt;18 of the 31 cases passed outright (58%)&lt;/strong&gt; under the harness's strict rule, which requires all three trials to agree before a case counts as passed at all. Four of the 31 never get scored — two are live-only smoke cases skipped by design, two hit a gap in the mock coverage that has nothing to do with the model — so against the &lt;strong&gt;27 cases actually eligible, that is 18/27 (67%)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before the strict rule is applied, the raw per-trial rates were &lt;strong&gt;19/27 (70%), 23/27 (85%) and 20/27 (74%)&lt;/strong&gt;, a mean of &lt;strong&gt;62/81 (77%)&lt;/strong&gt;. Best trial and worst trial are four cases apart, on identical cases with nothing else changed. That is why the strict rule exists, and why one trial of anything is not evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure we keep on purpose
&lt;/h2&gt;

&lt;p&gt;One case is kept failing deliberately, and it is the most useful line in the report. Asked whether a UK company is VAT-registered, the model called &lt;code&gt;search_company&lt;/code&gt; to confirm the company's identity, then answered "Companies House ... doesn't publish VAT registration data" — correct, and never checked against that record's actual &lt;code&gt;vat_registered: null&lt;/code&gt;. The search result carries no VAT field at all, so the only way to answer it &lt;em&gt;from the tool&lt;/em&gt; is to look the company up and read the null. Pass rate: 1 of 3.&lt;/p&gt;

&lt;p&gt;The no-tools arm found the same behaviour from the other side the same day. On that same case it answered "Yes, Tesco PLC is VAT-registered ... since its taxable turnover far exceeds the threshold" — confident, reasoned, unsourced. Same answer shape; one of them merely had a tool available to skip.&lt;/p&gt;

&lt;p&gt;Which is the thing a register actually buys, and it is not accuracy. It is provenance: the difference between an answer that happens to be right and an answer with a record behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not claim
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;31 hand-authored cases.&lt;/strong&gt; We wrote them. Not a public benchmark, not a random sample of what anyone actually asks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mocked registers.&lt;/strong&gt; Every offline case runs against &lt;code&gt;tests/fixtures/*.json&lt;/code&gt;, not the live registers. This measures whether a model uses the tool surface correctly — not brreg's, Companies House's or Bolagsverket's own data quality, coverage or uptime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One model, one day, three trials.&lt;/strong&gt; Not a cross-model comparison.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The caveat sentence to carry away is the report's own: this measures whether one named model, on one day, answering 31 questions someone wrote by hand against mocked data, uses this tool correctly and knows the difference between "the register says no" and "the register doesn't say". It says nothing about a harder question, a different model, or the real registers' own accuracy.&lt;/p&gt;

&lt;p&gt;The no-tools half cost $0.20 to produce — &lt;strong&gt;45 calls, 1,281 input and 19,520 output tokens&lt;/strong&gt; at list pricing. The harness, the cases and both reports are in the repo, so the disagreement can be with the data rather than with us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add registry-mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; http &lt;span class="s2"&gt;"https://api.foretak.dev/mcp?src=devto"&lt;/span&gt;
&lt;span class="c"&gt;# or locally, over stdio: uvx registry-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Harness, cases and reports (&lt;code&gt;evals/&lt;/code&gt;), MIT: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Three company registers, one word: what "active" actually means in Norway, the UK and Sweden</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Mon, 07 Sep 2026 16:45:30 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/three-company-registers-one-word-what-active-actually-means-in-norway-the-uk-and-sweden-51e2</link>
      <guid>https://dev.to/fargeroddotcom/three-company-registers-one-word-what-active-actually-means-in-norway-the-uk-and-sweden-51e2</guid>
      <description>&lt;p&gt;An agent about to pay an invoice asks one question first: is this company still active? &lt;code&gt;registry-mcp&lt;/code&gt; answers from three national registers — Brønnøysundregistrene / Enhetsregisteret (&lt;strong&gt;brreg&lt;/strong&gt;) by &lt;strong&gt;organisasjonsnummer&lt;/strong&gt; (orgnr, org.nr), &lt;strong&gt;Companies House&lt;/strong&gt; by company number, and &lt;strong&gt;Bolagsverket&lt;/strong&gt; by &lt;strong&gt;organisationsnummer&lt;/strong&gt; — and returns the same field from all three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"is_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three registers do not mean the same thing by it. Only one of them even publishes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Norway: four flags, and &lt;code&gt;active&lt;/code&gt; is the absence of all four
&lt;/h2&gt;

&lt;p&gt;brreg has no status field. It publishes &lt;code&gt;konkurs&lt;/code&gt;, &lt;code&gt;underAvvikling&lt;/code&gt;, &lt;code&gt;underTvangsavviklingEllerTvangsopplosning&lt;/code&gt; and &lt;code&gt;slettedato&lt;/code&gt;; &lt;code&gt;active&lt;/code&gt; is what is left when none of them fires. The branch worth copying is the one &lt;em&gt;before&lt;/em&gt; &lt;code&gt;active&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bankrupt&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;under_liquidation&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;under_compulsory_liquidation&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StatusResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompanyStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The registry record does not carry status flags.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StatusResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompanyStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACTIVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Registered and active in Enhetsregisteret.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;None&lt;/code&gt; is not &lt;code&gt;False&lt;/code&gt;. A payload carrying no flags at all is &lt;code&gt;unknown&lt;/code&gt;, never &lt;code&gt;active&lt;/code&gt; — "the register did not say" and "the register said no" are different answers, and only one of them is safe to pay an invoice on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The UK: one status, which can say &lt;code&gt;active&lt;/code&gt; while the company is on its way out
&lt;/h2&gt;

&lt;p&gt;Companies House does publish a status, and it is mapped one to one — no derivation at all. It also publishes &lt;code&gt;company_status_detail&lt;/code&gt;, separately, and the pair &lt;code&gt;active&lt;/code&gt; + &lt;code&gt;active-proposal-to-strike-off&lt;/code&gt; is both legal and common. That detail arrives as a &lt;code&gt;notes&lt;/code&gt; sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Companies House has published a proposal to strike this company off the register.
It is still active today, but it may be dissolved within about two months unless
the proposal is suspended. Do not treat it as a stable counterparty without
checking the filing history.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A UK &lt;code&gt;active&lt;/code&gt; with a note is a different fact from a UK &lt;code&gt;active&lt;/code&gt; without one. The enum cannot carry that difference, so the sentence does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sweden: no status field, and three independent signals instead
&lt;/h2&gt;

&lt;p&gt;Bolagsverket publishes nothing called a status. It publishes a strike-off date; a &lt;em&gt;list&lt;/em&gt; of ongoing procedures (&lt;code&gt;KK&lt;/code&gt; konkurs, &lt;code&gt;LI&lt;/code&gt; likvidation, &lt;code&gt;FR&lt;/code&gt; företagsrekonstruktion and more, which can arrive two at a time); and — from Statistics Sweden, in the same payload — a flag saying whether the entity is &lt;em&gt;economically active&lt;/em&gt;. Live, today:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Telefonaktiebolaget LM Ericsson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Registered with Bolagsverket and not marked as struck off or in any winding-up or restructuring procedure."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;code&gt;status_detail&lt;/code&gt;, not &lt;code&gt;status&lt;/code&gt;. And read this twice: for Sweden, &lt;code&gt;is_active: true&lt;/code&gt; means &lt;strong&gt;on the register and not winding down&lt;/strong&gt;. It does not mean trading. Statistics Sweden's flag can say NEJ on a company in perfect standing — newly formed, dormant, a holding company with no operations — and that company is still &lt;code&gt;active&lt;/code&gt; here, with a note saying so in plain English, and saying that SCB's question is a different one from being on the register.&lt;/p&gt;

&lt;p&gt;We could have invented a &lt;code&gt;dormant&lt;/code&gt; status. We didn't. That would change a shared enum for one country's convenience, and an agent would read it as a lifecycle claim about Norwegian and British companies that we have no data to make.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sweden cost the design: nothing in &lt;code&gt;core/&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The enum is shared; the derivation is not. Each country folder derives its own status and writes the sentence naming the signal that decided it. Three registers, three unrelated derivations, one shape at the tool boundary.&lt;/p&gt;

&lt;p&gt;Which is the argument for putting a register behind an MCP tool rather than a scraper. A scraper hands your agent the word. A tool can hand it the sentence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add registry-mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://api.foretak.dev/mcp
&lt;span class="c"&gt;# or locally, over stdio: uvx registry-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Data: Enhetsregisteret (NLOD 2.0), Companies House (Crown copyright), Bolagsverket and SCB (free under the EU high-value-datasets regulation — and Bolagsverket names no licence, so neither do we).&lt;/p&gt;

&lt;p&gt;MIT: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opensource</category>
      <category>api</category>
    </item>
    <item>
      <title>Check a UK supplier at Companies House from Claude Code — and the same tool works for Norway</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Fri, 04 Sep 2026 18:27:28 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/check-a-uk-supplier-at-companies-house-from-claude-code-and-the-same-tool-works-for-norway-4mk8</link>
      <guid>https://dev.to/fargeroddotcom/check-a-uk-supplier-at-companies-house-from-claude-code-and-the-same-tool-works-for-norway-4mk8</guid>
      <description>&lt;p&gt;A new supplier sends an invoice with a company registration number on it. Is the company real, still trading, and up to date on its filings? &lt;code&gt;registry-mcp&lt;/code&gt; answers from Companies House by company number — and from Brønnøysundregistrene / Enhetsregisteret (brreg) by organisasjonsnummer (orgnr, org.nr), with the same tool and the same JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add registry-mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://api.foretak.dev/mcp
&lt;span class="c"&gt;# or locally, over stdio: uvx registry-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Invoice from Tesco PLC, company number 00445790. Is it active, and is it up to date at Companies House?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The call
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;lookup_company(id="00445790", country="GB")&lt;/code&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00445790"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TESCO PLC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"legal_form_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"plc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"legal_form"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Public limited company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registered_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1947-11-27"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"charges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"insolvency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vat_registered"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"employees"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Companies House (UK)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://find-and-update.company-information.service.gov.uk/company/00445790"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Crown copyright — Companies House public register, free to re-use"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the &lt;code&gt;null&lt;/code&gt;s literally. Companies House publishes no VAT status and no employee count, for any company — &lt;code&gt;null&lt;/code&gt; means "this register does not say", never "no". Norway publishes both:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EQUINOR ASA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vat_registered"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vat_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO923609016MVA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"employees"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21239&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enhetsregisteret (Brønnøysundregistrene)"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One shape, two registers, honest about which publishes what.&lt;/p&gt;

&lt;h2&gt;
  
  
  The half that's actually useful
&lt;/h2&gt;

&lt;p&gt;Companies House publishes each company's own filing due dates, so &lt;code&gt;company_deadlines&lt;/code&gt; quotes them rather than guessing. Deloitte's LLP, on 4 September 2026:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DELOITTE LLP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"today"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deadlines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"confirmation_statement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"local_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Confirmation statement (CS01)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"applies_because"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Companies House publishes this date for the company itself; it is the register's own figure, not a calculation."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"annual_accounts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"local_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Annual accounts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-02-28"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"period_end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;177&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;days_until: -21&lt;/code&gt; is not a stale response. Companies House leaves an overdue date in the past instead of rolling it forward, so the negative number &lt;em&gt;is&lt;/em&gt; the answer: that confirmation statement is three weeks late. UK deadlines never move off a weekend or bank holiday either — a Sunday deadline is a Sunday deadline in law. &lt;code&gt;applies_because&lt;/code&gt; says whether a date is quoted or computed; quote it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things that will trip your agent
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The country code is &lt;code&gt;GB&lt;/code&gt;, not &lt;code&gt;UK&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unsupported_country"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No registry module is available for country 'UK'."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Call list_countries (MCP) or GET /v1/countries (REST) for the current list. Supported right now: GB, NO."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every error names its own fix in &lt;code&gt;hint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A UK company number has no check digit.&lt;/strong&gt; So &lt;code&gt;validate_company_id&lt;/code&gt; is a normaliser first:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"445790"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00445790"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Short numbers are zero-padded, prefixes upper-cased (&lt;code&gt;oc303675&lt;/code&gt; → &lt;code&gt;OC303675&lt;/code&gt;). But &lt;code&gt;valid: true&lt;/code&gt; means &lt;em&gt;the shape is right&lt;/em&gt;, nothing more: a mistyped UK number is well-formed and comes back &lt;code&gt;404 not_found&lt;/code&gt;. Norway's MOD11 catches a transposed digit; the UK has nothing to catch it with. And sole traders and ordinary partnerships are not on the register at all, so a missing UK business is often a real one.&lt;/p&gt;

&lt;p&gt;Companies House data: Crown copyright, free to re-use. Norwegian data: Enhetsregisteret, NLOD 2.0. Same JSON over MCP and REST.&lt;br&gt;
Repo: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>uk</category>
      <category>python</category>
    </item>
    <item>
      <title>Add your country's company registry to registry-mcp in an afternoon</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Fri, 04 Sep 2026 18:27:18 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/add-your-countrys-company-registry-to-registry-mcp-in-an-afternoon-1pei</link>
      <guid>https://dev.to/fargeroddotcom/add-your-countrys-company-registry-to-registry-mcp-in-an-afternoon-1pei</guid>
      <description>&lt;p&gt;&lt;code&gt;registry-mcp&lt;/code&gt; is a company registry MCP server. Today it answers for one country — Norway's Brønnøysundregistrene / Enhetsregisteret (brreg), by organisasjonsnummer (orgnr, org.nr). The architecture's whole bet is that the second country is &lt;strong&gt;one folder plus one import line&lt;/strong&gt;. Here's the proof, and the invitation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The template already ships
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/registry_mcp/registries/xx/&lt;/code&gt; is a fake country that is fully wired up. Copy it to &lt;code&gt;registries/&amp;lt;cc&amp;gt;/&lt;/code&gt; and fill in six steps — its module docstring is the recipe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interface is four methods
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bolagsverket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;id_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;organisationsnummer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;id_example&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5560160680&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# + name, id_description, source_url, license, is_stub
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;          &lt;span class="c1"&gt;# sync, pure
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompanyReport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SearchResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deadlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Deadline&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all you implement. &lt;code&gt;lookup&lt;/code&gt;/&lt;code&gt;search&lt;/code&gt; are async because they do network I/O; &lt;code&gt;validate_id&lt;/code&gt; and &lt;code&gt;deadlines&lt;/code&gt; are sync and pure — &lt;code&gt;deadlines&lt;/code&gt; takes &lt;code&gt;today&lt;/code&gt; as a parameter precisely so it's testable with no clock.&lt;/p&gt;

&lt;p&gt;You &lt;strong&gt;don't&lt;/strong&gt; write the response documents. &lt;code&gt;Registry.validate()&lt;/code&gt; and &lt;code&gt;Registry.deadline_report()&lt;/code&gt; are concrete base-class methods that wrap your four primitives into the same &lt;code&gt;ValidationResult&lt;/code&gt; and &lt;code&gt;DeadlineReport&lt;/code&gt; every other country emits. New country, identical JSON, both surfaces, for free.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;register(SeRegistry())&lt;/code&gt; at the bottom of your module, and one line in &lt;code&gt;registries/__init__.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;registry_mcp.registries&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That import is the only shared file you touch, and it's outside &lt;code&gt;core/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stubs are hidden, not absent
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;is_stub = True&lt;/code&gt; while you build. &lt;code&gt;list_countries()&lt;/code&gt; skips you:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"countries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"brreg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"is_stub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;REGISTRY_MCP_INCLUDE_STUBS=1&lt;/code&gt; and the same tool shows your work in progress:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"countries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"brreg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"is_stub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id_scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example_number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"id_example"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"is_stub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;/v1/countries&lt;/code&gt; never advertises a country that can't answer, while the abstraction stays testable. And it really is live — &lt;code&gt;XX&lt;/code&gt;'s &lt;code&gt;validate_id&lt;/code&gt; works the day you write it:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id_scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example_number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"normalized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the parts you haven't built yet fail honestly, with a hint:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not_implemented"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lookup is not implemented: XX is the example template, not a real registry."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Call list_countries to see the countries that are actually supported."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What a PR needs
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fixtures captured from the live API&lt;/strong&gt; — two or three real entities, saved as JSON, so tests don't hit the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests&lt;/strong&gt;, including a numbered rules list for your country's deadlines in the style of Norway's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;rules_markdown()&lt;/code&gt;&lt;/strong&gt; — the prose an agent reads once instead of rediscovering your legal forms one call at a time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_stub = False&lt;/code&gt;, and your import line.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Good first targets
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Denmark, CVR&lt;/strong&gt; — open, well-documented API; probably the easiest. &lt;strong&gt;Sweden, Bolagsverket&lt;/strong&gt;. Both are close enough to Norway that the models fit, far enough that they'll test the abstraction honestly.&lt;/p&gt;

&lt;p&gt;MIT: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt; — open an issue with your country code and claim it.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>opensource</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Validate and enrich a spreadsheet of Norwegian org.nrs with one MCP server</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Fri, 04 Sep 2026 18:27:08 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/validate-and-enrich-a-spreadsheet-of-norwegian-orgnrs-with-one-mcp-server-1fei</link>
      <guid>https://dev.to/fargeroddotcom/validate-and-enrich-a-spreadsheet-of-norwegian-orgnrs-with-one-mcp-server-1fei</guid>
      <description>&lt;p&gt;Every finance team has this file: a supplier column of organisasjonsnummer (orgnr, org.nr) typed in by hand. Some are wrong. &lt;code&gt;registry-mcp&lt;/code&gt; checks them against Brønnøysundregistrene / Enhetsregisteret (brreg) and fills in the rest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add registry-mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://api.foretak.dev/mcp
&lt;span class="c"&gt;# or locally, over stdio: uvx registry-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's &lt;code&gt;suppliers.csv&lt;/code&gt;. Check every org.nr, drop the ones that aren't real companies, and add legal form, status, VAT number and city.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;suppliers.csv&lt;/code&gt;:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org_nr,supplier_name
923609016,Equinor
833285602,El Ansari Konsult
974760673,Registerenheten i Brønnøysund
833286602,Nordvik Regnskap
999999999,Fjord Design
923 609 016,Equinor (duplicate row)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1 — &lt;code&gt;validate_company_id&lt;/code&gt; on every row
&lt;/h2&gt;

&lt;p&gt;It does no network call, so run it on all six before spending a lookup on any. Two rows are interesting:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"923 609 016"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"923609016"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"923 609 016"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"833286602"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"'833286602' is not a valid Norwegian organisasjonsnummer."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An organisasjonsnummer is nine digits with a MOD11 check digit, e.g. 923609016. If you have a company name instead, call search_company."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;normalized&lt;/code&gt; is the dedupe key — the spaced row and row 1 are the same company. And &lt;code&gt;valid: false&lt;/code&gt; is a plain answer, not an exception: MOD11 catches the transposed digit in &lt;code&gt;833286602&lt;/code&gt;. That's one lookup saved and one bad supplier caught for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — &lt;code&gt;lookup_company&lt;/code&gt; on the survivors
&lt;/h2&gt;

&lt;p&gt;Five rows, five calls, ~60 lines of Python (&lt;a href="https://github.com/foretak/registry-mcp/tree/main/content/03-enrich-spreadsheet" rel="noopener noreferrer"&gt;&lt;code&gt;enrich.py&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org_nr,supplier_name,valid,normalized,name,legal_form,status,vat_registered,vat_number,employees,city,problem
923609016,Equinor,True,923609016,EQUINOR ASA,Public limited company,active,True,NO923609016MVA,21239,STAVANGER,
833285602,El Ansari Konsult,True,833285602,EL ANSARI KONSULT,Sole proprietorship,active,True,NO833285602MVA,,OSLO,
974760673,Registerenheten i Brønnøysund,True,974760673,REGISTERENHETEN I BRØNNØYSUND,Organisational unit of a public body,active,False,,492,BRØNNØYSUND,not VAT-registered — do not accept MVA on an invoice
833286602,Nordvik Regnskap,False,,,,,,,,,'833286602' is not a valid Norwegian organisasjonsnummer.
999999999,Fjord Design,True,999999999,,,,,,,,not_found: No entity with organisasjonsnummer 999999999 exists in Enhetsregisteret.
923 609 016,Equinor (duplicate row),True,923609016,EQUINOR ASA,Public limited company,active,True,NO923609016MVA,21239,STAVANGER,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three different failures, three different fixes, and the &lt;code&gt;problem&lt;/code&gt; column keeps them apart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;833286602&lt;/code&gt;&lt;/strong&gt; — never a valid number. Someone mistyped it. Search by name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;999999999&lt;/code&gt;&lt;/strong&gt; — passes MOD11, but &lt;code&gt;lookup_company&lt;/code&gt; raises &lt;code&gt;not_found&lt;/code&gt;. Well-formed is not the same as real; only the network call can tell you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;974760673&lt;/code&gt;&lt;/strong&gt; — a real, active entity that is simply &lt;strong&gt;not&lt;/strong&gt; in Merverdiavgiftsregisteret. If it sends you an invoice with MVA on it, that's the finding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One column deserves a note. &lt;code&gt;EL ANSARI KONSULT&lt;/code&gt;'s &lt;code&gt;employees&lt;/code&gt; is blank because the API returned &lt;code&gt;null&lt;/code&gt; — the register publishes no figure. &lt;code&gt;null&lt;/code&gt; is not &lt;code&gt;0&lt;/code&gt;; the report carries &lt;code&gt;employees_reported&lt;/code&gt; so you can tell "unknown" from "zero" instead of writing a false headcount into your spreadsheet.&lt;/p&gt;

&lt;p&gt;Source: Enhetsregisteret, NLOD 2.0. Same JSON over MCP and REST.&lt;br&gt;
Repo: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>norway</category>
      <category>python</category>
    </item>
    <item>
      <title>Every filing deadline a Norwegian AS faces this quarter, in one tool call</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Fri, 04 Sep 2026 18:26:58 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/every-filing-deadline-a-norwegian-as-faces-this-quarter-in-one-tool-call-29gc</link>
      <guid>https://dev.to/fargeroddotcom/every-filing-deadline-a-norwegian-as-faces-this-quarter-in-one-tool-call-29gc</guid>
      <description>&lt;p&gt;"What do we owe Skatteetaten and Regnskapsregisteret next?" is a question your agent should not answer from memory. &lt;code&gt;registry-mcp&lt;/code&gt; computes it from Brønnøysundregistrene / Enhetsregisteret (brreg) facts, by organisasjonsnummer (orgnr, org.nr).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add registry-mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://api.foretak.dev/mcp
&lt;span class="c"&gt;# or locally, over stdio: uvx registry-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Today is 1 October 2026. For org.nr 923 609 016, list every statutory filing deadline coming up, with the date and why it applies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The call
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;company_deadlines(id="923609016", today="2026-10-01")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pass &lt;code&gt;today&lt;/code&gt; explicitly. Deadlines are &lt;strong&gt;computed, never fetched&lt;/strong&gt;, so the same entity and the same &lt;code&gt;today&lt;/code&gt; always give the same list — which is what makes the answer testable instead of "whatever the server clock said".&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EQUINOR ASA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"today"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deadlines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payroll_report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Monthly payroll report (A-melding)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"local_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A-melding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"authority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NAV / Skatteetaten (A-ordningen)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"statutory_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rolled_forward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"period_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"recurrence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monthly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"applies_because"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This entity has reported employees and must file the monthly payroll report (a-melding) with NAV/Skatteetaten (a-opplysningsforskriften § 2-1)."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vat_return"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"statutory_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rolled_forward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"period_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026 term 4 (Jul–Aug)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shareholder_register_statement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-02-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"statutory_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-01-31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rolled_forward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tax_return"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-05-31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;242&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"general_meeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-06-30"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;272&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"annual_accounts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-07-31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"statutory_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-07-31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rolled_forward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_until"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;303&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Filing deadlines are computed assuming a calendar-year accounting period. Enhetsregisteret does not publish a company's accounting year. For a financial year ending between 1 January and 30 June, regnskapsloven § 8-3(1) sets a different deadline — 1 February, not 31 July — so a deviating year changes which rule applies, not just the date. The Ministry may also postpone the accounts deadline by up to one month by regulation (§ 8-3(1)). Verify against Regnskapsregisteret before relying on an annual date."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two deadlines fall inside Q4: the a-melding on 5 October and the VAT return on 12 October.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quote &lt;code&gt;due_date&lt;/code&gt;, not &lt;code&gt;statutory_date&lt;/code&gt;.&lt;/strong&gt; The VAT return's statutory date is 10 October — a Saturday. &lt;code&gt;due_date&lt;/code&gt; is 12 October and &lt;code&gt;rolled_forward: true&lt;/code&gt; says why. The annual accounts deadline does &lt;strong&gt;not&lt;/strong&gt; get the same treatment: 31 July 2027 is also a Saturday, but &lt;code&gt;rolled_forward&lt;/code&gt; stays &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;due_date&lt;/code&gt; stays 31 July, because regnskapsloven § 8-3(1) only waives the late fee if the accounts are dispatched &lt;em&gt;before&lt;/em&gt; 1 August — rolling onto Monday would return a date the fee is already running. Roll-forward is decided per deadline, from that deadline's own source, not as a blanket rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;applies_because&lt;/code&gt; is the citation.&lt;/strong&gt; Nothing here is a legal-form guess: the a-melding appears because the entity reports employees, the VAT return because it's in Merverdiavgiftsregisteret. Each sentence also names the provision the date comes from. The docstring's rule is to quote that sentence rather than presenting a date as unconditional fact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read &lt;code&gt;notes&lt;/code&gt; before you send it to anyone.&lt;/strong&gt; &lt;code&gt;annual_accounts&lt;/code&gt; and &lt;code&gt;general_meeting&lt;/code&gt; assume a calendar-year accounting period, because Enhetsregisteret does not publish the real one — a financial year ending in the first half of the year has a &lt;strong&gt;1 February&lt;/strong&gt; deadline instead of 31 July, not just a shifted date. &lt;code&gt;tax_return&lt;/code&gt; and &lt;code&gt;shareholder_register_statement&lt;/code&gt; don't carry that caveat: they run from the tax period, not the accounting year.&lt;/p&gt;

&lt;p&gt;An empty &lt;code&gt;deadlines&lt;/code&gt; list is a real answer too — a bankrupt, deleted or compulsorily-liquidated entity, a sub-unit, or a legal form the module has not classified. &lt;code&gt;notes&lt;/code&gt; says which, and the service would rather omit an obligation than invent one.&lt;/p&gt;

&lt;p&gt;Source: Enhetsregisteret, NLOD 2.0. Same JSON over MCP and REST.&lt;br&gt;
Repo: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>norway</category>
      <category>python</category>
    </item>
    <item>
      <title>Check a Norwegian supplier is VAT-registered before you pay the invoice</title>
      <dc:creator>fargerod-dotcom</dc:creator>
      <pubDate>Fri, 04 Sep 2026 15:13:13 +0000</pubDate>
      <link>https://dev.to/fargeroddotcom/check-a-norwegian-supplier-is-vat-registered-before-you-pay-the-invoice-pb9</link>
      <guid>https://dev.to/fargeroddotcom/check-a-norwegian-supplier-is-vat-registered-before-you-pay-the-invoice-pb9</guid>
      <description>&lt;p&gt;Your agent is about to approve an invoice charging 25% MVA. Is the supplier actually in Merverdiavgiftsregisteret? &lt;code&gt;registry-mcp&lt;/code&gt; answers from Brønnøysundregistrene / Enhetsregisteret (brreg), by organisasjonsnummer (orgnr, org.nr).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add registry-mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://api.foretak.dev/mcp
&lt;span class="c"&gt;# or locally, over stdio: uvx registry-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Invoice 2026-0417 from EL ANSARI KONSULT, org.nr 833 285 602, charges NOK 4 000 + 25% MVA. Before I pay: is this supplier VAT-registered, and is the business active?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The call
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;lookup_company(id="833285602")&lt;/code&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EL ANSARI KONSULT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"legal_form_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vat_registered"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vat_registered_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-04-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vat_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO833285602MVA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"foretaksregisteret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mvaregisteret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cached"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-05T14:44:24.836394Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://data.brreg.no/enhetsregisteret/api/enheter/833285602"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NLOD 2.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four fields settle it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;vat_registered: true&lt;/code&gt;&lt;/strong&gt;, the answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;registers.mvaregisteret&lt;/code&gt;&lt;/strong&gt;, the same fact as the register publishes it. Cite this one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;vat_number&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;NO833285602MVA&lt;/code&gt; must match the number printed on the invoice. If it does not, stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;vat_registered_at: "2024-04-15"&lt;/code&gt;&lt;/strong&gt;: an invoice dated before this charged VAT the supplier was not entitled to collect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;status: "active"&lt;/code&gt; is the second half of the question. A bankrupt or deleted entity comes back with a different &lt;code&gt;status&lt;/code&gt; and a &lt;code&gt;notes&lt;/code&gt; entry saying so.&lt;/p&gt;

&lt;h2&gt;
  
  
  What skipping the check costs
&lt;/h2&gt;

&lt;p&gt;It does not stop invoice fraud: in payment redirection the supplier is real and registered, and only the bank account is wrong. What it stops is deducting VAT that was never chargeable, and Norway has put figures on that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skatteklagenemnda &lt;a href="https://www.skatteetaten.no/rettskilder/type/vedtak/skatteklagenemnda/etterberegning-av-inngaende-merverdiavgift-pa-bakgrunn-av-at-selger-ikke-var-registrert-i-merverdiavgiftsregisteret/" rel="noopener noreferrer"&gt;NS 116/2018&lt;/a&gt;, 15 September 2018: kr 691,100 of input VAT disallowed and kr 138,220 in penalty, for deducting VAT on invoices from a seller who was not registered.&lt;/li&gt;
&lt;li&gt;Skatteklagenemnda &lt;a href="https://www.skatteetaten.no/en/rettskilder/type/vedtak/skatteklagenemnda/etterberegning-av-inngaende-merverdiavgift-nar-selger-ikke-er-registrert-i-merverdiavgiftsregisteret.-skjerpet-tilleggsskatt/" rel="noopener noreferrer"&gt;NS 27/2019&lt;/a&gt;, 20 March 2019: kr 492,541 reversed, plus a 20% and a further 20% surcharge at kr 98,508 each. The board's line: &lt;em&gt;"Skattepliktige skulle selv ha kontrollert hvorfor selger utsteder fakturaer inkludert merverdiavgift når det ikke står MVA bak org nr."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both turn on &lt;a href="https://lovdata.no/dokument/SF/forskrift/2004-12-01-1558/KAPITTEL_5-1" rel="noopener noreferrer"&gt;bokføringsforskriften § 5-1-2&lt;/a&gt;: &lt;em&gt;"Dersom selger er registrert i Merverdiavgiftsregisteret, skal organisasjonsnummer etterfølges av bokstavene MVA."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finanstilsynet's &lt;a href="https://www.finanstilsynet.no/nyhetsarkiv/rundskriv/2019/veiledning-om-regnskapsforeres-og-regnskapsforerselskapers-etterlevelse-av-hvitvaskingsregelverket/" rel="noopener noreferrer"&gt;Rundskriv 15/2019&lt;/a&gt; § 4.4.1 accepts an &lt;em&gt;oppslag&lt;/em&gt; against Enhetsregisteret no older than three months, one month where the check rests on details the customer supplied, and asks for &lt;em&gt;notoritet&lt;/em&gt; about the lookup: what was consulted, and when. &lt;code&gt;fetched_at&lt;/code&gt;, &lt;code&gt;cached&lt;/code&gt; and &lt;code&gt;source_url&lt;/code&gt; are that record. Nothing is served more than 24 hours old.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two ways this says no
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Malformed number.&lt;/strong&gt; &lt;code&gt;validate_company_id&lt;/code&gt; is free and does no network call, so run it on invoice input first:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"833286602"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"'833286602' is not a valid Norwegian organisasjonsnummer."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An organisasjonsnummer is nine digits with a MOD11 check digit, e.g. 923609016. If you have a company name instead, call search_company."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;valid: false&lt;/code&gt; is a normal 200-shaped answer, never an exception. One transposed digit is the usual cause. Fix it, or call &lt;code&gt;search_company&lt;/code&gt; with the supplier's name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well-formed, but nobody's.&lt;/strong&gt; &lt;code&gt;999999999&lt;/code&gt; passes the MOD11 check and still is not a company:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not_found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No entity with organisasjonsnummer 999999999 exists in Enhetsregisteret."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The number is well-formed, so it may never have been issued or the entity may have been deleted. Call search_company with the company name instead."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;not_found&lt;/code&gt; means never issued, or deleted. Hold the payment and ask a human.&lt;/p&gt;

&lt;p&gt;Source: Enhetsregisteret, NLOD 2.0. Same JSON over MCP and REST.&lt;br&gt;
Repo: &lt;a href="https://github.com/foretak/registry-mcp" rel="noopener noreferrer"&gt;https://github.com/foretak/registry-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>norway</category>
      <category>python</category>
    </item>
  </channel>
</rss>
