Skip to main content
Glama
thodel

HGB Basel MCP Server

by thodel

HGB Basel — MCP Server

An MCP server that exposes the Historisches Grundbuch Basel (HGB) corpus for use with Claude and other MCP-compatible clients.

Architecture

hgb_full_*.xml  ──► build_db.py ──► hgb.db (SQLite + FTS5)
                                         │
                                    server.py  (mcp 2.0 MCPServer,
                                                streamable HTTP)
                                         │
                              http://<host>:8000/mcp

The 800 MB XML is parsed once into a ~100 MB SQLite database. The server then runs stateless queries against it (PRAGMA query_only).

The server targets mcp 2.0, which removed mcp.server.fastmcp — the high-level class is now MCPServer in mcp.server.mcpserver. requirements.txt pins the major version accordingly; the previous mcp[cli]>=1.0.0 floor meant a rebuild silently jumped major versions and broke the import.

The transport is streamable HTTP (/mcp), not the HTTP+SSE (/sse) this server used previously. SSE is deprecated, and its handshake hands the client an absolute /messages/ path computed from the app's own mount point — unreachable behind a reverse-proxy sub-path without rewriting the event stream in the proxy. Existing clients pointed at /sse must be repointed.

db.SCHEMA_SQL and db.TRIGGERS_SQL own the schema; build_db.py uses them, so the build and the tests cannot drift apart.

Related MCP server: Switzerland Land & Woodland MCP

Setup

1. Install dependencies

cd mcp_server
pip install -r requirements.txt

2. Build the database

python build_db.py --xml ../hgb_full_26_05_29_05.xml --db hgb.db

This takes ~10 minutes and produces hgb.db. Run it once; repeat only when the XML changes.

3. Start the server

python server.py --db hgb.db --host 0.0.0.0 --port 8000

Each flag also has an environment variable — EOS_DB, EOS_HOST, EOS_PORT, EOS_HTTP_PATH — which the flags override. Importing server.py never reads sys.argv, so it is safe to import from tests or an ASGI loader.

--http-path (default /mcp) is the path the MCP endpoint is served at. Behind a reverse proxy, set it to the public path — see Reverse proxy.

4. Connect a client

Claude Code — the name and URL are positional; there is no --url flag:

claude mcp add --transport http hgb http://<server-ip>:8000/mcp -s user

-s user makes the server available in every project; -s project writes it to .mcp.json to share with a repository; the default local scope is just you, in the current project. claude mcp list then reports the connection status.

Claude Desktop, Cowork, claude.ai — Customize → Connectors → +Add custom connector, and paste the same URL. These clients connect from Anthropic's cloud rather than from your machine, so the server has to be reachable over the public internet; claude_desktop_config.json only configures local stdio servers, not remote URLs.

Project-scoped .mcp.json:

{
  "mcpServers": {
    "hgb": {
      "type": "http",
      "url": "http://<server-ip>:8000/mcp"
    }
  }
}

type is required, and streamable-http is accepted as an alias for http. An entry with a url but no type is read as a stdio server and skipped with an error.


Build image

docker compose build

First-time: build the database

# Copy XML to /data/hgb/ on the server, then:
docker run --rm \
  -v /data/hgb:/data \
  hgb-mcp \
  python build_db.py --xml /data/hgb_full_26_05_29_05.xml --db /data/hgb.db

Run

docker compose up -d

Update /data/hgb in docker-compose.yml to match the actual path on the vServer.

Reverse proxy (nginx)

