SSRQ MCP Server

SSRQ MCP Server

Exposes the Swiss Summary of Roman Law (SSRQ) authority files for persons and organisations, enabling search, lookup, and relation queries through MCP-compatible clients.

Category
Visit Server

README

SSRQ — MCP Server

An MCP server that exposes the person and organisation authority file of the Sammlung Schweizerischer RechtsquellenLes sources du droit suisse / Le fonti del diritto svizzero, in English the Collection of Swiss Law Sources (SSRQ · SDS · FDS) — to Claude and other MCP-compatible clients.

The collection is published by the Rechtsquellenstiftung of the Swiss Law Society and comprises over 140 editions of legal-historical documents from the Middle Ages to 1798 (https://ssrq-sds-fds.ch). This server serves the authority file behind those editions: 23,674 persons, 7,047 organisations, and 138,298 name variants.

Architecture

ssrq__fuseki_*.ttl  ──►  SSRQ ETL  ──►  ssrq.db (SQLite)
                                          persons ────┐
                                          orgs ───────┼──►  server.py
                                          name_index ─┘     (mcp 2.0 MCPServer,
                                                             streamable HTTP)
                                                                   │
                                                         http://<host>:8002/mcp

The server targets mcp 2.0, which renamed the high-level server class (FastMCPMCPServer) and removed mcp.server.fastmcp; requirements.txt pins the major version accordingly.

The database is built by the SSRQ project's ETL pipeline from the RDF-TTL source dump; this repository only serves it. Every connection is opened mode=ro with PRAGMA query_only, so the server cannot write to the corpus.

Setup

1. Build the database

The database lives at /data/ssrq.db in the container. To rebuild it from the RDF-TTL source (in the SSRQ project repository):

python ssrq_parse_ttl.py --input /path/to/ssrq__fuseki_*.ttl --db ssrq.db

db.SCHEMA_SQL holds the schema this server expects — it is the contract between the ETL and the server, and the tests build their fixtures from it.

2. Install dependencies

pip install -r requirements.txt

3. Start the server

python server.py --db ssrq.db --host 0.0.0.0 --port 8002

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

4. Connect a client

Add to your claude_desktop_config.json (or equivalent):

{
  "mcpServers": {
    "ssrq": {
      "url": "http://<server-ip>:8002/mcp",
      "transport": "streamable-http"
    }
  }
}

Or for Claude Code:

claude mcp add ssrq --transport http --url http://<server-ip>:8002/mcp

Docker deployment

Build image

docker compose build

Run

docker compose up -d

The container serves on port 8002 and expects ssrq.db at /data/ssrq.db. Adjust the volume path in docker-compose.yml if your data lives elsewhere.

Reverse proxy (nginx, optional but recommended)

server {
    listen 443 ssl;
    server_name ssrq-mcp.example.unibe.ch;

    location / {
        proxy_pass         http://localhost:8002;
        proxy_http_version 1.1;
        # Required for the streaming responses
        proxy_set_header   Connection '';
        proxy_buffering    off;
        proxy_cache        off;
        chunked_transfer_encoding on;
    }
}

Note: the server has no authentication. By default docker-compose.yml publishes port 8002 on all interfaces; if a proxy fronts it, bind it to loopback instead so the authority file is not reachable directly:

SSRQ_BIND=127.0.0.1 docker compose up -d

Otherwise restrict access at the firewall.


Available tools

Tool Description
corpus_stats() Person/org/name-variant counts and the attested year range
list_persons(limit=50, offset=0) Paginated list of the person authority file, by id
search_persons(query, limit=50) Persons by standardised name, label, or spelling variant
get_person(pid) Full person record by SSRQ id (e.g. per000001), with name variants
get_persons_by_year(year_from, year_to, limit=100) Persons whose attested years overlap a range (max span 500 years)
search_orgs(query, limit=50) Organisation authority by name
get_org(oid) Full org record by SSRQ id (e.g. org000001), with name variants
search_name_index(query, type_filter="", limit=50) Search all 138k name variants; type_filter is person, org, or empty for both
get_name_variants(id) All name variants for a given person or org id
related_persons(pid) Spouses, mothers, fathers, organisations, and places, resolved to records

Available resources

URI Description
ssrq://stats Corpus statistics (JSON)
ssrq://orgs Organisation index — {total, returned, truncated, orgs: [...]}, capped at 9999 rows and flagged when truncated
ssrq://person/{pid} Single person record (JSON)
ssrq://org/{oid} Single organisation record (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 rather than returning the whole table. Use list_persons(limit, offset) to page through the register.

Name search. search_persons, search_orgs, and search_name_index do a plain case-insensitive substring match. SQL wildcards in the query are escaped, so searching for 100% finds a literal "100%" rather than matching every record. search_persons looks at std_name, label, and both spelling-variant columns; historical spellings that differ from the modern form are best reached through search_name_index.

Name index shape. Every row carries kind (person or org), so the result shape is the same whether or not type_filter is set.

Missing records. get_person, get_org, and related_persons return {"error": "... not found."} rather than raising.

Year ranges. get_persons_by_year matches on overlap: a person is returned when first_year <= year_to and last_year >= year_from. Persons with no attested years are never returned. An inverted range, or one spanning more than 500 years, comes back as an error object.

Places. related_persons resolves spouse_ids, mother_ids, father_ids, and org_ids against the persons and orgs tables. loc_ids point at the SSRQ place authority, which this database does not currently carry: those ids are returned as bare {"id": ...} entries together with a places_note. If a places (or locations) table is added to the database later, they are resolved to full records automatically.

Database schema

Table Contents
persons id, uri, etype, label, label_lang, std_name, forename, surname, sex, first_year, last_year, years, org_ids, spouse_ids, mother_ids, father_ids, loc_ids, orig_names, std_names
orgs id, uri, etype, label, std_name, surname, alias_of, org_type
name_index name_text, ssrq_id, is_orig (138k variant → canonical mappings)

Key notes

  • Person IDs: per000001per999999 (23,674 total)
  • Org IDs: org000001org999999 (7,047 total)
  • orig_names / std_names — original and normalised spelling variants (comma-joined)
  • is_orig=1 in name_index means the name is the original spelling; is_orig=0 is a normalised variant. Original spellings sort first in every variant listing.
  • The relation columns (org_ids, spouse_ids, mother_ids, father_ids, loc_ids) are comma-joined id lists; related_persons resolves them all in one call.

Tests

pip install -r requirements-dev.txt
pytest test_ssrq_mcp.py

Unit tests build their own throwaway database and run with no setup. The DB and server tests skip unless you point them at the real database and a running server:

SSRQ_DB=/data/ssrq.db SSRQ_SERVER=http://localhost:8002 pytest test_ssrq_mcp.py

The suite also runs standalone, with grouped output and a non-zero exit on failure:

python test_ssrq_mcp.py --unit --db /data/ssrq.db --server http://localhost:8002

Note that the DB tests assert corpus-size floors (≥20,000 persons, ≥6,000 organisations, ≥100,000 name variants) — they will fail against a small sample database.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured