Tool definitions
The definitions use JSON Schema, which most models and agent frameworks understand. Some call the field parameters instead of input_schema. The content is the same.
[
{
"name": "cvr_lookup",
"description": "Look up a Danish company by its 8-digit CVR number. Returns name, address, industry, status, owners and management.",
"input_schema": {
"type": "object",
"properties": {
"cvr": {"type": "string", "description": "8-digit CVR number, e.g. 24256790"}
},
"required": ["cvr"]
}
},
{
"name": "cvr_search",
"description": "Find Danish companies by name. Tolerates typos. Returns name, cvr_number and industry for each hit.",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Company name or part of it"}
},
"required": ["name"]
}
}
]Python handler
When the model calls a tool, run the function and send the result back as text. Production units are dropped because large companies can have hundreds, and they take up a lot of the model's context.
import json
import urllib.error
import urllib.parse
import urllib.request
BASE = "https://apicvr.dk/api/v1"
def _get(url):
try:
with urllib.request.urlopen(url, timeout=15) as res:
return json.load(res)
except urllib.error.HTTPError as err:
return {"error": err.code}
def run_tool(name, args):
if name == "cvr_lookup":
company = _get(f"{BASE}/{args['cvr']}")
company.pop("p_units", None) # keep the context small
return json.dumps(company, ensure_ascii=False)
if name == "cvr_search":
hits = _get(f"{BASE}/search/fuzzy/" + urllib.parse.quote(args["name"]) + "?limit=5")
return json.dumps(hits, ensure_ascii=False)
return json.dumps({"error": "unknown tool"})System prompt
You have access to CVR-API (https://apicvr.dk), a free JSON API for the
Danish CVR register with no API key. Use cvr_search to find the CVR
number from a name, and cvr_lookup to fetch details. If the "protected"
field is true, the company must not be contacted with marketing.llms.txt
If your agent reads documentation on its own, point it at https://apicvr.dk/llms.txt. It describes every endpoint briefly in Markdown. llms-full.txt also lists every response field and error code.
Tips
- Let the model search first and look up afterwards. Names are ambiguous, CVR numbers aren't.
- Use
/api/v1/{cvr}/direktion-og-ansvarligif the agent only needs owners and management. The response is much smaller. - Cache responses in your agent. CVR data rarely changes minute to minute.
- Owners and directors are personal data. Only use them for purposes you have a lawful basis for.
Frequently asked questions
Does it work with every language model?
Yes, with any model or framework that supports tool calling or function calling. The API itself is just HTTP and JSON.
Does the agent need an API key?
No. CVR-API needs no key, so there's nothing to store or rotate.
Can the agent get financial figures?
No, financial statements aren't part of CVR-API. The agent gets company details, owners, management and production units.