Serving under a sub-path (https://tei.example.ch/mcp/eos/mcp) has exactly one rule: the app's --http-path and the nginx location must be the same string. The endpoint is one path answering POST, GET, and DELETE; it builds no URLs of its own, so nginx only has to forward the path unchanged.

server {
    listen 443 ssl;
    server_name tei.example.ch;

    # EOS_HTTP_PATH=/mcp/eos/mcp — same string, no trailing slash on proxy_pass.
    location /mcp/eos/mcp {
        proxy_pass         http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Connection '';
        proxy_buffering    off;
        proxy_cache        off;
        proxy_read_timeout 3600s;
        chunked_transfer_encoding on;
    }
}

Under SSE this deployment needed a sub_filter rewriting data: /messages/ in the event stream, because the handshake advertised a path outside the proxy prefix. Streamable HTTP removes that workaround entirely.

Two failure modes, both returning a bare Not Found or 405:

  • A trailing slash on proxy_pass strips the location prefix, so the app sees /.

  • location and --http-path disagree — every request 404s. The startup line prints the path actually served: Starting EOS MCP server on 0.0.0.0:8000/mcp/eos/mcp.

Note: the server has no authentication. By default docker-compose.yml publishes port 8000 on all interfaces; if a proxy fronts it, bind it to loopback instead:

EOS_BIND=127.0.0.1 docker compose up -d

Available tools

Tool

Description

corpus_stats

Document/span/person/event counts and year range

search_persons(query, limit)

FTS search for person names (FTS5 syntax)

get_document(doc_id)

Full document: text, all spans, events

get_dossier(dossier_id, limit=100)

All documents for a property, ordered by year

search_text(query, limit)

Keyword search over raw transcriptions with snippets

get_persons_in_year_range(year_from, year_to, limit)

Person mentions filtered by year

get_cooccurrences(person_name, limit)

Other persons in the same documents

list_dossiers(limit)

All properties with coordinates and year ranges

Available resources

URI

Description

hgb://stats

Corpus statistics (JSON)

hgb://dossiers

Dossier index — {total, returned, truncated, dossiers: [...]}, capped at 1000 rows and flagged when truncated

hgb://document/{doc_id}

Single document (JSON)

Query behaviour

Limits. Every limit is clamped to at most 500; a negative, zero, or non-numeric value falls back to that tool's own default. The previous min(limit, 200) guard let every negative through, and SQLite reads LIMIT -1 as unbounded.

Full-text search. search_persons and search_text pass the query to FTS5, so operators work — Hans OR Anna, Mei*, NEAR(...). An invalid FTS5 query falls back to quoted phrases and then to a literal substring search instead of raising.

Name search. SQL wildcards are escaped, so get_cooccurrences("100%") looks for a literal "100%" rather than matching every document.

Co-occurrences. get_cooccurrences caps the matching documents at COOC_DOC_CAP (400). Their ids are bound one per placeholder, so a common name matching thousands of documents would otherwise exceed SQLite's variable limit and raise too many SQL variables.

Result size. Claude.ai and Claude Desktop truncate a tool or resource result at roughly 150,000 characters. get_dossier is bounded because it carries every document's full text_raw, and get_document caps spans and events at 2000 each.

Deployment

This server runs on tei.dh.unibe.ch at https://tei.dh.unibe.ch/mcp/eos/mcp, alongside four sibling MCP servers: Königsfelden, SSRQ, HLS, HBLS.

What they share — the nginx routing, the landing pages, and the deploy sequence — lives in tei_mcp_ops. Start there for anything that spans the fleet; in particular, the app's --http-path and the nginx location have to be the same string, which is the rule a sub-path deployment turns on.

Tests

pip install pytest
pytest test_eos_mcp.py

Unit tests build their own throwaway database from db.SCHEMA_SQL and need no setup. DB and server tests skip unless pointed at them:

EOS_DB=/data/hgb.db EOS_SERVER=http://localhost:8000 pytest test_eos_mcp.py
F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    Enables querying Swiss land and woodland regulations including land acquisition rules (BGBB), forest protection laws (WaG), spatial planning zones (RPG), and agricultural leases (LPG). Provides AI assistants with searchable access to federal land law for farmers, notaries, and spatial planners.
    33
    Apache 2.0
  • F
    license
    -
    quality
    B
    maintenance
    Enables querying the Historisches Grundbuch Basel corpus, including full-text search, person lookups, and property dossier retrieval, through MCP-compatible clients like Claude.

View all related MCP servers

Related MCP Connectors

  • Search Swiss federal legislation: laws, articles, amendments via the Fedlex SPARQL endpoint.

  • Search a billion+ documents — papers, books, code, legal cases, forums, Wikipedia, and more.

  • Search your knowledge bases from any AI assistant using hybrid RAG.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/thodel/eos_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server