Skip to main content
Glama
harrywesterman

geldersarchief-mcp

README.md
# Gelders Archief scan core

Lokale **MCP-server**, Python-core en CLI voor het zoeken van Gelders Archief-aktes,
het ontdekken van alle registerscans en downloaden met provenance.
De MCP-server draait via stdio met de officiële SDK. OCR is optioneel en standaard
uitgeschakeld in MCP; zoeken, inspecteren en expliciet downloaden werken zonder OCR.

```bash
uv sync --frozen
uv run --frozen geldersarchief-mcp
```

Verbind een MCP-client met [de clientconfiguratie](examples/mcp-client.json).
Zie [MCP-tools en gebruik](docs/mcp.md) voor de volledige interface en downloadflow.

De voorbeeldpermalink is live getest op 5 oktober 2026: **192 unieke scans**, met
volgorde 1–192. De viewer laadt aanvankelijk 25 referenties. De core ontdekt alle
scans rechtstreeks via HTTP, zonder Chromium te starten. Scan 176 is via de
expliciete viewer-downloadlink opgeslagen als JPEG, 3019 × 4170 pixels,
1.372.341 bytes. SHA256:

```text
c9488c8abf3aa2c97921c7f04e080cc2299a52af79156f606640f36b04091611
```

Dit is de door de viewer aangeboden downloadrepresentatie. Het archiefmaster-
bestand is niet onafhankelijk beschikbaar gesteld of op byte-identiteit getest.
Scan 176 is een technisch testobject; er is niet vastgesteld dat deze scan de
akte uit de voorbeeldpermalink bevat.

## Installatie

