> ## Documentation Index
> Fetch the complete documentation index at: https://docs.consile.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API reference: CVR

> The 5 read-only tools in the CVR connector: company search and lookup, production units, participants (owners/management) and person lookups, with arguments, return shapes and the built-in limits.

All CVR tools are **read-only** and are called via `tools/call` over
`POST /cvr/mcp`. CVR is a **managed** connector: there is no per-tenant connect
step and no per-tenant credential. Every read is a bounded, normalized query
against Erhvervsstyrelsen's public CVR distribution (`distribution.virk.dk`,
Elasticsearch index `cvr-permanent`).

<Info>
  Endpoint: `https://mcp.consile.ai/cvr/mcp`. CVR is its own named connector with
  only its own tools. The transport namespaces each tool as `cvr__<tool>` (e.g.
  `cvr__soeg_virksomhed`); your AI assistant invokes these for you, so the names
  below are the bare tool names.
</Info>

<Note>
  This connector is **read-only by construction**. Access runs on Consile's single
  shared ERST account (HTTP Basic over the CVR distribution API), synthesized from
  env, so a tenant connects nothing and the tools work immediately once the
  connector is in the subscription. Entitlement is still re-checked on every call.
  The client never exposes raw `scroll` / `search_after` / unbounded `size`; it only
  ever issues bounded `_search` reads and never a write.
</Note>

## Shared parameters & limits

<ParamField path="size" type="integer" default="10 or 50">
  Max results to return, bounded to **1..50**. Available on the search and
  list tools (`soeg_virksomhed`, `find_produktionsenheder`,
  `find_virksomheder_for_person`). The default is **10** for `soeg_virksomhed` and
  `find_virksomheder_for_person`, and **50** for `find_produktionsenheder`. A value
  outside `1..50` (e.g. `0` or `>50`) is **rejected with a validation error**, not
  clamped; there is no deep paging or scroll.
</ParamField>

<ParamField path="cvrNummer" type="integer" required>
  Danish CVR number (8 digits), e.g. `24256790`. Accepted as a string and coerced to
  an integer. Required on `hent_virksomhed`, `find_produktionsenheder` and
  `find_ejere`.
</ParamField>

Other built-in limits: every request has a **20-second timeout**, and because the
upstream ERST account is shared, a module-level throttle bounds Consile's
**aggregate concurrency** across all tenants (default 4 simultaneous calls, tunable
via `CVR_MAX_CONCURRENT`). This protects Erhvervsstyrelsen's fair-use, so very large
extracts are not possible.

<Note>
  **GDPR:** the `reklamebeskyttet` (advertising-protection) flag is surfaced on every
  company and production-unit result and never hidden. CVR exposes **no CPR numbers**
  at all; persons are identified by an internal `enhedsNummer`.
</Note>

***

## Companies

### soeg\_virksomhed

Search companies by their current name. Returns `{ total, companies: [...] }`, where
`total` is the full match count before the `size` cap and each company carries
`cvrNummer`, `navn`, `status`, `virksomhedsform`, `branchekode` / `branchetekst`,
`adresse`, `stiftelsesDato`, latest `beskaeftigelse` and `reklamebeskyttet`.
`kapital` is **not** included by search (use `hent_virksomhed`).

<ParamField path="query" type="string" required>
  Search text, typically a company name. Matched against the newest registered name
  (`operator: and`).
</ParamField>

<ParamField path="size" type="integer" default="10">
  Max results (1..50).
</ParamField>

### hent\_virksomhed

Fetch one company's current master data by CVR number. Returns the company object
(`cvrNummer`, `navn`, `status`, `virksomhedsform`, `branchekode` / `branchetekst`,
`adresse`, `stiftelsesDato`, `beskaeftigelse`, `kapital`, `kapitalValuta`,
`reklamebeskyttet`), or `{ found: false, cvrNummer }` when no company matches. This
is the only tool that returns share capital (`kapital` / `kapitalValuta`).

