Spring til indhold

MCP-server til AI-agenter

Grundfasts MCP-server giver en AI-agent adgang til registrene som værktøjer. Agenten forbinder med en almindelig API-nøgle og får de samme svar som REST-API’et.

Endpoint og protokol

EgenskabVærdi
Endpointhttps://api.grundfast.dk/mcp
TransportStreamable HTTP: hver JSON-RPC 2.0-besked sendes som POST og besvares med JSON.
Protokolversion2025-06-18
TilstandStateless — ingen session-id, hvert kald står alene.
GodkendelseAuthorization: Bearer gf_live_... (eller gf_test_...). Kan klienten ikke sætte en header, accepteres nøglen også som ?token= på URL’en — men brug headeren, hvor du kan.
Metoderinitialize, tools/list, tools/call, ping og notifikationer.
  • Forbrug: hvert vellykket tools/call tæller som ét kald, præcis som et REST-kald. initialize, tools/list, ping og værktøjskald, der fejler, faktureres ikke.
  • Grænser: samme nøgle, burst-grænse og månedskvote som REST. Rammer agenten en grænse, svarer serveren HTTP 429 med Retry-After. Se Rate limits.
  • Fejl: et værktøj, der fejler (ugyldigt argument, ukendt BFE, en kilde der er nede), svarer med isError: true og API’ets fejltekst i resultatet — ikke med en protokolfejl. Et ukendt værktøjsnavn er JSON-RPC-fejl -32601.
  • Testnøgler virker også, så du kan bygge og teste en agent uden at blive faktureret.

Værktøjerne

Hvert værktøj svarer til et REST-endpoint og returnerer den samme JSON som tekst i content. tools/list returnerer den aktuelle liste med JSON Schema for argumenterne.

VærktøjArgumenterHvad det svarer på
bbr_ejendom{ bfe }Hele BBR-ejendommen: bygninger, enheder, grund — med fredning og SAVE-bevaringsværdi pr. bygning.
bbr_historik{ bfe }Versionshistorikken for hver af ejendommens nuværende bygninger, nyeste først.
bbr_bygningssoegning{ kommunekode | postnr, anvendelse?, opfoert_fra?, opfoert_til?, varme?, opvarmning?, tag?, ydervaeg?, asbest?, areal_min?, areal_max?, olietank?, side?, per_side? }Alle stående bygninger i en kommune eller et postnummer, der matcher BBR-fakta: anvendelse, alder, varme, materialer, asbest, areal og olietank. Antal og første side på alle planer.
matrikel_ejendom{ bfe }Jordstykker og ejerlav for ejendommen.
ejendomsvurdering{ bfe }Ejendoms- og grundværdier pr. vurderingsår.
geodanmark_bygninger{ bfe }Bygningernes omrids som WGS84-polygoner.
dhm_bygningshoejder{ bfe }Målt højde på hver bygning, målt inde i omridset, i DVR90.
adresse_soeg{ q, per_side? }Adressesøgning i fritekst.
adresse_opslag{ uuid }En adresse på DAR-UUID, med BFE.
adresse_historik{ uuid }Versionshistorikken for en adresse eller et husnummer.
adresse_datavask{ betegnelse }Vask en uren adressestreng til rangerede kandidater med A/B/C-kategori.
adgangsadresse_reverse{ lon, lat, radius_m? }De nærmeste adgangsadresser til et punkt.
adresse_aendringer{ kommunekode?, postnr?, adgangsadresse_id?, haendelse?, efter?, antal? }Nye, ændrede og nedlagte adresser i et område fra den daglige DAR-opdatering. Kræver Pro.
cvr_virksomhed{ cvr }En virksomhed på CVR-nummer.
dagi_reverse{ lon, lat }Region, kommune, sogn, retskreds, politikreds og opstillingskreds for et punkt.
dagi_omraade{ tema, kode }Ét administrativt område på tema og kode, fx kommune 101.
dhm_hoejde{ lon, lat }Terræn- og overfladekote i et punkt.
stednavne_naer{ lon, lat, radius_m? }Stednavne nær et punkt.
jord_omraadeklassificering{ lon, lat }Om punktet er områdeklassificeret (§ 50 a), og om det er analysefrit.
natur_skovbyggelinje{ lon, lat }Om punktet ligger inden for en gældende eller ophævet skovbyggelinje (§ 17).
natur_byggelinjer{ lon, lat }Alle byggelinjer og § 3-beskyttelser på punktet med afstand til kanten.
plan{ lon, lat }Zone, lokalplaner, delområder og kommuneplanrammer på punktet (Plandata).
plan_ejendom{ bfe }Planerne for en ejendom plus vejledende restbyggeret fra Matriklen og BBR.
miljoe_screening{ bfe }Miljø-forhåndsscreening pr. bygningsdel (asbest, PCB, bly, PAH, klorparaffiner, tungmetaller) plus jord, bindinger og affald.
miljoe_stoej{ lon, lat }Vej-, jernbane-, lufthavns- og industristøj på punktet (Miljøstyrelsens støjkortlægning, 2017).
orto_luftfoto_aargange{ lon, lat }De luftfoto-årgange, der er fløjet over punktet.

