MCP Server for Swiss court decisions via entscheidsuche.ch - BGer, BVGer, BStGer + 26 cantonal courts
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
claude mcp add swiss-courts-mcp -- python -m swiss-courts-mcp{
"mcpServers": {
"swiss-courts-mcp": {
"command": "python",
"args": ["-m", "swiss_courts_mcp"]
}
}
}MCP Servers overview
> **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)**
# 🏛️ swiss-courts-mcp

[](https://opensource.org/licenses/MIT)
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io/)
[](https://github.com/malkreide/swiss-courts-mcp)

> MCP Server for Swiss court decisions — Federal Supreme Court (BGer), Federal Administrative Court (BVGer), Federal Criminal Court (BStGer), and all 26 cantonal courts via entscheidsuche.ch
[Deutsche Version](README.de.md)
<p align="center">
<img src="assets/demo.svg" alt="Demo: Claude searches Swiss court decisions via MCP tool call" width="720">
</p>
---
## Overview
Access Swiss court decisions from all judicial levels through a single MCP interface. Combines full-text search with structured filters for canton, court level, date range, and law references.
**🎯 Anchor demo query:** *"Find Federal Supreme Court case law on data protection (Art. 25 DSG) since 2020 — and if entscheidsuche.ch is down, still answer from the offline dump, clearly flagged."*
| Source | Coverage | Data |
|--------|----------|------|
| [entscheidsuche.ch](https://entscheidsuche.ch) (live, default) | Federal + 26 cantons | Court decisions since ~2000 |
| [SCD dump](https://doi.org/10.5281/zenodo.14867950) (offline fallback) | **Federal Supreme Court only, 2007–2024** | Metadata/regesten, **no full text** |
**Synergy with [fedlex-mcp](https://github.com/malkreide/fedlex-mcp):** Legislation (SR) + case law = complete legal research.
**Availability:** entscheidsuche.ch is non-profit infrastructure without an SLA. When it is unreachable, the server transparently falls back to a cached public dump (see [Offline fallback](#offline-fallback)). Every response declares its origin (`source: "live" | "dump"`), and dump answers carry a `coverage_note` — the fallback is **partial, not equivalent**.
---
## Features
- Full-text search across all Swiss court decisions
- Multi-stage law reference search with regex parser and Elasticsearch boost scoring
- Dedicated Federal Supreme Court search with chamber filter
- Canton and court level filtering
- Recent decisions feed
- Court taxonomy listing
- Decision statistics with aggregations
- Trilingual support (German, French, Italian)
- **Offline fallback** to a cached public dump when entscheidsuche.ch is unreachable — with explicit provenance on every response
- No API key required
---
## Prerequisites
- Python 3.11 or higher
- An MCP-compatible client (Claude Desktop, Cursor, Windsurf, etc.)
---
## Installation
```bash
pip install swiss-courts-mcp
```
Or install from source:
```bash
git clone https://github.com/malkreide/swiss-courts-mcp.git
cd swiss-courts-mcp
pip install -e ".[dev]"
```
---
## Quickstart
```bash
# Run directly
swiss-courts-mcp
# Or via Python module
python -m swiss_courts_mcp
```
---
## Configuration
### Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"swiss-courts": {
"command": "python",
"args": ["-m", "swiss_courts_mcp"]
}
}
}
```
### Cloud Deployment (HTTP transport)
The HTTP transport is **off by default**. The default bind host is `127.0.0.1`
(loopback only) — `0.0.0.0` must be opted into explicitly (the Dockerfile does
this). Running HTTP without authentication logs a warning; only do so behind an
authenticating reverse proxy.
```bash
# Local HTTP (loopback), no auth — development only
swiss-courts-mcp --http --port 8000
# Container (binds 0.0.0.0, auth enabled) — see Dockerfile
docker build -t swiss-courts-mcp .
docker run -p 8000:8000 -e MCP_AUTH_SECRET="$(openssl rand -hex 32)" swiss-courts-mcp
```
Relevant environment variables (see [`.env.example`](.env.example)):
| Variable | Default | Purpose |
|---|---|---|
| `MCP_HOST` | `127.0.0.1` | Bind host. Set to `0.0.0.0` only in containers. |
| `MCP_PORT` | `8000` | Bind port. |
| `MCP_ALLOW_PUBLIC_BIND` | `false` | Suppress the `0.0.0.0` warning (containers). |
| `MCP_STATELESS_HTTP` | `true` | Stateless HTTP → horizontal scaling without sticky sessions. |
| `MCP_AUTH_ENABLED` | `false` | Enable bearer-token auth for HTTP. |
| `MCP_AUTH_SECRET` | — | HS256 signing key (dev). |
| `MCP_OAUTH_JWKS_URL` | — | JWKS URL for RS256 validation (production). |
| `MCP_REQUIRED_SCOPES` | — | Comma-separated required scopes. |
| `MCP_CORS_ORIGINS` | — | Comma-separated allowed origins (no wildcard in prod). |
Authentication validates the user identity from the JWT `sub` claim only; see
[ADR 0001](docs/adr/0001-http-auth.md).
### Offline fallback (env)
| Variable | Default | Purpose |
|---|---|---|
| `SWISS_COURTS_FALLBACK_ENABLED` | `true` | Master switch. `0` disables the dump fallback (live-only). |
| `SWISS_COURTS_FORCE_DUMP` | `false` | Force the dump path (skip live) — for pre-warming the cache or offline testing. |
| `SWISS_COURTS_CACHE_DIR` | `platformdirs` cache | Override the cache directory for the downloaded dump. |
| `SWISS_COURTS_DUMP_RECORD` | `14867950` | Zenodo record id of the SCD dump to use. |
Pre-warm the cache (downloads the ~120 MB SCD CSV once, so the first real
outage does not pay the download cost):
```bash
SWISS_COURTS_FORCE_DUMP=1 python -m swiss_courts_mcp # then issue one search
```
---
## MCP Protocol Version
This server pins MCP protocol version **`2025-11-25`** (constant
`PROTOCOL_VERSION` in `server.py`). A regression test detects drift against the
installed SDK so a protocol bump is a conscious change (version + CHANGELOG +
this section). SDK updates land monthly via Dependabot.
## Project Phase
**Phase 1 — read-only** (see [ROADMAP.md](ROADMAP.md)). All tools are
`readOnlyHint: true`; there are no writing or destructive operations. A move to
Phase 2 (write) requires a clean re-audit and the gates listed in the roadmap.
---
## Available Tools
### Court Decision Search
| Tool | Description |
|------|-------------|
| `search_court_decisions` | Full-text search across all court decisions with canton, court level, and date filters |
| `get_court_decision` | Retrieve a single decision by its unique signature |
| `search_bger_decisions` | Search Federal Supreme Court decisions with optional chamber filter |
| `search_by_law_reference` | Find decisions citing a specific law article (e.g., "Art. 8 BV") |
### Court Information
| Tool | Description |
|------|-------------|
| `list_courts` | List all indexed courts, optionally filtered by canton |
| `get_recent_decisions` | Latest decisions, filterable by canton and court level |
| `get_decision_statistics` | Statistics on indexed decisions by canton and year |
| `get_fallback_status` | Offline-dump cache state, coverage, version, pre-warming (read-only) |
### Tool Annotations
All eight tools share the same hints — they are read-only, idempotent,
non-destructive, and reach an external system:
| Annotation | Value |
|---|---|
| `readOnlyHint` | `true` |
| `destructiveHint` | `false` |
| `idempotentHint` | `true` |
| `openWorldHint` | `true` |
A `rechtsrecherche` **prompt** is also provided (a second MCP primitive
alongside tools).
### Example Use Cases
| Use Case | Tool Chain |
|----------|------------|
| Research case law on data protection | `search_court_decisions("Datenschutz")` |
| Find practice on a constitutional right | `search_by_law_reference("Art. 8 BV")` |
| Latest Federal Supreme Court rulings | `search_bger_decisions("Arbeitsrecht", date_from="2024-01-01")` |
| Combined: Law text + case law | `fedlex_search_laws("DSG")` then `search_by_law_reference("Art. 25 DSG")` |
[→ More use cases by audience →](EXAMPLES.md)
---
## Architecture
```
┌─────────────────────────────────────┐
│ MCP Client (LLM) │
│ Claude / Cursor / Windsurf │
└──────────────┬──────────────────────┘
│ MCP Protocol
┌──────────────▼──────────────────────────────┐
│ swiss-courts-mcp │
│ 8 tools · Pydantic validation │
│ Elasticsearch query builder │
│ Provenance envelope: source = live | dump │
└───────┬──────────────────────────────┬──────┘
│ ① live (default) │ ② fallback
│ HTTPS POST/GET │ on bot-block / 5xx / 429 /
│ │ timeout, or SWISS_COURTS_FORCE_DUMP=1
┌───────▼──────────────────┐ ┌───────▼───────────────────────────────┐
│ entscheidsuche.ch │ │ SCD dump — Zenodo 14867950 (CC BY) │
│ Elasticsearch backend │ │ lazy download → platformdirs cache │
│ Federal + 26 cantons │ │ → local SQLite search │
│ no auth · no SLA │ │ BGer only · 2007–2024 · no full text │
└──────────────────────────┘ └───────────────────────────────────────┘
```
### Architecture decision
This server uses **Architecture C (metadata-only offline fallback), delivered
via lazy download (Option A mechanics)** — decided after a live probe on
2026-07-19:
- **Live-first, always.** entscheidsuche.ch remains the sole source on success;
its behaviour is unchanged. The fallback only engages on an availability
failure (bot-block, HTTP 5xx/429, timeout, connect error) or when forced.
- **Source: the SCD dump** (Zenodo `10.5281/zenodo.14867950`, Version 2024-3,
**CC BY 4.0**), the ~120 MB CSV — metadata/regesten only, **no full text**. The
375 MB full-text Parquet and its heavy `pyarrow` dependency were rejected: a
partial fallback does not justify the footprint, and full text would fake an
equivalence that does not exist (BGer only).
- **A second candidate was rWhat people ask about swiss-courts-mcp
What is malkreide/swiss-courts-mcp?
+
malkreide/swiss-courts-mcp is mcp servers for the Claude AI ecosystem. MCP Server for Swiss court decisions via entscheidsuche.ch - BGer, BVGer, BStGer + 26 cantonal courts It has 1 GitHub stars and was last updated today.
How do I install swiss-courts-mcp?
+
You can install swiss-courts-mcp by cloning the repository (https://github.com/malkreide/swiss-courts-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is malkreide/swiss-courts-mcp safe to use?
+
Our security agent has analyzed malkreide/swiss-courts-mcp and assigned a Trust Score of 87/100 (tier: Trusted). See the full breakdown of passed checks and flags on this page.
Who maintains malkreide/swiss-courts-mcp?
+
malkreide/swiss-courts-mcp is maintained by malkreide. The last recorded GitHub activity is from today, with 0 open issues.
Are there alternatives to swiss-courts-mcp?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy swiss-courts-mcp to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/malkreide-swiss-courts-mcp)<a href="https://claudewave.com/repo/malkreide-swiss-courts-mcp"><img src="https://claudewave.com/api/badge/malkreide-swiss-courts-mcp" alt="Featured on ClaudeWave: malkreide/swiss-courts-mcp" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!