<ParamField path="cvrNummer" type="integer" required>
  CVR number (8 digits), e.g. `24256790`.
</ParamField>

***

## Production units

### find\_produktionsenheder

Find production units (P-numbers, physical operating sites) for a CVR number. Returns
`{ total, units: [...] }`, where each unit carries `pNummer`, `navn`, `status` (e.g.
`Aktiv`), `branchekode` / `branchetekst`, `adresse`, latest `beskaeftigelse` and
`reklamebeskyttet`. `total` is the full number of P-units; up to 50 are returned per
call.

<ParamField path="cvrNummer" type="integer" required>
  CVR number of the owning company.
</ParamField>

<ParamField path="size" type="integer" default="50">
  Max units (1..50).
</ParamField>

***

## Participants (owners & persons)

### find\_ejere

Find participants (owners, management, founders, legal and real owners, auditors) for
a CVR number. Returns `{ cvrNummer, navn, antal, antalAktive, ejere: [...] }`, or
`{ found: false, cvrNummer }` when the company has none. `antal` counts all relations
(incl. historical members and auditors); `antalAktive` counts only **active** owner /
management relations (excludes auditors and historical). Each `ejere[]` entry carries
`navn`, `type` (`PERSON` / `VIRKSOMHED`), `enhedsNummer`, the participant's own
`cvrNummer` (set when `type === "VIRKSOMHED"`, for walking the ownership chain; null
for persons, never CPR), an `aktiv` flag, `roller[]` (each with its own active /
historical period), current `funktioner`, and `ejerandel` / `stemmeret` shares.
Ownership and voting shares are normalized to **0-100 percent** with the raw CVR value
(`raavaerdi`) and source register (`kilde`) preserved.

<ParamField path="cvrNummer" type="integer" required>
  CVR number of the company whose participants you want.
</ParamField>

### find\_virksomheder\_for\_person

Find the companies a person is or was a participant in, searched by person name.
Returns `{ total, personer: [...] }`, where each person carries `enhedsNummer` (never
CPR) and `virksomheder[]`, and each company relation carries `cvrNummer`, `navn`, an
`aktiv` flag (an open period, i.e. `gyldigTil` is null, distinguishes current from
past engagement) and `roller[]`. Each role carries an `organisation` name (the organ
or register, e.g. `Direktion`, `Bestyrelse`, `Stiftere`, `EJERREGISTER`, `Revision`)
and a separate `funktion` field holding the FUNKTION value within that organ (e.g.
`DIREKTOER`).

<ParamField path="navn" type="string" required>
  The person's full name.
</ParamField>

<ParamField path="size" type="integer" default="10">
  Max matching persons (1..50).
</ParamField>

<CodeGroup>
  ```json Request: soeg_virksomhed theme={null}
  {
    "name": "soeg_virksomhed",
    "arguments": { "query": "Novo Nordisk", "size": 5 }
  }
  ```

  ```json Request: hent_virksomhed theme={null}
  {
    "name": "hent_virksomhed",
    "arguments": { "cvrNummer": 24256790 }
  }
  ```

  ```json Request: find_ejere theme={null}
  {
    "name": "find_ejere",
    "arguments": { "cvrNummer": 24256790 }
  }
  ```

  ```json Request: find_virksomheder_for_person theme={null}
  {
    "name": "find_virksomheder_for_person",
    "arguments": { "navn": "Jens Hansen", "size": 20 }
  }
  ```
</CodeGroup>

<Note>
  Because the ERST account is shared, credential failures are a **platform-side**
  concern, not a tenant reconnect: an auth failure surfaces as `CvrAuthError` (CVR
  rejected the shared credentials) and a non-2xx as `CvrApiError` (carrying the HTTP
  status, never a secret). If the CVR env is not configured at all, the connector is
  dormant and an entitled tenant's call fails with the platform's generic
  `CredentialMissingError`; `CvrConfigError` fires only for a malformed or incomplete
  credential. See [Errors & limits](/developers/errors-and-limits).
</Note>
