SSRQ MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@SSRQ MCP ServerSearch for persons named 'Heinrich' in the SSRQ authority data"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
SSRQ — MCP Server
An MCP server that exposes the person and organisation authority file of the Sammlung Schweizerischer Rechtsquellen — Les 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/mcpThe server targets mcp 2.0, which renamed the high-level server class
(FastMCP → MCPServer) 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.
Related MCP server: swiss-courts-mcp
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.dbdb.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.txt3. Start the server
python server.py --db ssrq.db --host 0.0.0.0 --port 8002Each flag also has an environment variable — SSRQ_DB, SSRQ_HOST, SSRQ_PORT,
SSRQ_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 ssrq http://<server-ip>:8002/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": {
"ssrq": {
"type": "http",
"url": "http://<server-ip>:8002/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.
Docker deployment
Build image
docker compose buildRun
docker compose up -dThe 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)
Serving under a sub-path (https://tei.example.ch/mcp/ssrq/mcp) has exactly one rule:
the app's --http-path and the nginx location must be the same string. The
endpoint is one path that answers POST (requests), GET (the server→client stream),
and DELETE (session teardown); it builds no URLs of its own, so all nginx has to do is
forward the path unchanged.
server {
listen 443 ssl;
server_name tei.example.ch;
# SSRQ_HTTP_PATH=/mcp/ssrq/mcp — same string, no trailing slash on proxy_pass,
# so the path reaches the app unrewritten.
location /mcp/ssrq/mcp {
proxy_pass http://127.0.0.1:8002;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# The GET stream must not be buffered or timed out mid-session.
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
chunked_transfer_encoding on;
}
}Two failure modes worth knowing, both of which return a bare Not Found or 405:
A trailing slash on
proxy_pass(http://127.0.0.1:8002/) strips the location prefix, so the app sees/and no route matches.locationand--http-pathdisagree — the app 404s every request. Check the startup line, which prints the exact path being served:Starting SSRQ MCP server on 0.0.0.0:8002/mcp/ssrq/mcp.
Verify from outside before wiring up a client:
curl -sS -o /dev/null -w '%{http_code}\n' -X POST https://tei.example.ch/mcp/ssrq/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'200 means the endpoint is live. 404 is a path mismatch, 405 means nginx is not
passing POST to the app (a static location or a limit_except is shadowing it).
Note: the server has no authentication. By default
docker-compose.ymlpublishes 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 -dOtherwise restrict access at the firewall.
Available tools
Tool | Description |
| Person/org/name-variant counts and the attested year range |
| Paginated list of the person authority file, by id |
| Persons by standardised name, label, or spelling variant |
| Full person record by SSRQ id (e.g. |
| Persons whose attested years overlap a range (max span 500 years) |
| Organisation authority by name |
| Full org record by SSRQ id (e.g. |
| Search all 138k name variants; |
| All name variants for a given person or org id |
| Spouses, mothers, fathers, organisations, and places, resolved to records |
Available resources
URI | Description |
| Corpus statistics (JSON) |
| Organisation index — |
| Single person record (JSON) |
| 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.
Result size. Claude.ai and Claude Desktop truncate a tool or resource result at
roughly 150,000 characters. ssrq://orgs is capped at 1000 rows (about 100 KB) for that
reason and reports its own truncation; the 500-row tool ceiling stays comfortably under
the limit too.
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 |
| 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 |
| id, uri, etype, label, std_name, surname, alias_of, org_type |
| name_text, ssrq_id, is_orig (138k variant → canonical mappings) |
Key notes
Person IDs:
per000001–per999999(23,674 total)Org IDs:
org000001–org999999(7,047 total)orig_names/std_names— original and normalised spelling variants (comma-joined)is_orig=1inname_indexmeans the name is the original spelling;is_orig=0is 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_personsresolves them all in one call.
Deployment
This server runs on tei.dh.unibe.ch at
https://tei.dh.unibe.ch/mcp/ssrq/mcp, alongside four sibling MCP servers:
Königsfelden, HLS, HBLS, EOS / HGB Basel.
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 -r requirements-dev.txtpytest test_ssrq_mcp.pyUnit 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.pyThe 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:8002Note 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.
This server cannot be installed
Maintenance
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
- AlicenseAqualityFmaintenanceMCP server for FOLIO, the Federated Open Legal Information Ontology. Makes the full ontology available to AI agents as searchable, browsable tools.125MIT
- AlicenseAqualityAmaintenanceMCP server for searching Swiss court decisions from federal and cantonal courts via entscheidsuche.ch. Enables full-text search, law reference lookup, and filtering by canton, court level, and date without API keys.81MIT
- AlicenseAqualityFmaintenanceEnables querying Swiss cybersecurity regulations, guidance, advisories, and frameworks from NCSC.ch via any MCP-compatible client.6Apache 2.0
- AlicenseAqualityFmaintenanceEnables querying Swedish statutes, provisions, case law, preparatory works, and EU law cross-references from any MCP-compatible client.19156Apache 2.0
Related MCP Connectors
MCP server for Open Archives: Dutch genealogical records and historical page transcriptions.
MCP for CanLII: Canadian case law and legislation metadata (federal, provincial, territorial).
The everything Zotero MCP server — Web API v3 + local API, safe writes, citations, search.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/ssrq_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server