Agenten får kun offentlige registerdata i samme form som REST. Koordinater er WGS84 lon/lat og skal ligge i Danmark.

Opsætning

Claude Code

terminal
claude mcp add --transport http grundfast https://api.grundfast.dk/mcp \
  --header "Authorization: Bearer gf_live_..."

Claude Desktop

Claude Desktops konfigurationsfil tager lokale servere (en kommando), ikke en fjern-URL med headere. Brug broen mcp-remote, som kører lokalt via npx (kræver Node) og videresender til Grundfast med din nøgle:

claude_desktop_config.json
{
  "mcpServers": {
    "grundfast": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.grundfast.dk/mcp",
        "--header",
        "Authorization:${GRUNDFAST_AUTH}"
      ],
      "env": {
        "GRUNDFAST_AUTH": "Bearer gf_live_..."
      }
    }
  }
}

Bemærk, at der ikke er mellemrum efter kolonet i Authorization:${GRUNDFAST_AUTH}; mellemrummet ligger i værdien af GRUNDFAST_AUTH. Det undgår, at argumentet bliver delt i to. Genstart Claude Desktop efter ændringen.

Cursor

~/.cursor/mcp.json (eller .cursor/mcp.json i projektet)
{
  "mcpServers": {
    "grundfast": {
      "url": "https://api.grundfast.dk/mcp",
      "headers": { "Authorization": "Bearer gf_live_..." }
    }
  }
}

ChatGPT og OpenAI

ChatGPTs forbindelser til egne MCP-servere understøtter OAuth eller ingen godkendelse, ikke en fast Bearer-header, så Grundfast kan ikke tilføjes dér. Via OpenAI’s Responses API kan du derimod angive headeren på MCP-værktøjet:

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4.1",
    tools=[
        {
            "type": "mcp",
            "server_label": "grundfast",
            "server_url": "https://api.grundfast.dk/mcp",
            "headers": {"Authorization": "Bearer gf_live_..."},
            "require_approval": "never",
        }
    ],
    input="Hvornår er hovedbygningen på BFE 5651067 opført, og hvad er taget lavet af?",
)
print(response.output_text)

Andre MCP-klienter, der understøtter Streamable HTTP med egne headere, forbindes på samme måde: URL’en ovenfor og Authorization-headeren.

Rå JSON-RPC

Vil du teste serveren uden en klient, kan du sende beskederne med curl. Hver besked er ét JSON-objekt; batches af flere beskeder understøttes ikke.

terminal
KEY="gf_test_..."

# 1. Håndtryk
curl -s https://api.grundfast.dk/mcp \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

# 2. List værktøjerne
curl -s https://api.grundfast.dk/mcp \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# 3. Kald et værktøj
curl -s https://api.grundfast.dk/mcp \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"bbr_ejendom","arguments":{"bfe":5651067}}}'
Svar på initialize
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "grundfast", "title": "Grundfast", "version": "1.0.0" }
  }
}
Svar på tools/call
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{ "type": "text", "text": "{\"type\":\"ejendom\",\"bfe_nummer\":5651067, … }" }]
  }
}

Et værktøj, der fejler, svarer med "isError": true og fejlteksten i content. En notifikation (en besked uden id) besvares med 202 uden indhold, og GET på endpointet giver 405. Uden gyldig nøgle svarer serveren 401 med en WWW-Authenticate-header, der beskriver det forventede format.