Vereist: Python 3.12+ en [uv](https://docs.astral.sh/uv/).

```bash
uv sync --frozen
# Alleen nodig voor vieweronderzoek en browserfallbacks:
uv run playwright install chromium
```

## CLI

```bash
uv run geldersarchief inspect \
  'https://permalink.geldersarchief.nl/E42035012A364A3E9190403665F46E77'

uv run geldersarchief download-scan \
  'https://permalink.geldersarchief.nl/E42035012A364A3E9190403665F46E77' \
  --sequence 176
```

JSON-resultaten staan op stdout; geredigeerde JSON-logs op stderr. Gebruik
`geldersarchief --debug inspect URL` voor batchdiagnostiek. Downloads komen
standaard in `data/downloads/`; `--output DIRECTORY` overschrijft dat per opdracht.

Bestanden heten `<register>_scan-<volgnummer>.<ext>`. De sidecar bevat bronpermalink,
register, inventaris, scan-ID, oorspronkelijke download-URL, UTC-downloadtijd,
SHA256, dimensies, discoverymethode en representatietype. Aktenummer en confidence
blijven bij een expliciet gekozen CLI-scan `null`. MCP-downloads bewaren daarnaast
de resolutiestatus en het eventuele bewijs van een aktekoppeling. Afbeeldingen worden zonder
hercompressie opgeslagen.

## Vieweronderzoek

```bash
uv run python scripts/inspect_viewer.py --load-all --output debug/research
```

Dit legt consolemeldingen, requests, redirects, relevante JSON/JavaScript,
DOM, scriptbronnen, window-keys en stores vast. `--load-all` laadt de batches van
het geselecteerde register opeenvolgend met 350 ms vertraging. `--headed` opent
een zichtbaar venster. Een expliciete viewer-URL kan als positioneel argument
worden meegegeven om ook de iframe-/tileviewer te onderzoeken.

Op de ontwikkelmachine sluit het lokale netwerkverkeer van Chromium verbindingen
met deze site af, terwijl HTTP met OS-certificaatvertrouwen werkt. Hiervoor is er
een expliciete transportoptie:

```bash
uv run python scripts/inspect_viewer.py --http-transport --load-all
GA_BROWSER_HTTP_TRANSPORT=true uv run pytest -m live
```

Hierbij voert Chromium nog steeds de viewer-JavaScript uit en traceert Playwright
requests; HTTPX verzorgt het transport met gecontroleerde TLS via de OS-truststore.
TLS-verificatie wordt niet uitgeschakeld. Deze optie is geen site-authenticatie- of
CAPTCHA-omzeiling. Normale CLI-discovery heeft deze optie niet nodig.

## Python-interface

```python
import asyncio
from geldersarchief_mcp.archives.geldersarchief import GeldersArchiefAdapter

async def main():
    async with GeldersArchiefAdapter() as archive:
        register = await archive.inspect_register(
            'https://permalink.geldersarchief.nl/E42035012A364A3E9190403665F46E77'
        )
        image = await archive.get_scan(register.scans[175])
        # image bevat de originele bytes van de viewer-downloadrepresentatie.

asyncio.run(main())
```

`inspect_register` geeft alleen een volledig register terug. Ontbrekende batches,
conflicterende posities of een onbekend totaal geven een expliciete exception.
`download_scan` geeft naast bytes ook de gevalideerde URL, MIME en dimensies terug.
Een verlopen URL vereist verse registerdiscovery; langdurige tokencaching is nog
niet geïmplementeerd.

## Configuratie

| Variabele | Default |
|---|---|
| `GA_DATA_DIR` | `data` |
| `GA_DOWNLOAD_DIR` | `<GA_DATA_DIR>/downloads` |
| `GA_BROWSER_HEADLESS` | `true` |
| `GA_BROWSER_HTTP_TRANSPORT` | `false` |
| `GA_MAX_CONCURRENT_REQUESTS` | `2` |
| `GA_MAX_IMAGE_DOWNLOADS` | `2` |

Eén adapter beheert één herbruikbare browser, maximaal twee contexts, maximaal twee
registeroperaties en twee downloads met de defaults. Bulk-batches zijn sequentieel
met 350 ms vertraging. HTTP 429/502/503/504 krijgt beperkte retries met back-off en
`Retry-After`. Alleen openbare bronnen worden gebruikt; geen externe AI-services.
De resolver gebruikt uitsluitend lokale OCR en een lokale observatiecache.

Debugoutput en lokale downloads zijn uitgesloten via `.gitignore`. Debugoutput
redigeert sessies en toegangstokens, ook in base64-HTML en ge-escapete JSON.
**Lokale provenance bevat de volledige publieke download-URL** voor reproduceerbaarheid;
publiceer die sidecars niet zonder de URL-tokens te redigeren.

## Tests

```bash
uv run pytest            # offline; live tests uitgesloten
uv run pytest -m live    # bewuste publieke netwerkcontrole
```

Offline tests dekken parsing, volgorde, ontdubbeling, storeselectie, ontbrekende
batches, downloadvalidatie, provenance, concurrency, redactie en back-off.
Live tests herhalen discovery/download met verse HTTP-clients en browserinstances.
CI draait alleen offline tests, op Python 3.12 en 3.14.

De bewezen HTTP-route is primair. Browserstore-, DOM- en navigatiefallbacks zijn
voor toekomstige wijzigingen; hun algemene werking buiten het geteste register
is niet aangetoond. Zie [het onderzoeksrapport](docs/reverse-engineering.md) voor
waarnemingen, beperkingen en de tien onderzoeksvragen.

Docker is nog niet geïmplementeerd. De observatiecache is geen volledige
registerindex.

MIT-licentie. Het project is niet verbonden aan het Gelders Archief of DE REE.

## Open Archieven-integratie

Zoeken en het normaliseren van records zijn geïmplementeerd via de officiële
Open Archieven 1.1 API. Zoekresultaten worden verrijkt met de volledige A2A-records,
zodat aktenummer, inventarisnummer en archiefbron beschikbaar zijn.

```bash
uv run geldersarchief search-acts --name 'Derk Jan van Brink' \
  --place Terwolde --year 1921 --record-type geboorte

uv run geldersarchief get-record \
  'gld:E4203501-2A36-4A3E-9190-403665F46E77'

uv run geldersarchief inspect \
  --record 'gld:E4203501-2A36-4A3E-9190-403665F46E77'
```

`download-scan` accepteert eveneens `--record` in plaats van een URL, maar vereist
nog steeds een expliciet `--sequence`. Dat is **geen automatische akte→scan-match**.
De sidecar bewaart het aangevraagde record apart van de ongeverifieerde aktevelden.

Typen: `birth`/`geboorte`, `death`/`overlijden`, `marriage`/`huwelijk`. Een `--date`
filtert op de gebeurtenisdatum. `--limit` (1–100) en `--start` bedienen paginering;
`number_found` is het aantal persoonstreffers van de API, vóór lokale filtering en
ontdubbeling op record-ID. `next_start` verwijst naar de volgende API-pagina.

De bekende geboorteakte heeft `event_date=1921-12-30`, `act_date=1922-01-02` en
`act_number="1"`. De resolver gebruikt de aktedatum voor nummerpositionering.
Onvolledige datums worden niet aangevuld met verzonnen maanden of dagen.

De client begrenst opeenvolgende calls tot maximaal één per 350 ms. Ontbrekende
records, HTTP-fouten en API-foutcodes worden expliciet afgehandeld. Er is geen
API-key of AI-provider nodig. Configureer `GA_USER_AGENT` met je project-URL of
contactadres bij publiek gebruik, zoals Open Archieven vraagt.

Details en bronnen: [Open Archieven-integratie](docs/openarchieven.md).

## Conservatieve akte→scan-resolver

Installeer voor `resolve` lokaal Tesseract en Nederlandse taaldata, bijvoorbeeld
op macOS met `brew install tesseract tesseract-lang`.

```bash
uv run geldersarchief resolve \
  --record 'gld:E4203501-2A36-4A3E-9190-403665F46E77' --max-scans 12

uv run geldersarchief resolve 'https://permalink.geldersarchief.nl/E42035012A364A3E9190403665F46E77' \
  --act 1 --date 1922-01-02 --name 'Derk Jan van Brink'
```

Bij `resolve` betekent `--date` de **aktedatum**. De zoekstrategie controleert
expliciete scanmetadata, bemonstert het register en gebruikt interpolatie alleen
bij voldoende monotone nummerankers. Het scanbudget voorkomt een onbeperkte
OCR-doorloop. Kandidaten en buurscans zijn verwijzingen, geen automatische download
van een als juist bevestigde akte.

Live proef op het voorbeeldregister: na 12 bekeken scans blijft het resultaat
`AMBIGUOUS`. Tesseract las losse nummers, maar bevestigde geen bijbehorende datum
of naam. Vervolgonderzoek heeft scan **3** visueel bevestigd als akte 1. De optionele
lokale Kraken-reader herkent de aktedatum en stelt scan 3 voor als kandidaat
(`AMBIGUOUS`), maar bevestigt de akte nog niet automatisch. Zie [lokale HTR](docs/local-htr.md) voor installatie en downloadbewijs. Algorithmische tests met
gecontroleerde observaties bewijzen de zoeklogica, niet de leeskwaliteit van deze
handgeschreven bron. Zie [resolver en live beperkingen](docs/resolver.md).

| Variabele | Default |
|---|---|
| `GA_CACHE_DB` | `<GA_DATA_DIR>/cache.sqlite` |
| `GA_RESOLVER_MAX_SCANS` | `24` |
| `GA_OCR_EXECUTABLE` | `tesseract` |
| `GA_OCR_LANGUAGE` | `nld` |

De lokale SQLite-cache bewaart OCR-tekst met afbeeldings-SHA256 en readeridentiteit.
Verwijder de cache na het vervangen van taalmodelbestanden; hun inhoud is geen
onderdeel van de readeridentiteit. Cache en scans blijven buiten Git.

TDQS

A3.5/5.0

Scored across 6 tools

Disambiguation4/5

Most tools have distinct purposes (search, get, inspect, resolve, download), but resolve_act_scan and download_act overlap since download_act also resolves, and download_scan vs download_act both download scans. Descriptions provide enough guidance to differentiate them, keeping this a minor issue.

Naming Consistency5/5

All six tools follow a consistent snake_case verb_noun pattern: get_record, search_acts, inspect_register, resolve_act_scan, download_scan, download_act. The convention is predictable and readable throughout.

Tool Count5/5

Six tools is well-scoped for a focused archival/genealogy resolution workflow. Each tool targets a distinct stage (search, retrieve, inspect, resolve, download) without redundancy bloat.

Completeness4/5

The surface covers the full discovery-to-download lifecycle: searching acts, fetching records, inspecting registers, resolving acts to scans, and downloading scans/acts. Minor gap in that there's no explicit listing of collections/registers beyond inspect_register, but core workflows are covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues