Skip to content

Local Install

Requirements

  • Python 3.14 and uv
  • A harvested Sisältöhaku export (see The Corpora)

Build the tables

git clone https://github.com/AI-Riksarkivet/kansallisarkisto-mcp
cd kansallisarkisto-mcp
make install
make harvest                                          # df: ~3 s
make ingest-df
uv run python scripts/harvest.py --index voudintilit  # ~1 min
make fetch-astia                                      # ~35 min, resumable
make ingest-voudintilit

make ingest-df reads .data/df/df.jsonl.gz and writes the df table into data/, building a Swedish full-text index over the searchable text and scalar indexes on the filtered columns. It takes a couple of seconds for df.

make ingest-voudintilit does the same for .data/voudintilit/voudintilit.jsonl.gz, joined with the Astia snapshot make fetch-astia writes beside it — each volume's archival reference and each page's image link, which the export itself lacks. Without the snapshot the pages are still ingested, but carry neither.

tuomiokirjat is the large one — 7.8 million pages:

uv run python scripts/harvest.py --index tuomiokirjat   # ~1.5 h, 6.3 GB
make fetch-astia-tuomiokirjat                            # 12,284 requests, ~1.5 h, resumable
make ingest-tuomiokirjat                                 # ~20 min, ~7 GiB of memory, 21 GB on disk

The ingest keeps one record per image (Sisältöhaku holds 60,000 twice), and builds the full-text index with one shard and 256 MiB partitions — settings the script fixes before lance loads, because with lance's defaults the build needs more memory than most machines have spare.

Both directories are git-ignored: .data/ is the harvest, data/ is derived from it.

Point an ingest elsewhere if your export lives somewhere else:

uv run python scripts/ingest_df.py --jsonl /path/to/df.jsonl.gz --output /path/to/lancedb
uv run python scripts/ingest_voudintilit.py --jsonl /path/to/voudintilit.jsonl.gz --astia /path/to/astia.jsonl --output /path/to/lancedb

Run

uv run kansallisarkisto-mcp                          # stdio
KA_MCP_TRANSPORT=http uv run kansallisarkisto-mcp    # streamable HTTP on :8000

Container

The image carries no data — the corpora are gigabytes and are rebuilt, not shipped. Mount a LanceDB directory at /data:

docker build -f .docker/kansallisarkisto-mcp.dockerfile -t kansallisarkisto-mcp .
docker run -p 8000:8000 -v "$PWD/data:/data:ro" kansallisarkisto-mcp

Or via compose, which builds the image and mounts ./data read-only:

docker compose -f .docker/docker-compose.yml up --build

Settings

variable default meaning
KA_LANCEDB_URI (resolved) Where the LanceDB tables live. Unset resolves to <project root>/data in a clone and /data in the image. Any lancedb URI works, including s3:// and gs://.
KA_MCP_STAGE_DATASETS false Copy the tables onto local disk at boot and serve the copy — for a Hugging Face Space, whose bucket mount lance cannot query under load. See Deployment.
KA_MCP_STAGE_DIR /data-local Where that copy goes. Must be writable by the runtime user.
KA_MCP_TRANSPORT stdio stdio or http. An unknown value fails loudly rather than silently serving stdio.
HOST 0.0.0.0 HTTP bind address.
PORT 8000 HTTP port.
LOG_LEVEL INFO Root log level. Logs go to stderr, so stdio transport stays clean.
KA_MCP_OTEL_ENABLED false Master switch for OpenTelemetry. Unset, the SDK is never initialised and every instrumentation call resolves to a no-op — which is what a stdio client on a laptop should get.
OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:4317 Collector endpoint. Only read when telemetry is enabled.
OTEL_EXPORTER_OTLP_PROTOCOL grpc grpc or http/protobuf.
OTEL_SERVICE_NAME kansallisarkisto-mcp Service name reported to the collector.
KA_MCP_OTEL_LOG_BRIDGE true Bridge Python logging into OpenTelemetry logs.

Settings are read from the environment or a .env file in the working directory. The OTEL_* names are the OpenTelemetry SDK's own, so anything else it recognises works too; none of them is read at all unless KA_MCP_OTEL_ENABLED is set.